Domain-Driven Design: Everything You Believe Is Wrong!
Domain-Driven Design claims that it provides a strategy for building applications that will grow and evolve over time without collapsing under their own complexity. It does that by upsetting some conventional wisdom.
The real life of a developer isn't about development -- it's about maintenance. A typical IT shop spends 75 percent of its time extending, modifying, enhancing or fixing existing applications that, typically, the current staff didn't initially build. What, in the end, defeats this process is application complexity.
When maintenance is handled badly, we end up with those applications that everyone is afraid of: When any change is made, you're given surprising (and unfortunate) results. With these applications, we stop doing maintenance well and, instead, go into a defensive programming mode: We put in code we know is stupid from a structural point of view but is guaranteed to work correctly. We call these changes to existing code "patches" and each one of these "non-structural" solutions makes the application more complex. Eventually, the application becomes too complex to be maintained and has to be abandoned and rebuilt from scratch. Let's call this the Create/Repair/Abandon/rePlace cycle: The CRAP cycle.
I'm working on one of these replace projects right now and doing the best job I can to build an application that will survive maintenance. However, I can't get over the feeling that I'm just restarting a process that will lead to this application having to be abandoned and be rebuilt by the developers who come after me.
The Evolution of Software Development: Breaking the CRAP Cycle
The question of how to break the CRAP cycle is a critical part of the evolution of software design strategies. We used to talk about building "table-driven" applications that derive their logic from entries in a table. I took this to an extreme in one application and discovered that I hadn't eliminated the complexity problem: I had just moved it into some tables that, because they were so complex to maintain, were eliminated in a later rewrite.
Then we started creating "configurable" applications. The poster child for configurable applications is probably SAP, which can take two or three years to implement and configure for a specific company. This is also not a solution.
The SOLID principles for writing code are intended to break the CRAP cycle by ensuring two things: First, new functionality is handled with new code (no patches to existing code) and, second, changes to existing code, when necessary, are isolated from the rest of the application. Unfortunately, applying SOLID principles to large applications results in an overwhelming number of objects the developers new to a project never really grasp. It moves the complexity around (from the objects to the architecture), but doesn't eliminate it.
Refactoring tries to solve the problem a different way by encouraging developers to make structural changes rather than apply patches when performing maintenance. Coupled with test-driven development, refactoring aims to maintain the quality of the code (or even to improve it) as changes are made … provided, of course, that you can get someone to allow the time and money for you to do refactoring. Often, you can't: "So," your manager asks, "when you finish this ‘refactoring' will the application run faster? Have fewer errors? No? It will just do exactly what it's doing right now? Perhaps you could move on to something that actually is helpful to the company?"
The final solution is, of course, "planning": architecting solutions that will withstand the test of time. In this scenario, maintenance activities are to be handled like development with an equal amount of time spent on design and analysis as in the original application. As with refactoring, there's an issue around whether anyone is willing to pay for this. More important, though, the plans usually fail: We don't seem to do a good job of predicting what changes will be required in the future and, typically, our solutions don't adapt well to the changes that are required. We spend the money on planning and don't see any return.
Which, of course, led to Agile development: give up on long-term planning (because we're not good at it) to concentrate on what we can do -- managing the short term.
Which is where DDD comes in. DDD doesn't abandon refactoring, SOLID programming or any of the other tools that are part of a developer's toolkit. It merely says that we should stop building large applications (or, in DDD-talk, "big balls of mud"). The fundamental assumption of DDD is that human beings are incapable of building enterprise applications, but are capable of understanding small, well-bounded problems (a "domain").
These domains can be described using words that are well understood by the team of developers and business people working on the application. DDD also acknowledges that these words don't mean the same thing in different parts of the organization. If I'm talking to someone from Marketing and use the word "customer" I'm almost certainly using the word differently than I would if I was talking to someone in Customer Support. By "customer," marketing probably means something like "someone who has bought something from us or is on the verge of buying something from us"; Customer Support probably means "someone who has a support contract with us."
As a result, DDD assumes that creating applications that "integrate the organization" or creating "enterprise-wide object models" is a fool's game and doomed to failure. Instead, DDD suggests you should create applications only for Marketing, only for Customer Support -- that you should create applications within those parts of the organization where the words have a constant meaning (in fact, the definition of a "domain" is the area where the words you use to talk about what the business does have only one meaning).
The description of the business activities that make up a domain (descriptions that use those constant definitions) is called the "ubiquitous language" for that domain. Here, the word "ubiquitous" doesn't mean the language applies everywhere (in fact, it only applies within the domain). Instead, "ubiquitous" means the team uses those words with those definitions to the exclusion of all others, in all descriptions of the organization's activities.
For the application developer this means there are some things you stop doing. You don't build a Customer object that works everywhere in the organization; instead, you build a Customer object for the domain in which you're working. You don't build an application that integrates the organization; instead, you build an application that supports a domain. In building an application for a domain you might, of course, need to talk to another domain … but that's why we have service-oriented architectures: to allow domains to talk to each other.
These last two approaches are so very different from the standard approach for building enterprise applications that they just might work. It's still far too early to tell whether DDD can break the CRAP cycle. But the idea of limiting our aspirations to what we can actually accomplish has a certain appeal.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.