Discovering the Domain Architecture
- The real added value of domain-driven design
- The ubiquitous language
- Bounded contexts
- The layered architecture
- Finishing with a smile
- Essentially, all models are wrong, but some are useful.
- —George P. O. Box
Software definitely stems from mathematics and is subject to two opposing forces: the force of just doing things and the force of doing things right. We like to think that software is overall a big catch-me-if-you-can game. Visionary developers riding on the wings of enthusiasm quickly build a prototype that just works. The prototype then becomes a true part of the business; sometimes what originally was just a prototype changes and expands the business. Next, more down-to-earth developers join in to analyze, stabilize, consolidate or, in some cases, rewrite the software as it should have been done the first time, in accordance to theoretical principles.
Software, however, cannot be constrained into too-formal and rigid theorems.
Software mirrors real life, and life follows well-known rules defined in the context of some model. Unfortunately, at some given time, T1, we find that our understanding of the model is in some way limited. At some later time, T2, our understanding might deepen and we become aware of extended rules that better explain the overall model.
That’s how things go in the real world out there; but it’s not always how we make things go in real software.
Software architects tend to restrict the solution within the boundaries of a fixed, top-level architecture. We have done it ourselves many times. If we look back, we find it is a common error to start defining some top-level architecture (for example, Client/Server, Layered Architecture, Hexagonal) and then use it everywhere in our business application. That approach might work in the end, but the working solution you reach descends from the wrong approach. It’s a sort of technical debt you are going to end up paying at some point.
Because software is expected to mirror real life, as a software architect you should first understand the segment of the real world you are modeling with software. That segment of the real world is the business domain, and it might contain multiple business contexts, each calling for its own ideal architecture.
The real added value of domain-driven design
Domain-Driven Design, or DDD for short, is a particular approach to software design and development that Eric Evans introduced over a decade ago. The essence and details of DDD are captured in the book Domain-Driven Design, which Evans wrote for Prentice Hall back in 2003. The book subtitle transmits a much clearer message about the purpose of DDD: Tackling Complexity in the Heart of Software.
When it debuted, DDD was perceived as an all-or-nothing approach to application design. You were given a method, some quite innovative guidelines, and the promise that it would work. Using DDD was not actually cheating, and we dare say that it really fulfilled the promise of “making it work”—except that it works only if you do it right. Doing it right is not immensely hard; but it’s also immensely easy to do it wrong. (See Figure 5-1.)
FIGURE 5-1 The DDD road to success is not always as smooth and easy as you expect.
What makes DDD so powerful but also so error prone? We think it’s the context.
DDD is about crunching knowledge about a given business domain and producing a software model that faithfully mirrors it. The business domain is how a company conducts its own business: it’s about organization, processes, practices, people, and language. The business domain lives in a context. Even very similar businesses may live in different contexts.
DDD is easy and powerful to use if you crunch enough knowledge and can model faithfully. DDD is painful and poor if you lack knowledge or fail to turn your knowledge into a model that fits the business domain.
What’s in DDD for me?
However you want to frame it, DDD represents a significant landmark in software development. Not coincidentally, DDD initially was developed in the Java space, where the adoption of advanced design techniques (in the beginning of the last decade) was much faster and more widespread than in the .NET space. For many years, the scope and relevance of DDD was not really perceived in the .NET space.
Do we really need it? That was the question we are often asked and have asked ourselves many times.
DDD is not right for every project because it requires mastery and might have high startup costs. At the same time, nothing in DDD prevents you from using it in a relatively simple system. As we see it, the crucial point to using DDD is understanding where its real value lies and learning techniques to take advantage of it. The two biggest mistakes you can make with DDD are jumping on the DDD bandwagon just because it sounds cool, and stubbornly ignoring DDD because in the end you think your system is only a bit more complex than a plain CRUD.
In summary, we think DDD has two distinct parts. You always need one and can sometimes happily ignore the other.
DDD has an analytical part that sets out an approach to express the top-level architecture of the business domain in terms of bounded contexts. In addition, DDD has a strategic part that relates to defining a supporting architecture for the identified bounded contexts.
The real added value of DDD lies in using the analytical part to identify bounded business contexts. Next, the strategic design might or might not be leveraged to implement any of the bounded contexts.
Conducting analysis using DDD
The analytical part of DDD consists of two correlated elements: the ubiquitous language and bounded contexts.
The ubiquitous language is a vocabulary shared by all parties involved in the project and thoroughly used throughout the projects, ideally in all forms of spoken and written communication. As an architect, you typically populate the vocabulary of verbs and nouns as you acquire knowledge about the domain. This is the most common approach to starting to populate the vocabulary. More generally, you should also carefully look into adverbial phrases you find in requirements, because they might reveal a lot about the domain, such as events, processes, and triggers of processes.
The ubiquitous language is also the template that inspires the names and structure of the classes you end up writing. The ubiquitous language serves to improve and speed up the acknowledgment of requirements and simplify communication between parties so that they avoid misunderstandings, flawed assumptions, and botched translations when moving from one set of jargon to another.
Initially, there’s just one ubiquitous language and a single business domain to understand and model. As you come to understand the requirements and explore the domain further, you might discover some overlap between nouns and verbs and find that they have different meanings in different areas of the domain. This might lead you to think the original domain should be split into multiple subdomains.
Bounded context is the term used with DDD to refer to areas of the domain that are better treated independently because of their own ubiquitous language. Put another way, you recognize a new bounded context when the ubiquitous language changes. Any business domain is made of contexts, and each context is shaped by logical contours. The primary responsibility of a software architect is identifying business contexts in a domain and defining their logical contours.
Context mapping is an expression often used to refer to the analytical part of DDD. Context mapping is a universal technique that can be applied to nearly any software scenario. Context mapping builds a high-level view of the domain from the perspective of a software architect. It shows subdomains and their relationships and helps you make strategic decisions.
Strategic model design
Coupled with context mapping is strategic model design. Once you identify the various bounded contexts, your next problem is determining the best architecture for each. DDD offers a recommended architecture in the form of the layered architecture and Domain Model. The term domain model here is subject to interpretation and deserves a bit of attention.
In the definition of DDD that Evans gives in his seminal book, the term domain model gives a nod to the Domain Model pattern formalized by Martin Fowler: http://martinfowler.com/eaaCatalog/domainModel.html. It consists of special flavor of an object model (also known as the domain model or entity model) and a set of domain service classes. More recently, the internal structure of the domain model is being reconsidered within the community. While seeing the domain model as an ad hoc collection of objects is still the most common perspective, a functional vision of it is gaining ground. Functional programming is, in fact, in many ways preferable to object-orientation for implementing tasks and expressing business concepts.
In the end, we can rephrase the whole thing today by saying that DDD suggests a layered architecture designed around a model of the domain. The model is mostly an object model, but it can be other things too—for example, a collection of functions. The persistence of data also depends on the structure of the model. It might require an O/RM tool if the model is a collection of objects; it might even be based on stored procedures invoked from idiomatic wrapper components if the model is, for example, function-based.
In the next chapters, we’ll explore in depth the most common scenario for the domain model—when it takes the form of a special object model.
The phase of strategic model design consists of evaluating the various architectural options and choosing the architecture for each bounded context. Beyond the layered architecture, with a domain model there are usually other options such as a plain CRUD, a CMS (when the bounded context is expected to be a website), or even more sophisticated things, such as event sourcing (which we’ll talk about in upcoming chapters.
Which parameters should drive your choice?
Overall, we think that today there’s only one guiding rule, and it’s based on the (carefully) estimated lifetime of the software you are about to write. Let’s go through a few scenarios.
Suppose you are writing a short-term, one-off application such as a survey web application or some analogous set of pages aimed at collecting raw data for your analysts. You know that the expected lifetime is very short and that after the expected data has been collected the app will be bluntly dismissed.
Does it really make sense to invest more than the least amount of time that could possibly make it work? It probably doesn’t.
So you can go with the quickest possible CRUD you can arrange, whether it is by using Web Forms, Silverlight, or plain HTML, depending on the skills and the target audience. If you are about to think something like, “Hey, I’m a senior architect, and no boss would pay my time for such trivial problems,” well, you are probably just experiencing the power of bounded contexts already. Taken out of context, a fast-food application is undoubtedly a very basic—even silly—example. But it might be just one bounded context of a much larger and more complex domain that you, as a senior architect, helped to map.
The project you’re on requires a web front end. It has a sufficiently complex back end, where a lot of business rules must be taken into account and a bunch of external web services must be coordinated, but at the end of the day the front end is a plain set of read-only pages with zero or limited forms. The most important requirement you have for it is, “It must be shockingly cool and engage people.”
Web Forms can be immediately ruled out because of its limited flexibility due to server controls. ASP.NET MVC is a much better option because it allows full control of HTML and can be effectively styled with CSS. Should you really go with an ASP.NET MVC solution from scratch?
Couldn’t a CMS be a quicker and equally cool solution?
We can probably hear the same objections—it’s a silly example for a book that claims to target software architects. Yes, but a software architect recognizes complexity where she sees it and doesn’t create any unnecessary complexity.
You might know that plugins can extend a CMS, like WordPress, to do almost anything you can think of. It’s not a far-fetched idea to just get a cool WordPress theme and a bunch of plugins, including custom plugins, to do the job.
Again, it’s a matter of opportunity and skills. It’s a matter of context.
Any other types of applications
As Thomas Edison used to say, the value of an idea lies in the using of it. So make-the-code-just-work is a common approach, especially in these hectic days of emerging ideas and startups. The make-the-code-just-work motto is fine if you don’t need to touch the code once it’s done and it works.
No matter what the customer might say and no matter what the current plans are, there’s just one reason that any software avoids further changes: it gets dismissed. If the software is not expected to be dismissed in just a few months, as an architect you better consider a more thoughtful approach than just fast-food code. And Domain Model and the other supporting architectures we’re slated to discuss in the upcoming chapters are the best options available for simplifying code maintenance.