How To Do DDD
Let's back away from heavy implementation discussions for a moment to consider one of the most empowering feature of DDD, the Ubiquitous Language. It's one of the two primary pillars of DDD's strengths, the second being the Bounded Context (2), and one cannot properly stand without the other.
Ubiquitous Language
The Ubiquitous Language is a shared team language. It's shared by domain experts and developers alike. In fact, it's shared by everyone on the project team. No matter your role on the team, since you are on the team you use the Ubiquitous Language of the project.
This is no gimmick to get developers to find the same page with domain experts. It's not just a bunch of business jargon being forced on developers. It's a real language that is created by the whole team—domain experts, developers, business analysts, everyone involved in producing the system. The Language may start out with terms that are the natural lingo of the domain experts, but it isn't limited to that because the Language must grow over time. Suffice it to say that when multiple domain experts are involved in creating the Language, they often disagree ever so slightly on the terms and meanings of what they thought were already ubiquitous.
Table 1.5 Analyzing the best model for the business.
Which is better for the business? Though the second and third are similar, how should it be designed? |
|
“Who cares? Just code it up.” Um, not even close. |
patient.setShotType(ShotTypes.TYPE_FLU); patient.setDose(dose); patient.setNurse(nurse); |
“We give flu shots to patients.” Better, but misses some important concepts. |
patient.giveFluShot(); |
“Nurses administer flu vaccines to patients in standard doses.” This seems like what we'd like to run with at this time, at least until we learn more. |
Vaccine vaccine = vaccines.standardAdultFluDose(); nurse.administerFluVaccine(patient, vaccine); |
In this example, we not only model the administration of flu vaccines in code, the team must also speak the Language openly. When the team discusses this aspect of the model, they literally speak phrases such as: “Nurses administer flu vaccines to patients in standard doses.”
There will be some haggling and wrangling over the Language that exists in the minds of experts and what evolves from there. It's all part of the natural progression of developing the best Language that will matter a lot for a long time. This happens through open discussion, looking at existing documents, business tribal knowledge that finally surfaces, as well as referencing standards, dictionaries, and thesauruses. There's also a point reached were we come to terms with the fact that some words and phrases just don't aptly fit the business context as well as once thought, and we realize that others fit it much better.
So how do you capture this all-important Ubiquitous Language? Here are some ways that work as experimentation leads to advancement:
- Draw pictures of the physical and conceptual domain and put names and actions on them. These are mostly informal but may contain some aspects of formal software modeling. Even if your team does some formal modeling with UML, you want to avoid any kind of ceremony that will bog down discussions and stifle the creativity of the ultimate Language being sought.
- Create a glossary of terms with simple definitions. List alternative terms including the ones that show promise and the ones that didn't work, and why. As you include definitions you cannot help but develop reusable phrases for the Language because you are forced to write in the language of the domain.
- If you don't like the idea of a glossary, still capture some kind of documentation that includes the informal drawings of important software concepts. Again, the goal here is to force additional language terms and phrases to surface.
- Since only one or a few team members may capture the glossary or other written documents, circle back with the rest of the team to review the resulting phrases. You won't always, if ever, agree on all the captured linguistics, so be agile and ready to edit heavily.
Those are some ideal first steps to coining a Ubiquitous Language that fits your specific domain. However, this is absolutely not the model that you are developing. It's only the genesis to the development of the Ubiquitous Language that will very soon be expressed in your system's source code. We are talking Java, or C#, or Scala, or some other programming language of choice. These drawings and documents also don't address that the Ubiquitous Language will utterly continue to expand and morph over time. The artifacts that originally led us down an inspiring path to developing a useful Ubiquitous Language that was just right for our specialized domain, are very likely rendered obsolete over time. That's why in the end it is team speech and the model in the code that is the most enduring and the only guaranteed current denotation of the Ubiquitous Language.
Since team speech and the code will be the lasting expression of our Ubiquitous Language, be quite prepared to abandon the drawings, glossary, and other documentation that will be difficult to keep up to date with the spoken Ubiquitous Language and source code as it is rapidly enhanced. This is not a requirement of using DDD, but it is pragmatic because it becomes impractical to keep all the documentation in sync with the system.
With this knowledge we can redesign the saveCustomer() example. What if we chose to make Customer reflect each of the possible business goals that it must support?
public interface Customer { public void changePersonalName( String firstName, String lastName); public void postalAddress(PostalAddress postalAddress); public void relocateTo(PostalAddress changedPostalAddress); public void changeHomeTelephone(Telephone telephone); public void disconnectHomeTelephone(); public void changeMobileTelephone(Telephone telephone); public void disconnectMobileTelephone(); public void primaryEmailAddress(EmailAddress emailAddress); public void secondaryEmailAddress(EmailAddress emailAddress); }
We can argue that this is not the best model for a Customer, but when implementing DDD questioning the design is expected. As a team we are free to haggle over what is the best model and settle only after we've discovered the Ubiquitous Language that is agreed upon. Still, the above interface does explicitly reflect the various business goals that a Customer must support, even if the Language could be improved by refinements again and again.
It's important to understand too that the Application Service (4, 14) would also be refactored to reflect the explicit intentions of the business goals at hand. Each Application Service method would be modified to deal with a single use case flow or user story:
@Transactional public void changeCustomerPersonalName( String customerId, String customerFirstName, String customerLastName) { Customer customer = customerRepository.customerOfId(customerId); if (customer == null) { throw new IllegalStateException("Customer does not exist."); } customer.changePersonalName(customerFirstName, customerLastName); }
This is different from the original example because in that code a single method was used to deal with many different use case flows or user stories. In the new example we have limited a single Application Service method to deal with changing the personal name of the Customer, and nothing more. Thus, when using DDD it is our job to refine Application Services accordingly. This implies that the user interface likewise reflects a narrower user goal, which may have previously been true. Now, however, this specific Application Service method doesn't require its client to pass 10 nulls following the first and last name parameters.
Doesn't this new design put your mind at ease? You can read the code and easily comprehend it. You can also test it and confirm that it does exactly what it is meant to do, and that it doesn't do anything that it shouldn't.
Thus, the Ubiquitous Language is a team pattern used to capture the concepts and terms of a specific core business domain into the software model itself. The software model incorporates the nouns, adjectives, verbs, and richer expressions formally formulated and spoken by the close-knit team. The software and the tests that verify the model's adherence to the tenets of the domain, both capture and adhere to this Language, the same spoken by the team.
Ubiquitous, but Not Universal
Some further clarification about the reach of a Ubiquitous Language is in order. There are a few basic concepts that we need to keep carefully in mind:
- Ubiquitous means pervasive, or “found everywhere,” as spoken among the team and expressed by the single Domain Model that the team develops.
- The use of the word ubiquitous is not attempting to describe some kind of enterprise-wide, company-wide, or world-wide, universal domain language.
- There is one Ubiquitous Language per Bounded Context (2).
- Bounded Contexts are relatively small; smaller than we might at first imagine. It is large enough only to capture the complete Ubiquitous Language of the isolated business domain, and no larger.
- The Language is ubiquitous only within the team that is working on the project that develops in an isolated Bounded Context.
- On a single project that develops a single Bounded Context, there are always one or more additional isolated Bounded Contexts that it integrates with using Context Maps (3). Each of the multiple Bounded Contexts that integrate have their own Ubiquitous Language, even though some terms of each may overlap.
- If you try to apply a single Ubiquitous Language to an entire enterprise, or worse, universally among many enterprises, you will fail.
When you begin a new project in which you are properly using DDD, identify the isolated Bounded Context that is being developed. This places an explicit boundary around your domain model. Discuss, research, conceptualize, develop, and speak the Ubiquitous Language of the isolated domain model within the explicit Bounded Context. Reject all concepts that are not part of the agreed upon Ubiquitous Language of your isolated Context.