DEV Community


Domain Driven Disaster

cheetah100 profile image Peter Harrison ・Updated on ・8 min read

How many businesses still use spreadsheets as part of their business operations? The reality is that they are used even at the highest levels in multinational companies. Is this some kind of accident? Did they not receive the memo about the security risks they present, their limitations, their complexity?

Spreadsheets still exist because they put the power to adapt and evolve in the hands of those who need it most. Spreadsheet users are not banging down the door of IT every week requesting that new features be added to Excel so they can get their job done. They have the power to do what they need to do no matter what domain they work in.

But the professional information technology people know better, or so they believe. They want to write software that is designed from the ground up to do a specific narrowly defined job. It begins with the foundation stones, the data structures and schema on top of which the object models and object relational mappings are built. Developers build business logic directly into these domain specific business classes.

A more recent development has been microservices, where a system is broken down into separate mini applications with a well defined subset of functionality. This is a similar concept to encapsulation with classes, in that every class has a well defined contract and that only the contract is visible to the outside. Furthermore microservices are related to domain specific aspects of the system.

Modularity and clear separation of concerns is a critical part of complex systems which allows you to break applications into sections with well defined responsibilities. But should we design our module architecture around a specific business domain? The answer is an emphatic no!

Building in Inflexibility

The first reason domain driven development is a bad idea is that it encourages you to build business logic into the application. Imagine if database software was domain specific, where the database language was applicable only to a single industry. Imagine there were different databases for each industry rather than a standard which allowed all kinds of different systems to be built on top of them. Database servers are an example of good architectural design where their purpose of storing data is not confused with the domain the database will be used for.

Building unnecessary domain level business logic into your software can only result in the software being more brittle and less adaptable to change. Think of spreadsheets where a user can modify how it works in real time, add a new field or add a new graph. Can your applications users add new fields? Can they create new reports and queries? Spreadsheets never require a rebuild or redeploy. Databases do not need to be rebuilt and redeployed simply because you added a column. But business software developers have been indoctrinated into using the fixed schema static domain model. But it does not need to be that way.

Domain Derived Modules

With the advent of microservices it appears that we are making the same mess as early Java EJB technology, in that it is trying to scale by breaking services up based on their contracts. Let us say you have clients, suppliers and partners. Each of these entities has different fields and business rules. The classic approach would be to have separate tables for each of these in the database, to have separate classes for each, which would be mapped to tables and for web services to be exposed for each individual object.

The microservice approach takes this a step further, breaking the system into stand alone applications. Perhaps all these entities will end up in separate services, running on different containers that talk to their own databases. The predictable result is a nightmare trying to do things like create cumulative data sets across multiple services. One common justification for taking this approach is scalability, but what actually occurs is a maze of services calling services, all the time increasing latency.

What usually happens when people start building real systems and run into this problem is the gurus come in and tell them they have been doing it wrong. They didn't quite understand the one true path. It seems our guru never really evaluated whether breaking an architecture apart based on the domain was a clever idea in the first place, or indeed whether using the domain as the foundation for application boundaries was a sensible.

Root of the Disease

Introductory books on Object Oriented Programming often use real world objects as examples. It encourages developers to model the real world in terms of code. I know they probably meant well, but what this did was inculcate modern developers with the idea that the business domain belongs in the binary.

While databases themselves are cleverly universal they did at the same time expect a static schema to be defined, which in reality was just as inflexible as any binary. The ongoing mess with trying to keep data structures up to date as the software is updated is a common experience for developers. It wasn't exactly their fault; after all the popular languages encourage binding the database and the code together.

Ok, clever clogs, what's the answer?

It is all very well waxing lyrical about the failure of modern software architectural approaches without presenting some kind of working example of how else it could be done. Condemning an approach without providing an alternative isn't constructive. And so I present the following case studies based on the idea of decoupling code from the domain.

In 2006 I worked on the a healthcare data integration application. That sounds pretty domain specific, but that health specific functionality was a very small part of the system, limited primarily to several connectors for a protocol used in hospitals and healthcare providers. The rest of the system was a universal integration engine that would work for any company in need of integrating different systems.

