Domain-Driven Design (DDD) Again

Original author: Gabriel Schenker
  • Transfer

Introduction


Too many times I met applications that were said to have a domain model and the application was designed based on this domain. However, in reality, everything that I saw was a collection of entities (I would even say DTO) that have a bunch of properties without any real logic related to the entity. In addition, I can find many services of all kinds that contain a colorful mixture of business logic and / or infrastructure. If the application also uses a message bus (such as NServiceBus, Mass Transit Bus or Azure Bus), then of course it is noticeable that some messages are transmitted from one module to another or to several modules. Unfortunately, messages often have very generic names containing the words “update”, “change”, “add” or “delete”, and carry a large amount of payload - dozens of diverse properties. Often the name of the message is not at all obvious whether it is a team or an event, and to determine this, one has to dig deep into the implementation.

I sincerely wish that everything written above would be an exaggeration or would only make sense for the "old" applications that have grown and gone out of control. But the sad truth is that this applies to many new projects, even those that are only a few months old. Why it happens? Of course, there are many different reasons: lack of knowledge is one of the most important.

DDD - What is important?


When we want to create a new application, ideally from scratch, and we want to use DDD, we need to consider this: not everything that was written in the blue book is equally important. Unfortunately, the first 14 chapters or so describe implementation details that have more in common with good object-oriented programming and not so much with subject-based design. The last few chapters of the book contain very interesting things, but many developers do not read so far, and have already begun work on the implementation of their applications.

In search of a single language


When we start a new project, we must first try to truly understand the subject area of ​​the business for which we are building the application. We need to talk with interested persons and experts in the field (yes, often these are representatives of the client for whom we create software) and try to understand their language. We need to listen carefully to what they say and how they say. Do they use certain words that are understandable and used in the same way by everyone working in this subject area? Is the meaning of these words unambiguous? If not, then we should ask questions and demand from experts a more detailed description of their terminology, or a change in wording, or even use some analogies. One of my favorite “games” is to ask experts about how would they do their tasks in the absence of a computer. What will be actions and what are objects, objects, concepts or nouns?

Over time (yes, it may take several days or weeks to do it right), we will have a common vocabulary that is understandable to everyone: interested parties, subject matter experts, business analysts, architects, developers and testers. We will call this dictionary or “language” “one language” .

Please note that a single language is likely to develop throughout the life of the project as we gain a deeper and deeper understanding of the subject area, studying all its subtleties and bottlenecks.

Sharing a difficult problem


After we have found or identified a single domain language, we can begin to model the domain. But much more often the subject area of ​​the business for which we are going to write software is complex enough to understand everything at once and / or to easily lose sight of something. Thus, we must begin to divide the entire complex area into smaller parts of less complexity. This is the same approach as during a meal - we are not able to swallow the whole steak at once, but we can cut it into pieces and then eat one piece after another.

We can do this with every subject area. With the help of experts, it is necessary to identify sub-domains or areas that we can isolate from each other and look at them separately in order to solve them separately. We call these sub-domainslimited contexts (approx.per. - bound contexts) . A restricted context is a part of the entire subject area that has clear boundaries, interacts via clearly defined interfaces with other limited contexts, and which can be individually determined.

Definition of interfaces and contracts


Now that we have divided a complex business domain into smaller and less complex limited contexts, we need to think about how these contexts should interact with each other. Each restricted context outside should look like a black box. Implementation details should not matter if I am not part of this context. Interaction with a limited context occurs through well-defined interfaces, using carefully designed contracts. Once again - this is wonderfully combined with our experience in the real world. If I want to deal with an insurance company, then both parties, me and the insurance company, must agree on some contracts. The contracts describe our interaction in detail; there is no ambiguity in them.

After the contract has been created and signed by both parties, it is already difficult to change it: it is possible to do it, but you will have to make efforts. In the same way, we must think when defining interfaces and contracts for our limited contexts. Contracts are messages (commands and events) exchanged between contexts. This is where the name and contents of the contract matter. The name determines the context of the command or event, and the content determines the difference in data needed to transition from one state to another.

