Introduction
Too many times I have met applications that were said to have a domain model and the application was designed based on this domain. However, in reality, all I saw was a collection of entities (I would even say DTO), having 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 (like NServiceBus, Mass Transit Bus or Azure Bus), then of course it is noticeable that certain messages are transmitted from one module to another or several modules. Unfortunately, messages often have very generalized names containing the words “update”, “change”, “add” or “delete”, and carry a large amount of payload - dozens of different properties. Often, it is not at all obvious from the title of the message whether it is a team or an event, and in order to determine this, you have to dig deep into the implementation.
I sincerely wish that everything written above would be an exaggeration or it would make sense only for “old” applications that have grown and got out of control. But the sad truth is that this applies to many new projects, even those that are just 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 the following: 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 domain-based design. The last few chapters of the book contain very interesting things, but many developers do not read this 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 to stakeholders and experts in the field (yes, they are often representatives of the client for whom we are creating software) and try to understand their language. We need to listen carefully to what they say and how they speak. Do they use certain words that are understood and used in the same way by everyone working in this subject area? Is the meaning of these words unambiguous? If not, then we must ask questions and demand from experts a more detailed description of their terminology, or a change of wording, or even use certain analogies. One of my favorite “games” is to ask experts how they would do their tasks in the absence of a computer. What will be the actions, and what 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 everyone understands: stakeholders, 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 evolve throughout the project’s life as we gain a deeper and deeper understanding of the subject area, learning all its subtleties and bottlenecks.
Sharing a difficult problem
After we have found or defined a single language of the domain, 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 difficult enough to understand everything at once and / or 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 when eating - 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 each 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 individually, in order to solve them separately. We call these sub-domains
bounded contexts . Limited context is a part of the entire subject area that has clear boundaries, interacts through well-defined interfaces with other limited contexts, and which can be determined individually.
Defining 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. Every limited 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, myself and the insurance company, must agree on some contracts. Contracts describe our interaction in detail, there is no ambiguity in them.
After the contract is created and signed by both parties, it is already hard to change: it can be done, but it will take an effort. Similarly, we need to think in defining interfaces and contracts for our limited contexts. Contracts are messages (commands and events) that contexts exchange. This is where the name and the contents of the contract matter. The name defines the context of the command or event, and the content is the difference in the data needed to transition from one state to another.
We must clearly distinguish between teams and events. A team always has only one goal. A team goal can either accept or reject a team. The result of the command, as a rule, is 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 is true that events can be ignored ... but events can never be rejected, as they tell us what has already happened. To clearly distinguish between teams and events, we must use imperative names for commands; events must be called in the past tense. Names must use the single language we created.
What we should avoid in DDD
If we want to use domain-based design in our development, we must avoid several traps.
Build the app just around the data.
In the past, most business applications were data oriented. First of all, architects and engineers created a data scheme, and everything else followed it. People said "... at the end of the day, only data makes sense ... The data we collect is our value. Applications that collect and / or use 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! The data itself does not make sense. Separately, the data is a dead weight, and only logic gives them meaning. Both the same data have different meanings in different contexts. Thus, we must always begin 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 money 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 transfer money to him or b) a friend takes my wallet out of my pocket (I don’t notice it) and takes a $ 10 bill out of it. Thus: never use the database as an integration point in business applications.
When developing an application, ERD is one of the least important things for me. I do not allow myself and the model of my subject area to be controlled by the data warehouse or data model. For me, a business application does not automatically mean that I have to use a DBMS, and that my data model must be in 3rd normal form!
Talk about implementation details
The main idea of ​​DDD is not in entities, services, application of a factory or repository. These are all implementation details. They have no real meaning until we have done our homework and have defined a single language, limited contexts, as well as interfaces and contracts. If we start implementing it too soon, we end up with an anemic model consisting of a collection of DTOs, surrounded by a huge number of services and business logic spread all over.
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 have no 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. Preservation is not a business concept. Imagine a world without computers - do real people use statements like "... let me save it ..." (one exception is saving money in a bank account). In addition, in the real business, we never apply 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” must also be avoided. What does the manager do? What does "control" mean? No, no, please give us more context!
DB Transactions
Although DB transactions are a good thing, they should not be abused. Business transactions are much more important than DB transactions. Despite the fact that, by definition, database transactions are completely complete (and run quickly), business transactions are not. An example of a business transaction that you are all probably familiar with is what happens at Starbucks when you order your favorite coffee in the morning. This is a “long” process with many possibly “contradictory” intermediate states and asynchronous tasks. However, it all works, scales and is widely accepted by all.
Thus, when modeling your subject area with DDD, do not think about database transactions (or worse, “distributed transactions”) in general. Think about the possible actions, their results and the cancellation scenario in case of failure. And you will see that you will solve mainly business problems, and not to deal with technical problems.
Conclusion
In order to successfully use DDD, you need to do several things very well from the very beginning: define a common vocabulary (common language), divide the common complex problem area into smaller sub domains called limited contexts, and carefully define the boundaries and contracts used between separate 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 the subject area only around data (without logic); focusing on implementation details (entities, services, etc.) instead of the basic concepts discussed above; use of generic and developer-specific terms and concepts when implementing an application; and, finally, reassessing the value of database transactions instead of focusing on business processes or transactions.