Recently, while I was participating in an interview with a candidate to a software developer position, we asked the candidate for a definition of DDD (you know, Domain Driven Design).
Unfortunately, the candidate’s answer was unsubstantial, and actually wrong:
“To practice DDD is to develop software according to the Domain”
I’d got to stop myself from asking the candidate “Which software is not?“, because it is unfair to blame anyone for not knowing something.
A second thought made me refine my first intention though. There are already other information sources to learn about the concept of DDD. One of them is very obvious: the Eric Evans‘ book itself. However, most of them are full of examples using compiled languages, like Java. So I decided to reconsider my original intention and write a brief review of the key concepts of DDD, illustrated with code written in PHP, the most popular programming language for the web right now.
DDD: a better way to develop software
Domain Driven Design is a methodology to produce software built upon the intimate connection between the actual code and the knowledge about the application shared among all the participants in the conversations with the people in charge of developing that software and the people who knows about the business.
So in practice, DDD is a methodology which key feature is that the code and the conversations among the participants in the development of that code are tightly intertwined. Important detail: the code should be runnable at any stage during its development, so that everyone participating in the conversations may play with it whenever they want to.
In a way, DDD is like the dance hall where code and knowledge dance together cheek to cheek.
Beyond practical details, there is nothing else in DDD that this dance.
But, if this is so, why so serious about The Domain?
Because the word is a very useful abstraction. As a term, the Domain refers to the whole bunch of information that may be relevant, no matter how, to the software we are building. We should think of the Domain as an unstructured set of data as wide as needed so that anything relevant remains in it, and consequently the Domain boundaries are not strictly painted on the floor.
My personal experience is that people in the teams are used to think of applications with clear limits: the Shipping Application, the Accounting Application, the Marketing Application, and so on. Actually this is the traditional way of producing software.
In DDD we must look wider: unless there is absolutely no connection between the Shipping application, the Accounting application, and the Marketing application, they all are part of the same Domain. Even third-party applications, like payment providers, public tax agencies, etc, are in the Domain since there are functional relationships that connect them to our software.
Now that we learn that we must think wider in order to play with DDD, let’s narrow the focus.
The Ubiquitous Language
The most important tool DDD relies on in practice is the list of terms which condense the shared knowledge. Given the specifics that every software application implements, this list of terms is also specific to the application, and consequently we may say that the meaning of every one of those terms is domain-specific.
Being such an important tool, this list of domain-specific terms gets its own name: it is called Ubiquitous Language.
What does it mean domain-specific? That any given word may have a different meaning in different Domain’s Ubiquitous Language. This circumstance is very clear with common-use words, like “customer“, “product“, or “contract“, which differences in meaning might seem slightly but actually deepen as we dive into each Domain’s details, otherwise we would be dealing with one and only Domain again and again.
In fact, the very same application developed in two different companies would ultimately produce a slightly different UL, for those companies must have competitive uniquenesses which clearly become very relevant in their particular Domains, and therefore in their particular UL.
As fas as I know, teams are used to writing their UL in documents, either digital or in paper. Personally, I love to write the words in the UL in paper and stick them on the walls where the team works, so that they stand as close as the eye as possible. They even may be taken out of the wall and passed during conversations.
But, what if one term has different meanings in different parts of the Domain? Is that even possible? Yes, it is, because company language comes out of the culture, the company’s culture, and its environment culture, and culture is inconsistent per se.
For instance, every one in Accounting is used to talk about “accounts“, and they all know what they are referring to. But those “accounts” have nothing in common with the “accounts” that people in Finance are used to. The former stands for a way to classify the ledgers, and so there are Income accounts, and Fixed Assets accounts, etc, in Accounting. While the latter refers to money held in Banks on behalf on its owners.
Whenever this happens in DDD, we say we just found two separated Bounded Contexts.
A Bounded Context is like a Domain which belongs to a bigger Domain, because there are relationships that connect it to other Bounded Contexts in the same Domain. Some operations in Accounting accounts have impact on Bank accounts, for instance whenever the company pays a bill to one of its suppliers. And the opposite holds as well, for instance when customers pay their bills.
Some operations may even impact on more than two bounded contexts. For instance, whenever the company pays its taxes, there are at least three bounded contexts involved: Accounting, Finance, and Taxes.
Every Bounded Context has its own Ubiquitous Language. At the same time, there is a unique, application-wide Ubiquitous Language that includes terms common to all Bounded Contexts.
What happens at the boundaries between BCs?
Well, the culture reigns there. I have seen companies failing to implement DDD mostly because some terms were commonly used everywhere, but hold a different meaning in some parts of the company. That made software pieces less understandable, because they had to invent translations which were no used beyond the contracts between BCs.
For instance, a team found that Sales and Marketing both used the term “lead” to refer to people at any stage of their way to become a customer. But Sales and Marketing were actually two different BCs for other reasons, and conflicts in their common relationship happened because of the simple fact that a person who is a lead in Marketing may, or may not, ultimately become a lead in Sales.
As a mistake, this might not look a big deal, but it is actually because it breaks an implicit rule in DDD which says that the software and the shared knowledge must be so intertwined that any person in the team must be able to read, and understand, the code.
In other words, a mess. Mess not as a question of philosophy, or aesthetics. Mess as chaos which spreads, mess that makes people lose focus, so that the software they produce gets more and more buggy, hard to change, and ultimately far less efficient.
Some times this mess catastrophe is avoided thanks to brilliant people in the team, who keep the translations clear in their minds. Unfortunately, this is risky, because people ultimately goes away.
Following the example above, in another company I also worked with, Marketing people handled “Marketing Qualified Leads“, or MQLs, whereas Sales people worked with “Sales Qualified Leads“, or SQLs. And this gets even better because they had already defined one single operation (what we later are calling a Domain Event) which converts an MQL into an SQL. All this was previous to start working with DDD, but it clearly made our code extremely more understandable.
The task of looking into the Domain, to identify BCs and the relationships among them, and to make explicit the Ubiquitous Language, is a team task which requieres some individual work between conversations, and that never ends. There is a beautiful term for this task, to explore the Domain, a task I love insanely.
Since it comes out of a dance between knowledge and code, DDD is iterative by nature. Almost all software development methodologies are iterative nowadays, so DDD is not special by this.
What makes DDD special is that the code and the conversations by which the shared knowledge arises, are intertwined. A capital consequence of this tight relationship is that the software must be runnable at every stage of the process, and that the code must be readable by every participant. These two requirements impact the code deeply, as we’ll see in the examples.
One way to ensure that the software reflects exactly what the knowledge out of the conversations say is by producing tests. Therefore, tests are instrumental in DDD. But tests are a key instrument, which allows teams practicing DDD to practice TDD techniques as well.
Also, the requirement that the code must be runnable at every stage, and available to every participant in the conversations, makes Continuous Integration and Continuous Deployment operational practices a must. CI/CD are not only convenient to save time but also necessary to make possible that every participant may run the code as it is at every moment.
All these operational techniques help DDD keep the conversations, and so the shared knowledge, and the software alligned and evolving together.
So, as a resume, we see that practice DDD requieres teams to:
- Do tests, even TDD, in a Continuous Integration environment.
- Run Continuous Deployment.
- Produce code understandably readable by every participant, and structured in a way that purely reflects the shared knowledge.
To say it plainly: DDD is not easy, it requieres skilled people to make it work.
On the other hand, DDD can be extremely efficient producing runnable software, in the sense that it is software less prone of error, very close to the business needs it was produced to serve, and easier to evolve since every one can understand what it does.
- DDD is a methodology to produce better software in an efficient way.
- DDD works by gathering skilled people in a team and let them talk. Some of them are software developers, and some of them are not.
- Conversations must be followed by new code which reflects the shared knowledge collected during them.
- Code must be runnable after every iteration.
- Code must be readable and understandable by every participant in the conversations.
In addition to this, there are some common terms very common in DDD:
- The Domain is an abstract term to refer to the whole information, explicit or not, which is relevant for the application.
- The collection of terms and their specific meaning in the Domain is called Ubiquitous Language.
- Some parts of the Domain are functionally closer than others. These parts are called Bounded Contexts, and they all have their own Ubiquitous Language.
- Relationships among Bounded Contexts requiere to set ways to union their individual Ubiquitous Languages. Whenever an intersection exists among these Languages, conflicts might happen. Therefore solutions must be put in place before going on with the code that implements those relationships.
Let’s practice with an example of this in the next post in this series. In the meantime, do not hesitate to drop your comments below.