We must clearly distinguish between teams and events. A team always has only one goal. The goal of the team can either accept or reject the team. The result of executing a command is usually a change in the system. Something was done that changed the state of the model. On the other hand, an event can have from zero to several subscribers. Yes, it’s true that events can be ignored ... but events can never be rejected, as they tell us what has already happened. To clearly distinguish teams from events, we must use imperative names for teams; events should be called in the past tense. Names should use the single language we created.

What should we avoid in DDD


If we want to use domain-based design in development, we must avoid a few pitfalls.

Build an application around data only

In the past, most business applications have been data driven. First of all, architects and engineers created the data scheme, and everything else followed it. People said, "... at the end of the day, only the data makes sense ... The data we collect is our value. The applications that collect and / or use the data appear and disappear, but the data itself remains ...". And while the data remains, it’s just wrong to say that data is all that matters! Data alone does not make sense. Separately, the data is dead weight and only logic gives them meaning. And the same data has different meanings in different contexts. Therefore, we should always start with context and logic.

The view that data is the main value leads to the fact that databases are often used as an integration point. Many different applications use the same database. This leads to many problems in the long run. To give you a good analogy, of what I'm talking about, let's imagine that I have a wallet with money in my pocket. Now my friend has lost control and wants to borrow $ 10. How does our world work in this case? a) a friend politely asks me to borrow 10 bucks, and I open my wallet to give him money, or b) a friend takes my wallet out of my pocket (I don’t notice this) and takes a $ 10 bill out of it. Thus: never use a database as an integration point in business applications.

When I develop an application, ERD (ER data model - approx. Per.) Is one of the least important things for me. I do not allow myself and my domain model to be controlled by a data warehouse or data model. For me, a business application does not automatically mean that I should use a DBMS, and that my data model should be in 3rd normal form!

Talk about implementation details

The main idea of ​​DDD is not in entities, services, factory or repository application. These are all implementation details. They have no real meaning until we have done our homework and defined a single language, limited contexts, and also interfaces and contracts. If we begin to engage in implementation too soon, we will end up with an anemic model consisting of a collection of DTOs surrounded by a huge number of services and business logic spread everywhere.
Use technical noise

In our application, we should never use concepts or words like save, update, insert, delete, handle, manage. These are either very technical terms or abstract concepts that do not have a specific meaning. If I have a save, then one of the first things I do is remove the “Save” button from the user interface of the application. Saving is not a business concept. Imagine a world without computers - do real people use sayings like "... let me save this ..." (one exception is to save money in a bank account). In addition, in real business, we never use the words “Delete” or “Add” or “Update” to anything. Do we remove an employee when he is no longer required by the company?

Very general terms like “manager” should also be avoided. What does a manager do? What does it mean to control? No, no, please give us more context!

Db transactions

Although database transactions are a good thing, do not abuse them. Business transactions are much more important than database transactions. Despite the fact that, by definition, database transactions are completely consistent (and execute quickly), business transactions are not. An example of a business transaction that you are all likely familiar with is what happens at Starbucks when you order your favorite coffee in the morning. This is a “long” process with many possibly “conflicting” intermediate states and asynchronous tasks. However, it all works, scales, and is widely accepted by everyone.

Thus, when modeling your subject area using DDD, do not think about database transactions (or worse “distributed transactions”) at all. Think about the possible actions, their results, and the scenario for canceling the action in case of failure. And you will see that you will mainly solve business problems, and not deal with technical problems.

Conclusion


To successfully use DDD, you need to do several things very well from the very beginning: define a common vocabulary (single language), divide a common complex problem area into smaller sub-domains, called restricted contexts, and carefully determine the boundaries and contracts used between individual limited contexts. On the other hand, we must avoid certain practices that are too often used by software developers: build an application only around data while modeling a domain only around data (without logic); focusing on implementation details (entities, services, etc.) instead of the basic concepts discussed above; use of generalized and developer-specific terms and concepts when implementing the application; and finally

Also popular now: