Why You Should Do DDD
Actually I've already given you some pretty good reasons why DDD makes so much practical sense. At the risk of breaking the DRY principle (don't repeat yourself) I reiterate in Table 1.1, and also add to the earlier reasons. Does anyone hear an echo?
Table 1.1 The key reasons why you should do DDD.
1. Put domain experts and developers on a level playing field, which produces software that makes perfect sense to the business, not just the coders. This doesn't mean merely tolerating the opposite group. It means becoming one cohesive, tight-knit team. 2. That “makes sense to the business” thing means investing in the business by making software that is as close as possible to what the business leaders and experts would create if they were the coders. 3. You can actually teach the business more about itself. No domain expert, no C-level manager, no one, ever knows every single thing about the business. It's a constant discovery process that becomes more insightful over time. With DDD, everybody learns because everybody contributes to discovery discussions. 4. Centralizing knowledge is key, because with that the business is capable of ensuring that understanding the software is not locked in “tribal knowledge,” available only to a select few, who are usually only the developers. |
1. There are zero translations between domain experts, the software developers, and the software. That doesn't mean maybe some few translations. It means zero translations because your team develops a common, shared language that everyone on the team speaks. 2. The design is the code, and the code is the design. The design is how it works. Knowing the best code design comes through quick experimental models using an agile discovery process. 3. DDD provides sound software development techniques that address both strategic and tactical design. Strategic design helps us understand what are the most important software investments to make, what existing software assets to leverage in order to get there fastest and safest, and who must be involved. Tactical design helps us craft the single elegant model of a solution using time-tested, proven, software building blocks. |
Like any good, high-yielding investment, DDD has some upfront cost of time and effort for the team. Considering the typical challenges encountered by every software development effort will reinforce the need to invest in a sound software development approach.
Delivering Business Value Can Be Illusive
Developing software that delivers true business value is not the same thing as developing ordinary business software. Software that delivers true business value aligns with the business strategic initiatives and bears solutions with clearly identifiable competitive advantage—software that is not about technology, but about the business.
Business knowledge is never centralized. Development teams have to balance and prioritize between the needs and requests of multiple stakeholders, and engage with many people having diverse skill sets, all with the goal of uncovering software functional and non-functional requirements. After gathering all that information, how can teams be certain that any given requirement delivers true business value? In fact, what are the business values being sought, and how do you uncover them, prioritize them, and realize them?
One of the worst disconnects of a business software development effort is seen in the gap between domain experts and software developers. Generally speaking, true domain experts are focused on delivering business value. On the other hand, software developers are typically drawn to technology and technical solutions to business problems. It's not that software developers have wrong motivations; it's just what tends to grab their attention. Even when software developers engage with domain experts, the collaboration is largely at a surface level, and the software that gets developed often results in a translation/mapping between how the business thinks and operates, and how the software developer interprets that. The resulting software generally does not reflect a recognizable realization of the mental model of the domain experts, or perhaps only partially. Over time this disconnect becomes costly. The translation of domain knowledge into software is lost as developers transition to other projects or leave the company.
A different, yet related, problem is when one or more domain experts do not agree with each other. This tends to happen due to each expert having more or less experience in the specific domain being modeled, or they are simply an expertise in a related but different area. It's also common for multiple “domain experts” to have no expertise in a given domain, where they are more of a business analyst, yet they are expected to bring insightful direction to discussions. When this situation goes unchecked, it results in blurred rather than crisp mental models, which lead to conflicting software models.
Worse still is when the technical approach to software development actually wrongly changes the way the business functions. While a different scenario, it is well known that ERP software will often change the overall business operations of an organization to fit the way the ERP functions. The total cost of owning the ERP cannot be fully calculated in terms of license and maintenance fees. The reorganization and disruption to the business can be far more costly than either of those two tangible factors. A similar dynamic is at play as your software development teams interpret what the business needs into what the newly developed software actually does. This can be both costly and disruptive to the business, its customers, and its partners. Furthermore, this technical interpretation is both unnecessary and avoidable with the use of proven software development techniques. The solution is a key investment.
How DDD Helps
DDD is an approach to developing software that focuses on these three primary aspects:
- DDD brings domain experts and software developers together in order to develop software that reflects the mental model of the business experts. This does not mean that effort is spent on modeling the “real world.” Rather, DDD delivers a model that is the most useful to the business. Sometimes useful and realistic models happen to intersect, but to the degree that they diverge, DDD chooses useful.
- DDD addresses the strategic initiatives of the business. While this strategic design approach naturally includes technical analysis, its concern is more for the strategic direction of the business. It helps define the best inter-team organizational relationships, and provides early warning systems for recognizing when a given relationship could cause software and even project failure. The technical aspects of strategic design have the goal of cleanly bounding systems and business concerns, which protects each business-level service. This provides meaningful motivations for how an overall service-oriented architecture or business-driven architecture is achieved.
- DDD meets the real technical demands of the software by utilizing tactical design modeling tools to analyze and develop the executable software deliverables. These tactical design tools allow developers to produce software that is a correct codification of the domain experts' mental model, is highly testable, is less error prone (a provable statement), performs to SLAs, is scalable, and allows for distributed computing. DDD best practices generally address a dozen or more higher-level architectural and lower-level software design concerns, with a focus on recognizing true business rules and data invariants, and protecting the rules from error situations.
[NLB]With this aspect the efforts of domain experts and software developers are on jointly developing a Ubiquitous Language of the areas of the business that they are focused on modeling. The Ubiquitous Language is developed with full team agreement, is spoken, and is directly captured in the model of the software. It is worth reiterating that the team is comprised of both domain experts and software developers. It's never “us and them.” It's always us. This is a key business value that allows business knowhow to outlive the relatively short initial development efforts that deliver the first few versions of the software, and the teams that produce it. It's the point where the cost of developing software is a justifiable business investment, not just a cost center.
[NLB]This entire effort unifies domain experts who initially disagree with each other, or who simply lack core knowledge of the domain. Further, it strengthens the close-knit team by spreading deep domain insight among all team members, including software developers. Consider this the hands-on training that every company should invest in its knowledge workers.
Using this approach to software development, you and your team can succeed in delivering true business value.
Grappling With the Complexity of Your Domain
We primarily want to use DDD in the areas that are most important to the business. You don't invest in what can be easily replaced. You invest in the non-trivial, the more complex stuff, the most valuable and important stuff that promises to return the greatest dividends. That's why we call such a model a Core Domain (2). It is these, and in second priority the significant Supporting Subdomains (2), that deserve and get the biggest investment. Rightly then, we need to grasp what complex means.
What qualifies as complex will differ from business to business. Different companies have different challenges, different levels of maturity, and different software development capabilities. So rather than determining what is complex, it may be easier to determine what is non-trivial. Thus, your team and management will have to determine if a system you are planning to work on deserves the cost of making a DDD investment.
DDD Score Card: Use Table 1.2 to determine if your project qualifies for an investment in DDD. If a row on the score card describes your project, place the corresponding number of points in the right-hand column. Tally all the points for your project. If it's 7 or higher, seriously consider using DDD.
Table 1.2 The DDD Score Card.
Does Your Project Score a Total of 7 Points Or Higher? |
|||
If Your Project... |
Points |
Supporting Thoughts |
Your Score |
If your application is completely data centric and truly qualifies for a pure CRUD solution, where every operation is basically a simple database query to Create, Read, Update, or Delete, you don't need DDD. Your team just needs to put a pretty face on a database table editor. In other words, if you could trust your users to just insert data directly into a table, update it, and sometimes delete it, you wouldn't even need a user interface. That's not realistic, but conceptually relevant. If you could even use a simple database development tool to create a solution, don't waste your company's time and money on DDD. |
0 |
This seems like a no-brainer, but it's not usually that easy to determine simple versus complex. It's not like every application that isn't pure CRUD deserves the time and effort of using DDD. So maybe we could come up with other metrics to help us draw a line between what is complex and what is not... |
|
If your system requires just 30 or less business operations, it's probably pretty simple. This would mean that your application would have no more than 30 total user stories or use case flows, with each of those flows having only minimal business logic. If you could quickly and easily develop such an application using Ruby on Rails or Groovy and Grails and not feel the pain of lacking power and control over complexity and change, your system probably doesn't need to use DDD. |
1 |
To be clear, I am talking 25-30 single business methods, not 25-30 whole service interfaces, each with multiple methods. The latter might be complex. |
|
So let's say that somewhere in the range of 30 to 40 user stories or use case flows could be creeping toward complexity. Your system might be getting into DDD territory. |
2 |
Caveat Emptor: Very often complexity is not recognized soon enough. We software developers are really, really good at underestimating complexity and level of effort. Just because we might want to code up a Rails or Grails application doesn't mean we should. In the long run those could hurt more than help. |
|
Even if the application is not going to be complex now, will it grow in complexity? You may not know this for sure until real users start working with it, but there is a step under Supporting Thoughts that may help uncover the true situation. Be careful here. If there is any hint at all that the application has even moderate complexity—here's a good time to be paranoid—it may be sufficient indication that it will actually be more than moderately complex. Lean toward DDD. |
3 |
Here it pays off to walk through the more complex usage scenarios with domain experts and see where it leads. Are domain experts... (1) already asking for more complex features? If so, it's likely an indication that the application is already or will soon become too complex to use a CRUD approach. (2) so bored with the features that they can hardly bear discussing them? It's probably not complex. |
|
The application's features are going to change often over a number of years, and you can't anticipate that the kinds of changes will be simple. |
4 |
DDD can help you manage the complexity of refactoring your model over time. |
|
You don't understand the Domain (2) because it's new. As far as you and your team know, nobody has done this before. That most likely means it's complex, or at least deserves due diligence with analytical scrutiny to determine the level of complexity. |
5 |
You are going to need to work with domain experts and experiment with models to get it right. You certainly also scored on one or more of the previous, so use DDD. |
|
Is your total score 7 or higher? |
|
This scoring exercise may have lead your team to these conclusions:
“It's too bad that we can't shift gears quickly and easily when we discover we are on the wrong side of complexity, no matter if the wrong side is more or less complex than we thought.”
“Sure, but that just means that we need to become much better at determining simplicity versus complexity early on in our project planning. That would save us a lot of time, expense, and trouble.”
“Once we make a major architectural decision and get several use cases deep in development, we are usually stuck with it. We had better choose wisely.”
If any of these observations resonate with your team, you are making good use of critical thought.
Anemia and Memory Loss
Anemia can be a serious health ailment with dangerous side effects. When the name Anemic Domain Model [Fowler, Anemic] was first coined it wasn't meant to be a complimentary term, as if to say that a domain model that is weak, without the power of inherent behavioral qualities, could possibly be a good thing. Strangely enough, anemic domain models have popped up left and right in our industry. Trouble is, most developers seem to think this is quite normal, and would not even acknowledge that a serious condition exists when employed in their systems. It's a real problem.
Are you wondering if your model is feeling tired, listless, forgetful, clumsy, needing a good shot in the arm? If you're suddenly experiencing technical hypochondria, here's a good way to perform a self examination. You'll either put yourself at ease or confirm your worst fears. Use the steps in Table 1.3 to perform your checkup.
Table 1.3
Determine your domain model health history using a self-checkup.
Domain Model Health History |
Yes / No |
Does the software you call a “domain model” have mostly public getters and setters, and no business logic or almost none at all? You know, objects that are mostly attribute value holders. |
|
Are the software components that frequently use your “domain model” the ones that house most of the business logic of your system, and do those heavily invoke the public getters and setters on the “domain model”? You probably call this particular client layer of the “domain model” a Service Layer or Application Layer (4, 14). If instead this describes your user interface, answer “yes” to this question and write 1,000 times on a white board that you'll never ever do that again. |
|
Hint: The correct answers are either “Yes” to both questions or “No” to both questions. |
|
How did you do?
If you answered “No” to both questions, your domain is doing well.
If you answered “Yes” to both of these questions, your “domain model” is very, very ill. It's anemic. The good news is you can get help for it by reading on.
If you answered “Yes” to one question and “No” to the other question, you are either in denial or suffering from delusions or another neurological issue that could be caused by anemia. What should you do if you have conflicting answers? Go straight back to the first question and run the self examination once again. Take your time, but remember that your answer to both questions must be an emphatic “Yes!”
As [Fowler, Anemic] says, anemic domain model is a bad thing because you pay most of the high cost of developing a domain model, but you get little or none of the benefit. For example, because of the object-relational impedance mismatch, developers of such a “domain model” spend a lot of time and effort mapping objects to and from the persistence store. That's a high price to pay while getting little or no benefit in return. I'll add that what you have is not a domain model at all, but just a data model projected from a relational model (or other database) into objects. It's just an impostor that may actually be closer to the definition of Active Record [Fowler, P of EAA]. You can probably simplify your architecture by not being pretentious and just admit that you are really using a form of Transaction Script [Fowler, P of EAA].
Reasons Why Anemia Happens
So if anemic domain model is the sickly outcome of a poorly executed design effort, why do so many use it while even thinking that their model is experiencing fine health? Certainly it does reflect a procedural programming mentality, but I don't think that's the primary reason. A good portion of our industry is made up of sample code followers, which isn't bad as long as the samples are quality ones. Often times, however, sample code is purposely focused on demonstrating some concept or API feature in the simplest possible way, without concern for good design principles. Yet over-simplified sample code, which usually demonstrates with a lot of getters and setters, is copied every day without a second thought about design.
There is another, older influence. The ancient history of Microsoft's VisualBasic had much to do with where we are today. I'm not saying that VisualBasic was a bad language and IDE, because it's always been a highly productive environment and in some ways influenced the industry for the good. Of course some may have avoided its direct influence altogether, but VisualBasic indirectly caught up with just about every software developer eventually. Really, just note the time line shown in Table 1.4.
Table 1.4
The time line from behavior-rich to infamous anemia.
1980s |
1991 |
1992-1995 |
1996 |
1997 |
1998-... |
Objects make an impact due to Smalltalk and C++ |
VisualBasic properties and property sheets |
Visual tools and IDEs become prolific |
Java JDK 1.0 released |
JavaBean specification |
Explosion of reflection-based tools for Java and .NET platforms based on properties |
What I am talking about is the influence of properties and property sheets, both backed by property getters and setters that were made so popular by the original VisualBasic forms designer. All you had to do is place a few custom control instances on a form, fill out their property sheets, and voila!, you had a fully functioning Windows application. It took just a few minutes to do that as compared to the few days required to program a similar application directly against the Windows API using C.
So what does all that have to do with anemic domain models? The JavaBean standard was originally specified to assist in the creation of visual programming tools for Java. It's motivation was to bring the Microsoft ActiveX capabilities to the Java platform. It held the hope of creating a market full of third-party custom controls of various kinds, just like VisualBasic's. Soon almost every framework and library jumped on the JavaBean bandwagon. This included much of the Java SDK/JDK as well as libraries such as the popular Hibernate. Specific to our DDD concerns, Hibernate was introduced to persist domain models. The trend continued as the .NET platform reached us.
Interestingly any domain model that was persisted using Hibernate in the early days had to expose public getters and setters for every persistent simple attribute and complex association in every domain object. This meant that even if you wanted to design your POJO (Plain Old Java Object) with a behavior rich interface, you had to expose your internals publicly so that Hibernate could persist and reconstitute your domain objects. Sure, you could do things to hide the public JavaBean interface, but by in large most developers didn't bother or even understand why they should have.
Most, if not all, of the web frameworks also function solely on the JavaBean standard. If you want your Java objects to be able to populate your web pages, the Java objects better support the JavaBean specification. If you want your HTML forms to populate a Java object when submitted to the server-side, your Java form object better support the JavaBean specification.
Just about every framework on the market today requires, and therefore promotes, the use of public properties on simple objects. Most developers can't help but be influenced by all the anemic classes all over their enterprises. Admit it. You've been bitten by it, haven't you? As a result, we have a situation that might be best labeled anemia everywhere.
Look At What Anemia Does To Your Model
Alright, so let's say we can agree that this is both true and vexing to us. What does anemia everywhere have to do with memory loss? When you are reading through the client code of an anemic domain model (e.g. the impostor Application Service, a la Transaction Script), what do we usually see? Here's a rudimentary example:
@Transactional public void saveCustomer( String customerId, String customerFirstName, String customerLastName, String streetAddress1, String streetAddress2, String city, String stateOrProvince, String postalCode, String country, String homePhone, String mobilePhone, String primaryEmailAddress, String secondaryEmailAddress) { Customer customer = customerDao.readCustomer(customerId); if (customer == null) { customer = new Customer(); customer.setCustomerId(customerId); } customer.setCustomerFirstName(customerFirstName); customer.setCustomerLastName(customerLastName); customer.setStreetAddress1(streetAddress1); customer.setStreetAddress2(streetAddress2); customer.setCity(city); customer.setStateOrProvince(stateOrProvince); customer.setPostalCode(postalCode); customer.setCountry(country); customer.setHomePhone(homePhone); customer.setMobilePhone(mobilePhone); customer.setPrimaryEmailAddress(primaryEmailAddress); customer.setSecondaryEmailAddress (secondaryEmailAddress); customerDao.saveCustomer(customer); }
What did this code just do? Actually it's pretty versatile code. It saves a Customer no matter whether it is new or preexisting. It saves a Customer no matter whether their last name changed or they moved to a new home. It saves a Customer no matter whether they got a new home phone number, or if they discontinued their home phone service, or whether they got a mobile phone for the first time, or both. It even saves a Customer if they switched from using Juno to using Gmail instead, or if they changed jobs and now have a new work email address. Wow, this is an awesome method!
Or is it? Actually, we have no idea under what business situations this saveCustomer() method is used—not exactly, anyway. Why was this method created in the first place? Does anyone remember it's original intent, and all the motivations for changing it to support a wide variety of business goals? Those memories have quite likely been lost only a few weeks or months after it was created and then modified. And it gets even worse. You don't believe me? Look at the next version of this same method:
@Transactional public void saveCustomer( String customerId, String customerFirstName, String customerLastName, String streetAddress1, String streetAddress2, String city, String stateOrProvince, String postalCode, String country, String homePhone, String mobilePhone, String primaryEmailAddress, String secondaryEmailAddress) { Customer customer = customerDao.readCustomer(customerId); if (customer == null) { customer = new Customer(); customer.setCustomerId(customerId); } if (customerFirstName != null) { customer.setCustomerFirstName(customerFirstName); } if (customerLastName != null) { customer.setCustomerLastName(customerLastName); } if (streetAddress1 != null) { customer.setStreetAddress1(streetAddress1); } if (streetAddress2 != null) { customer.setStreetAddress2(streetAddress2); } if (city != null) { customer.setCity(city); } if (stateOrProvince != null) { customer.setStateOrProvince(stateOrProvince); } if (postalCode != null) { customer.setPostalCode(postalCode); } if (country != null) { customer.setCountry(country); } if (homePhone != null) { customer.setHomePhone(homePhone); } if (mobilePhone != null) { customer.setMobilePhone(mobilePhone); } if (primaryEmailAddress != null) { customer.setPrimaryEmailAddress(primaryEmailAddress); } if (secondaryEmailAddress != null) { customer.setSecondaryEmailAddress (secondaryEmailAddress); } customerDao.saveCustomer(customer); }
I have to note here that this example isn't as bad as it gets. Many times the data mapping code becomes quite complex, and a lot of business logic gets tucked away in it. I'm sparing you from the worst in this example, but you've probably seen it for yourself.
Now each of the parameters other than the customerId is optional. We can now use this method to save a Customer under at least a dozen business situations, and more! But is that really a good thing? How could we actually test this method to ensure that it doesn't save a Customer under the wrong situations?
Without going into extensive detail, this method could function incorrectly in more ways than it could correctly. Perhaps there are database constraints that prevent a completely invalid state from being persisted, but now you have to go look at the database to be sure. Almost certainly it will take you some time to mentally map between Java attributes and column names. Once you've figured out that part you find that the database constraints are missing or incomplete.
You could look at the possibly many clients (not counting those added after the user interface was completed to manage automatic remote clients) and compare source revisions to gain some insight into why it is implemented that way it is right now. As you search for answers you learn that nobody can explain why this one method works the way it does, or how many correct uses there are. It could take several hours or days to understand it on your own.
Cowboy Logic AJ: “That fella's so confused, he doesn't know if he's sack'n potatoes or rollerskat'n in a buffalo herd.”
Domain experts can't help here because they would have to be programmers to understand the code. Even if a domain expert or two knew enough about programming or could at least read the above code, they would probably be at least equally at a loss as a developer as to all that code is meant to support. With all these concerns in mind, do we dare change this code in any way, and if so, how?
There are at least three big problems here:
- There is little intention revealed by the saveCustomer() interface.
- The implementation of saveCustomer() itself adds hidden complexity.
- The Customer “domain object” isn't really an object at all. It's really just a dumb data holder.
Let's call this unenviable situation anemia induced memory loss. It happens all the time on projects that produce this kind of implicit, completely subjective, code “design.”
By now you should be worried about this kind of code and how you can create a better design. The good news is, you can succeed in producing an explicit, carefully crafted design in your code.