The application was an excellent example of a technology called OSGi, which separates the application into different concerns. None of the core modules were related to health at all. All were well defined and had a contract or API. These modules were based on the functionality required by the application, but the actual configuration of the routing and transforms was all in dynamic configuration that users could modify in real time with no deployment. Importantly the modules were not in any way influenced by the intended domain. This domain independence would inform my later experiences.

In a subsequent role I wrote a process orchestration system for a Internet Service Provider. Originally I was brought in to complete a project that had been developed in a way that was tightly coupled with the domain. After a while it became clear that it was so inflexible that we needed a different solution. So we developed a plan for another system which would address the architectural shortfalls of the first attempt.

One of the key architectural principles that we adopted for the new project was universality. The domain would be evicted from the application and into configuration. This project would never be domain specific. We would not hack it to address an immediate need with no thought for adaptability or flexibility. It took three months to complete the first version of the core functionality, another three to complete plug in components and write the dynamic configuration for the business processes.

Just like with the healthcare integration application we ended up with a universal system. It was able to handle the workflow automation needs of any business process. Soon we were developing new business processes faster than we were ever able to before. The workflow engine was released as open source.

Today it is being used successfully in a totally different domain from the original purpose it was developed. Ironically it is replacing spreadsheets because while it has many of the flexible features of spreadsheets it is also secure, central and easy to use. It delivers flexibility and adaptability more typically found in spreadsheets while being totally decoupled from the business domain at the source code level. The domain is in the run time configuration.

Complexity is the enemy of Scalability and Availability

Let us do a little thought experiment about how to scale based on two competing models; functional architecture and domain driven architecture. In the functional architecture we have three web servers fronted by a load balancer and a cluster of three database servers. The function of the web servers is to service API calls. The function of the database cluster is to store data in a single logical database replicated across all three servers regardless of API call.

In the domain architecture we have a router which routes API calls to different web servers depending on the required service. Each of these web servers are connected to a database server to store the data for that service.

So let us think about scalability and availability in these two systems. Imagine that there is a head crash on one of the database servers. In a functional architecture the other two servers take over and everything trucks along as normal while the damaged server is brought back into service. What happens to the domain driven architecture? Well one of the API will go totally offline. Not only that but you will need to restore the database server from backup, potentially with data loss.

Okay, but surely domain driven architecture is better for scaling? Well lets think about it. With the domain driven architecture all the hits for a specific service will go to the same servers. That means that if you are unfortunate enough to get a rush on one specific API it will get over loaded. But with a functional architecture any web server can service any call and the load is spread across multiple web servers and database servers.

And if you have multiple interdependent services I can't even begin to imagine the mess you will get into. We need services which are both universal and well defined. The 'application' should be a dynamic veneer.

Is your solution just bad old Monolithic Architecture?

Not at all. A monolithic architecture attempts to include all the domain specific rules within itself.

Imagine all the uses that spreadsheets have been put. Do spreadsheets contain all the inherent complexity that they have enabled? No. What I am proposing here is that we decouple our applications from the domain and make the user experience a consequence of run time configuration in data.

This is the same model that spreadsheets used, but we are using this principle in business software over the web. We should not be building business rules and domain entities into our code. The domain still needs to exist, but it can be expelled from code into dynamic run time configurations. This is a fundamental change of mindset.

Wrapping up the case against Domain Driven Development

Putting business logic and rules into an application sets them in stone. It undermines the power of managers to use the software in ways a developer might not imagine. How many companies have a culture where managers and users are always in conflict with the software developers as any and all changes no matter how small need to be designed, planned and rolled out as part of a software deployment. Under such circumstances is it any wonder managers have a death grip on their beloved spreadsheets?

This is more than a design principle, it is a fundamental philosophy about the relationship between developers and the user; that we should be empowering users rather than creating barriers. Rather than holding onto the keys to the castle, the domain, we should set them free.

Discussion (5)

Editor guide
kspeakman profile image
Kasey Speakman • Edited

There is no universal solution that fits all cases. DDD works best in certain scenarios and not well in others.

