Domain-Driven Design
I used to always think I knew better.
I nearly failed my databases course in college because I (hilariously) thought SQL-based databases would be dead soon thanks to “NoSQL technology.” Relational algebra and SQL statements seemed like a waste when you could just write some JavaScript and slam everything into memory.
The universe has a sense of humor, and the biggest technical failings in my career have been direct results of shaky data models.1
The data model lives at the core of any software. It is the “secret sauce” that makes the software within a company valuable. The Model represents the company’s best understanding of the problem they are trying to solve.
Over the course of my career, the book Domain-Driven Design has been mentioned more than a time or two. Eventually, the steady drip became a torrent and I designed to finally pick up a copy.
In many ways, DDD is the book I wished I had read much earlier. In other ways, this year was the best year to read the book.
Learning by Doing
The content of Domain-Driven Design really only sticks after you’ve seen the problems the book describes.
DDD applied too early could yield a programmer that might be more book-smart than street-smart. For some—such as my younger self—the book would have remained unfinished. Too aloof and too disconnected from the day-to-day, I would have told myself. Or, Written in 2004? It’s already obsolete!
But behind the diagrams and Small Caps, the book nails a crucial point: problems in software construction are really problems in communication.
A project faces serious problems when its language is fractured. Domain experts use their jargon while technical team members have their own language tuned for discussing the domain in terms of design… The terminology of day-to-day discussions is disconnected from the terminology embedded in the code (ultimately the most important product of a software project).
This can either be communication among developers, or communication between developers and stakeholders. Without a continuously-refined common language, everyone talks past each other 10% of the time. This mismatch becomes an easy scapegoat once deadlines approach: “Oh, you meant that type of BillingDataTranformation
. No wonder why it doesn’t do what we expect it to.”
Thus, DDD advocates for a shared Ubiquitous Language. It’s a language that both developers and stakeholders can share to describe the domain and the problem.
If the design, or some central part of it, does not map to the domain model, that model is of little value, and the correctness of the software is suspect.
This Ubiquitous Language concept resonates with me at work. At Gusto, we have people who know tax code as well as software developers know computer code. It’s amazing to see others at the top of their game, but within a different discipline.
For the code we write, it’s imperative that we develop and maintain a Ubiquitous Language with these domain experts. It makes the developers’ lives so much easier when the discussion from a meeting maps to class names in the source code.
Knowledge Crunching
As developers write code, it’s normal to refactor things as you go. Replace Method with Method Object. Pull some implementation up into a superclass. Cut down on a public interface. And so forth.
But many of these operations, like those found in Refactoring, are implementation focused. They are primarily a tool of organizing small pieces at a time, shoveling some bits from this file into this other file. (DDD refers to them as “micro-refactorings.”)
As we work with a system, we also undertake larger refactorings. These refactorings can be composed of hundreds of individual micro-refactorings. These larger refactorings are done to bring our existing data model more in line with our new understanding of a problem. This is the closest we get to a “Eureka!” moment in software development: The structure that we had been wrestling with for weeks suddenly becomes much easier to work with and understand. We have refactored toward deeper insight.
This type of refactoring is crucial as businesses better understand their domain as their once MVPs slowly become the dragon that is legacy software.
DDD advocates for performing these refactorings constantly. As the data model drifts from the Ubiquitous Language, it is necessary to resync the two. To not keep them in sync is to accrue communication debt for years to come.
How to Abstract
As software developers, we are authors of abstraction. What must an implementer need to know, and what can they go without knowing for the time being? Do they need to know that this talks to a database? Sends a web request? Doesn’t handle all the cases?
At the Deconstruct conference this year, abstraction was a topic covered multiple times by many different angles. How do other professions handle abstraction? What can we learn from them?
Abstraction is also an easy thing to bikeshed over. Some folks advocate that only “real developers” intimately know every part of the stack, and the code must reflect that. “Real developers” slang C— no wait—assembly, but only on RISC architectures.2 Every abstraction is a leaky abstraction, to them.
Others can’t be bothered with the minutiae of how a processor smushes numbers together and would rather not think about it. We have software to ship and customers to please, so most developers gladly operate at higher levels of abstraction.
Nonetheless, the debates continue. And here, I found DDD to have one of the most concise definitions abstraction with a metric for how to decide if an abstraction is useful.
If a developer must consider the implementation of a component in order to use it, the value of encapsulation is lost. If someone other than the original developer must infer the purpose of an object or operation based on its implementation, that new developer may infer a purpose that the operation or class fulfills only by chance. If that was not the intent, the code may work for the moment, but the conceptual basis of the design will have been corrupted, and the two developers will be working at cross-purposes.
Abstracting or not abstracting is not a disk-measuring contest for how many machine codes you can recite from memory, but a matter of organizational hygeine.
This definition is useful because it finally decides the color of a very important bikeshed, while the larger book also provides useful atoms for building powerful abstractions. DDD’s simple breakdown of Entities, Value Objects, and Services. The thorough dive into these three fundamental aspects of Model-Driven Design immediately christened me with the vocabulary I had been searching for for a while.
Conclusion
To create a supple, knowledge-rich design calls for a versatile, shared team language, and a lively experimentation with language that seldom happens on software projects.
Much like financial markets, it only takes about a decade before it seems we’ve lost all of our institutional wisdom and go belly up. We have an wisdom crash. We start re-solving solved problems.3 We sharecrop platforms, convinced that this one is going to be permanent. Intellectual fatigue sets in and we feel like all of the interesting problems are already being worked on or have been solved.
Industrial ennui.
Domain-Driven Design reminds us that software doesn’t have to always be terrible, and gives us tools to begin fixing some of the most crucial parts of projects. If you have cut your teeth a bit in the industry and you are looking to improve your outlook on software as a whole, this book is worth the price tag.
Thanks for reading this little book review. What were your takeaways from DDD? Let me know on Twitter.
Special thanks to Justin Duke and Matan Zruya for reading early drafts of this post.
-
Many years ago, I was the CTO of a company called LayerVault. We were trying to build version control for designers. If there was ever a problem to make sure you understood the importance of data modeling, that was it. We were plagued with bugs because I did not do a good job of getting the data model right. ↩
-
I don’t even know if this joke makes sense. ↩
-
GraphQL. ↩