Why choose Domain-Driven Design?
The term Domain-Driven Design (DDD) was coined by Eric Evans in his now-iconic book Domain-Driven Design: Tackling Complexity in the Heart of Software published by Addison-Wesley back in 2004.
More than a decade after the book was published, interest in the practices and principles described in the book started to grow exponentially. Many factors influenced this growth in popularity, but the most important one is that Domain-Driven Design explains how people from the software industry can build an understanding of their users' needs and create software systems that solve the problem and make an impact.
This article is taken from the book Hands-On Domain-Driven Design with .NET Core by Alexey Zimarev. In this book you will apply domain-driven design principles using modern tools such as EventStorming, Event Sourcing, and CQRS. You will learn how domain-driven design applies to various architectural styles such as REST, reactive systems, and microservices.
In this article, we will discuss how understanding the business domain, building domain knowledge, and distinguishing essential complexity from accidental complexity can help in creating software that matters.
Understanding the problem
We rarely write software to just write some code. Of course, we can create a pet project for fun and to learn new technologies, but professionally we build software to help other people to do their work better, faster, and more efficiently. Otherwise, there is no point in writing any software in the first place. It means that we need to have a problem that we intend to solve.
Problem space and solution space
In their book Human Problem Solving, Allen Newell and Herbert Simon outlined the problem space theory. The theory states that humans solve problems by searching for a solution in the problem space. The problem space describes the initial and desired states, as well as possible intermediate states. It can also contain specific constraints and rules that define the context of the problem. In the software industry, people operating in the problem space are usually customers and users.
Each real problem demands a solution, and if we search properly in the problem space, we can outline which steps we need to take to move from the initial state to the desired state. This outline and all the details about the solution form a solution space.
Jumping to a solution is very easy, and since most of us have a rather rich experience of solving everyday problems, we can find solutions for many issues almost immediately. However, as Bart Barthelemy and Candace Dalmagne-Rouge suggest in their article When You're Innovating, Resist Looking for Solutions, thinking about solutions prevents our brain from thinking about the problem. Instead, we start going deeper into the solution that first came to our mind, adding more levels of detail and making it the most ideal solution for a given problem.
There's one more aspect to consider when searching for a solution to a given problem. There is a danger of fixating all your attention on one particular solution, which might not be the best one at all but it was the first to come to mind, based on your previous experiences, your current understanding of the problem, and other factors,
The exploratory approach to find and choose solutions involves quite a lot of work to try out a few different things, instead of concentrating on the iterative improvement of the original good idea. However, the answer that is found during this type of exploration will most probably be much more precise and valuable. We will discuss fixation on the first possible solution later in this chapter.
What went wrong with requirements
We are all familiar with the idea of requirements for software. Developers rarely have direct contact with whoever wants to solve a problem. Usually, some dedicated people, such as requirements analysts, business analysts, or product managers, talk to customers and generalize the outcomes of these conversations in the form of functional requirements.
Requirements can have different forms, from large documents called a requirements specification to more agile means such as user stories. Let's have a look at this example:
"Every day, the system shall generate, for each hotel, a list of guests expected to check in and check out on that day."
As you can see, this statement only describes the solution. We cannot possibly know what the user is doing and what problem our system will be solving.
In contrast, with user stories, we have more insight into what our user wants. Let's review this real-life user story: "As a warehouse manager, I need to be able to print a stock-level report so that I can order items when they are out of stock." In this case, we have an insight into what our user wants. However, this user story already dictates what the developers need to do. It is describing the solution.
We should not think that the requirements are a waste of time. There are many excellent analysts out there who produce high-quality requirements specifications. However, it is vital to understand that these requirements almost always represent the understanding of the actual problem from the point of view of the person who wrote them.
However, lean and agile methodologies embrace more direct communication between developers and end users. Understanding the problem by all stakeholders, from end users to developers and testers, finding solutions together, eliminating assumptions, building prototypes for end users to evaluate—all these things are being adopted by successful teams, and are also closely related to domain-driven design.
Dealing with complexity
In software, the idea of complexity is not much different from the real world. Most software is written to deal with real-world problems. Those problems might sound simple but be intrinsically complex, or even wicked. Realizing what kind of complexity, we are dealing with when creating software thus becomes very important.
In 1986, the Turing Award winner Fred Brooks wrote a paper called No Silver Bullet – Essence and Accident in Software Engineering in which he made a distinction between two types of complexity—essential and accidental. Essential complexity comes from the domain, from the problem itself, and it cannot be removed without decreasing the scope of the problem. In contrast, accidental complexity is brought to the solution by the solution itself—this could be a framework, a database, or some other infrastructure, with different kinds of optimization and integration.
Brooks argued that the level of accidental complexity decreased substantially when the software industry became more mature. High-level programming languages and efficient tooling give programmers more time to work on business problems. However, as we can see today, more than 30 years later, the industry still struggles to fight accidental complexity. Indeed, we have power tools in our hands, but most of those tools come with the cost of spending the time to learn the tool itself.
We often seem to get problem statements that are more complex than the problems themselves. Usually, this happens due to problems being mixed with solutions, as we discussed earlier, or due to a lack of understanding.
Domain-driven design helps you focus on solving complex domain problems and concentrates on the essential complexity. For sure, dealing with a new fancy front-end tool or using a cloud document database is fun. But without understanding what problem we are trying to solve, it all might be just waste. It is much more valuable to any business to get something useful first and try it out than getting a perfect piece of state-of-the-art software that misses the point entirely. To do this, domain-driven design offers several useful techniques for managing complexity by splitting the system into smaller parts and making these parts focus on solving a set of related problems.
Many junior developers tend to think that software development is just typing code. Well, the reality is harshly different. In fact, after getting some experience and after deliberately spending months and maybe years in death-marches towards impossible deadlines and unrealistic goals, people usually slow down. They begin to understand that writing code immediately after receiving a specification might not be the best idea. Being obsessed with solutions instead of understanding the problem, ignoring essential complexity and conforming to biases—all these factors influence us when we are developing software. As soon as we get more experience and learn from our own mistakes and, preferably, from the errors of others, we will realize that the most crucial part of writing useful, valuable software is the knowledge about the problem space, for which we are building a solution.
Domain knowledge is knowledge about the domain in which you are going to operate with your software. If you are building a trading system, your domain is financial trading, and you need to gain some knowledge about trading to understand what your users are talking about and what they want.
This all comes to getting into the problem space. If you are not able to at least understand the terminology of the problem space, it would be hard to even speak to your future users. If you lack domain knowledge, the only source of information for you would be the specification. When you do have at least some domain knowledge, conversations with your users become more fruitful since you can understand what they are talking about. One of the consequences of this is building trust between the customer and the developer. Such trust is hard to overestimate. By speaking the domain language to domain experts (your users and customers), you also gain credibility.
Obtaining domain knowledge is not an easy task. The art of obtaining domain knowledge is through effective collaboration. Domain experts are the source of ultimate truth (at least, we want to treat them like this). However, they might not be. Some organizations have fragmented knowledge; some might just be wrong. Knowledge crunching in such environments is even harder, but there might be bits and pieces of information waiting to be found at the desks of some low-level clerks, and your task is to see it.
The general advice here is to talk to many different people from inside the domain, from the management of the whole organization, and from adjacent domains. There are several ways to obtain domain knowledge, and here are some of them:
- Conversations are the most popular method, formalized as meetings. However, conversations often turn into a mess without any visible outcome. Still, some value is there, but you need to listen carefully and ask many questions to get valuable information.
- Observation is a very powerful technique. Software people need to fight their introversion, leave the ivory tower and go to a trading floor, to a warehouse, to a hotel, to a place where business runs, and then talk to people and see how they work. Jeff Patton gave many good examples in his talk at the DDD Exchange 2017.
- Domain Storytelling, a technique proposed by Stefan Hofer and his colleagues from Hamburg University, advocates using pictograms, arrows, and a little bit of text, plus numbering actions sequentially, to describe different interactions inside the domain. This technique is easy to use, and typically there is not much to explain to people participating in such a workshop before they start using it to deliver the knowledge.
- EventStorming was invented by Alberto Brandolini. He explains the method in his book Introducing EventStorming (2017, Leanpub). EventStorming uses post-it notes and a paper roll to model all kinds of activities in a straightforward fashion. Workshop participants write facts from the past (events) on post-its and put them on the wall, trying to make a timeline.
This article briefly touched on the concepts of problem and solution spaces, requirements specifications, complexity, and knowledge. Although at first, these topics don't seem to be directly related to software development, they have a significant impact on how and what we deliver. If you found this post useful, do check out the book, Hands-On Domain-Driven Design with .NET Core by Packt Publishing. By the end of this domain-driven design book, you will have gained the confidence to implement the domain-driven design approach in your organization and be able to explore new techniques that complement what you’ve learned from the book.