Also, you should fairly point out that configuration-based solutions have negative trade-offs that your customers will notice. For one thing, they typically require an extensive setup and training process to master the implications of the configuration options. Depending on how deep those configuration options are, that may even require training and keeping on staff an expert in your software. Many times end users also have increased training times because such systems give enough "freedom" for users to do the wrong things with the data. So they need to undergo training to learn the right way. These are non-trivial requirements to place on customers.

Because of the "freedom" such systems enable to administrative users, over time the system can rot into a convoluted mess of determining how all the ad hoc rules interact. As a dev, coding new features against such a system while maintaining the integrity of all the user's ad hoc rules is also a daunting challenge, and not the kind I would consider enjoyable.

For some use cases, these trade-offs are minimal and it is a good design to use. I am happy for you that it works for your product. But by no means is it universally the best option, nor is DDD always the wrong choice. Everything has a circumstance where it will flourish... it just may not be one you are facing today.

armanerfani profile image
armanerfani • Edited

I also agree with what Kasey mentioned specially the downsides of the configuration based systems which tend to evolve into ESB like system after a while.
And on the top of that the samples given on the article are integration and orchestration which usually implemented in Application Layer in DDD terminology. These areas usually should have simple businesses like transformation, aggregation ... or keeping the simple orchestration state. So if you do not any businesses in theses areas other than what I menti
oned, it is totally ok to skip DDD complexities

fourpastmidnight profile image
Craig E. Shea

I would argue that a DBMS is actually designed around a domain--that being the efficient storage and retrieval of data. That simple. That's why you don't see other "business domain" logic in a DBMS--it's not its domain in the first place. The only purpose of a DBMS is the efficient storage and retrieval of data. Period.

And I agree with @Kasey, there is no universal solution that fits all cases. I also agree with most of everything else said in the comment with respect to how all that external configuration can lead to problems. In my organization we moved away from that architecture because of the exact problems Kasey mentioned. And in other organizations I have dealt with software that strived to be as general (universal) as possible. And usually what resulted was a piece of software that was so general it need a lot of additional work to make it more specific to meet a particular purpose, i.e. the generalized software was next to useless. Not to mention, it was confusing for both users and developers.

To be clear, I cannot say that in all situations that a solution consisting of generalized software is bad; and nor can I say that in all situations DDD is good. As anyone who's been around long enough comes to know, the answer is always "It depends." In my particular situations, generalized software did not result in bringing value. I have also been in other situations where DDD did not bring value either. You must have context to understand whether a particular design methodology and/or architecture will result in realizing the expected business value.

cheetah100 profile image
Peter Harrison Author

To address Kasey and Craig's points. I am not claiming that we can create a single solution to meet all needs, rather that software should be designed around function rather than domain. Therefore video editor software cannot be created from a forms and data type application because it is dealing with video functionally. However, many software projects begin with requirements tightly coupled with the domain. Often this leads to software unnecessarily coupled to the domain, and thus limits itself. Imagine the designer of Word getting requirements to write a specific style of letter and writing a word processor around that specific style of letter for a particular industry.

The critique that pushing the domain into configuration leads to complexity in the configuration is valid. In fact the configuration becomes the place you define the domain rules. Just like you can write scripts inside Excel the systems I'm writing allow embedded Javascript which runs server side, and can be replaced with a REST call, by the right administrator of course. Where once there were developers and users we now have a more diverse environment where developers deliver core functionality and business analysts can construct new systems using these tools.

This article was primarily addressing the practice of using the domain as the foundation of the system. In the past this was necessary because of the way databases were static. Adding complex structures at runtime was not possible. Now it is possible, and with this possibility goes new opportunities to dramatically improve development efficiency.

This is not just a supposition. I'm now running the second project using the same software, despite the domains being totally different. We are now looking to develop it into a generally available tool for the entire organization.

neemzy profile image
Tom Panier

How can you guarantee your business logic works as intended in such a scenario? You can't write tests for it, since it isn't part of your codebase.

It sounds to me like you've set up a framework. Probably a good one, but that's not what it takes to meet your users' expectations. I also have trouble to believe runtime configuration can cover every single possible need, business-wise.

Your points against microservices are fair enough, given they're not an universal solution. Neither is DDD (even if it's a global step forward in theory, as it encourages software developers to not intertwine business logic with technical layers). Neither is your own - but I know you agree with that.