πŸ“œ ⬆️ ⬇️

Developer Cookbook: Domain Driven Design Recipes (Part 2, Structure and Interaction)

ddd-header


Introduction


In the first article, we identified the scope of these practices, for which projects they can be applied, and for which they should not.


In this article I would like to make a brief overview of the basic principles of DDD, and also to share my personal experience of their application. More details will be given about communication and structural approaches with examples of their implementation.


In the following article I will paint the possible combinations of the applied design patterns with regard to their implementation, and ultimately I will give an example of a specific implementation of one small microservice.


DDD


Recall the definition given earlier:


Domain-based design (DDD, Domain-driven design) is an approach to developing software for complex needs satisfaction, by strongly linking the implementation with the main business models that are in constant development.

The reference book that describes the practice of building complex systems is the book Domain Driven Dedign (Big Blue Book) by Eric Evans. If you read any review article on this topic, you already know about it. By the time DDD is used in practice, you will have to read it. This is not the easiest book to read:


The canonical source for DDD is Eric Evans book. It is not a real investment.

Martin Fowler: January 15, 2014

If you scroll through the content of the book, it will seem to you not quite structured. But the map will help us.
DDD-map


The map highlighted the practices that we consider today.


The scope of the practices reviewed in the book is huge. The scope of practices that can be applied outside of this book is even greater. Before adopting at least some of them, set goals for yourself. Let me give you my example.



Common language


Software development rarely leads to the creation of something new, as a rule, it is a simulation of something existing.


Model - a representation of a real object, which includes only the necessary properties and functions.

We cannot create a software product that will cover the entire subject area. It is possible to reproduce only the part that will play the necessary functionality.


A good example of a model would be a topographic map. She is a terrain model. The map does not contain meadows of fields and rivers, it only reflects the location of real objects relative to each other.


To build a clear and clear model for all, you need to speak the same language. This tells us not only Eric Evans, but common sense. If programmers use their own terms, and business their own slang, then the former will simply not understand what needs to be done. The business in this case will not be able to realize the real value of the development of a particular 'feature'. How many times have you heard: "Yes, it's just add a button"?


Your goal as a system designer should be to get the maximum understanding of each other from the whole team. How to achieve this? Start talking. If people begin to communicate in any cramped group, they will have a generally accepted set of terms. In different companies, the process of introducing a common language is likely to be different. This can be a strong-willed decision, as well as a democratic procedure. The language can be designated explicitly, and it can be entered implicitly, in this case it is just started to speak it. A good way to introduce a common language will be common documentation.


How to maintain project documentation


  1. Any communication between business and development should improve your model.
  2. After the meeting, record the result in the form of documentation (Scrum artifact), and show this documentation to all participants of the development process.
  3. Use a single language in the documentation.
  4. The most important thing: do not waste time on documentation. You will still have to write code, and the documentation will be rewritten many times, spending resources is expensive. Instead of messing with the UML drawing application for a long time, use a tissue, pen, and camera on your phone.
  5. Documentation requires discipline, you can not write it from time to time.
  6. Separate documentation:
    • Comments in the code - describe incomprehensible moments directly in the code, leave #ODO: (remove when merging the code in the master). Express your opinion in the comments, for example, you have to use one or another crutch when working with legasy code.
    • Comments to the README.md project in the root directory of your project should contain technical information: how to run the project, how to run tests, etc. It is also a good idea to have a map, where you have all the projects, and on which servers they are running. Separately write down all accepted agreements.
    • And the most important is the knowledge base. The collection of documents describing business processes is the part of the documents that is available to you as well as to business.
  7. The main error of those who write documentation is redundancy. Do not try to cover everything and everyone, convey only the general meaning. Documentation should complement your project, but not replace it. Do not record all terms that have ambiguous meaning only. If the definition takes more than two sentences, this is a bad definition.

Example of documentation:


 #         . # :  : -    - email -  ## : ###        ,     email  ,    1  (      email  ). ###           .          email . ###  email  email     .    ,    email.   . ###       ,     ,    .   . ###      email,    ,       .   2 . ###                  .    ,     ,             . 

Note that in this example we did not specify an explicit dictionary, nevertheless, we fixed the concept User , Authorization , Registration . Writing such documentation will not take an expert more than 20 minutes.


For a person who is not an expert in the subject area, the process of writing documentation is perceived as something complicated. It is necessary to separate the collection of knowledge and the recording of collected knowledge. != + .


β€œWhat you call the universe,” argued the fourth, β€œis, in fact, a cluster of worlds that, like the skin of an onion, are one on top of another and are gradually separated from each other.

- Unusually clearly stated! - admired abderity. - Amazingly clear! β€œThey thought they understood the philosopher, because they knew very well what an onion was.”
')
History of Aberdites, Kristov Martin Wiland

Limited contexts and domains


Imagine that we are a designer of a progressive startup. We all love cold pizza, swearing with couriers and hours to fill out forms on the site. Therefore, we came up with a wonderful startup "Four turtles and one rat":



Let us recall the documentation we described in the previous chapter. There the term registration was used in our single dictionary. But in this project we have several:



Unified language belongs to a limited context. The domain of the above documentation is the 'Authorization System'. Let's try to highlight domains for our startup.


But before we get down to this, let's look at the terminology for a bit, what a domain is and what a limited context is.


Domain (Domain) - is the representation of the real business structure that solves a specific task.

For example: Logistics System, Payment System, Authorization System, Order Management System.


The domain is divided into subdomains, which describe smaller structures, for example: a basket of orders, a system for constructing routes.


Each domain has a limited area of ​​responsibility - limited functionality.


Bounded context - a set of domain restrictions that helps the domain focus only on one task for its best solution.

I like to represent this term as such an abstraction. A domain is a circle. The limited context is a circle.


Domain context


Even in DDD terminology, the core is distinguished.


Core (Core domain) - the most important domain that most succinctly characterizes your business.

So, the domains of the project "Four turtles and one rat":


Work with pizzeria (Pizzarias)


Context : b2b everything related to working with pizzerias


Subdomains :



Work with the client (Clients)


Context : b2c, everything related to working with pizza customers


Subdomains :



Work with couriers (Delivery system)


Context : b2e, all that relates to working with couriers


Subdomains :



Order system


Context : Core. Allows you to coordinate all individual domains, providing a full cycle from receiving the order to the delivery of pizza to the user. He is not a performer, but plays the role of a conductor.


Subdomains :



Billing system


Context : Contains all financial transactions. Provides interaction with the processing center.


Subdomains :



Statistics System


Context : The collection and processing (not issuing) of analytical information.


Subdomains :



Management System (Managment panel)


Context : Issuing analytical information. Management decisions toolkit.



On the basis of domains, let's draw up his map.


Domain map (Context map) - a graphical tool that allows you to describe the relationship between individual domains.

Context-map


The map shows the connections between domains. This map is very superficial, but the subject area has not been studied enough. This is the first draft, rewriting which you will get the expected result.


The most important thing in the map is that we see connections between domains. This structure fits very well on the microservice architecture:


The main principle of microservice architecture: weak connectivity and strong grip.

This principle is given in Sam Newman's book, Creating Microservices , this is the second book that you will have to read in order to begin the practical use of the approaches described in this article. What is meant: the domains should be loosely connected with each other, but closely linked inside.


The translation of these terms is taken from the official Russian translation and, perhaps, reflects the transmitted meaning poorly. In the original, the terms sound like: Low coupling (connectedness, gearing, grip, conjugacy), high cohesion (connectedness, strength).


The practice of implementing domain separation


I would like to share my personal experience - a set of informed decisions. I do not urge you to use these solutions. But they can be a good choice if you don’t know where to start. With personal experience, the toolkit will be customized to your needs.


The main principles that guided us:



How to implement domains?


It is very convenient to allocate domains as separate microservices.


Microservices is a separate application that implements the logic of one domain.

In DDD-development, limited context will serve as the principle of separating microservice into a separate application. This does not negate the technical principle of separation of services (if this is due to the need to ensure high performance). But the contextual principle will be dominant and mandatory.


How to highlight the connection between domains?


Links between domains are always APIs. It can be RESTful json api, gRPC, AMPQ. In this article we will not compare one protocol with another and highlight their advantages and disadvantages; each of them has its own scope. But nevertheless, we will stop on the general recommendations:


Be flexible in the choice of protocol and rigid in the uniformity of its implementation.


Choose a protocol for each pair of domains individually, do not try to use http everywhere, perhaps you will need asynchronous queues somewhere and the benefits of AMPQ will become obvious to you. Do not ignore this feature because you have RESTful everywhere.


On the other hand, if you implement RESTful json, use one data structuring standard. You can take ready for example jsonapi or openapi. If for some reason, ready-made solutions do not suit you and you feel that you can develop your own standard - describe and use it. But apply it everywhere, do not breed the "zoo" of standards. If you need to communicate with an external system, where they know nothing about your standards, write a microservice adapter.


Adapter


How to implement subdomains?


As separate modules inside microservice.


A module is an implementation of a subdomain, by putting the logic in a separate namespace (Namespace) within one microservice.

How does all this look? Let's look at an example. As we remember, we have a domain. Work with couriers (Delivery system) - this domain has three subdomains:



Imagine this all in the form of a folder structure:


 $ tree --dirsfirst delivery_system delivery_system β”œβ”€β”€ app/ β”‚ β”œβ”€β”€ health_checker/ β”‚ β”‚ └── endpoints.rb β”‚ β”œβ”€β”€ registrations/ β”‚ β”‚ β”œβ”€β”€ entities/ β”‚ β”‚ β”œβ”€β”€ forms/ β”‚ β”‚ β”œβ”€β”€ repositories/ β”‚ β”‚ β”œβ”€β”€ interactor/ β”‚ β”‚ β”œβ”€β”€ services/ β”‚ β”‚ β”œβ”€β”€ validations/ β”‚ β”‚ β”œβ”€β”€ endpoints.rb β”‚ β”‚ └── helpers.rb β”‚ β”œβ”€β”€ tasks β”‚ β”‚ β”œβ”€β”€ entities/ β”‚ β”‚ β”œβ”€β”€ queries/ β”‚ β”‚ β”œβ”€β”€ repositories/ β”‚ β”‚ β”œβ”€β”€ endpoints.rb β”‚ β”‚ └── helpers.rb β”‚ └── withdrawals β”‚ β”œβ”€β”€ entities/ β”‚ β”œβ”€β”€ forms/ β”‚ β”œβ”€β”€ repositories/ β”‚ β”œβ”€β”€ interactor/ β”‚ β”œβ”€β”€ services/ β”‚ β”œβ”€β”€ validations/ β”‚ β”œβ”€β”€ endpoints.rb β”‚ └── helpers.rb β”œβ”€β”€ config/ β”œβ”€β”€ db/ β”œβ”€β”€ docs/ β”œβ”€β”€ lib/ β”‚ β”œβ”€β”€ schemas/ β”‚ └── values/ β”œβ”€β”€ public β”œβ”€β”€ specs β”œβ”€β”€ config.ru β”œβ”€β”€ Gemfile β”œβ”€β”€ Gemfile.lock β”œβ”€β”€ Rakefile └── README.md 

Each folder in the apps/ directory implements a particular subdomain, within each domain there are different patterns: entities , forms , services , etc. We will look at each of the applied patterns in detail in one of our future articles.


Each such pattern is implemented in the corresponding namespace (Namespace). For example, the form of creating wigs for payment to the courier:


 module Withdrawal #   module Forms #  class Create end end end 

How to implement links between subdomains?


Let's look at a specific example. We have a courier account: Registrations::Entities::Account . It relates to the Registrations subdomain - since we do not consider this domain as a registration process, but rather as an account table and registration book, as indicated in our documentation for the business.


We have two processes in the execution of which we access this account.



As we see these two processes belong to different subdomains - Registration and Wihtdrawal.


 module Registrations module Serivices class CreateAccount def call account = Entities::Account.new end end end end module Withdrwals module Serivices class CreateOrder def call account = Registrations::Entities::Account.new end end end end 

In the first call, an appeal to the class will be implemented through a call to Entities::Account . And in the second case, through an explicit call to Registrations::Entities::Account . Those. if we explicitly specify a subdomain, then a class from another subdomain, and so we clearly designate the connection.


If a class does not explicitly refer to any of the subdomains, it makes sense to put it in the lib/ folder. As a rule, these are classes that implement the 'ValueObject' pattern. We will look at this pattern in more detail in one of the following articles.


Implementation through the model.


To quote Eric Evans:


If the architecture of the program, or at least some of its central part, does not correspond to the structure of the domain model, then such a model is practically useless, and the correctness of the program’s work must also be put under suspicion. At the same time, the too complex interrelationships between models and functions in a software architecture are difficult to understand, and in practice they are difficult to maintain as the architecture changes.

Let us recall an example of a good model that I already cited at the beginning of this article - a topographic map. Our goal is to be able to quickly find the distance between two settlements. We could use a reference table with two points between cities. And we can use the card. Both here and there we will get the same result at about the same time. But the map is more compact, it more accurately displays the subject area, it is more universal. Map as a model is incredibly expressive. And if we consider it within the framework of this task, then it is more convenient to measure the distance on the map than on the territory itself, which it reflects. A model that reflects the subject area may exceed it in some properties. This is really amazing.


The implementation of the model is always a creative process with an unpredictable result. The quality of your code is not its performance, and not its complexity, it is simplicity and expressiveness. Improve it through constant refactoring, make it flexible and cut off all unnecessary. Separate the layer that will be responsible for the business logic of the model from the layers whose need is due to the technical implementation. How we managed to do this will be discussed later.




Source: https://habr.com/ru/post/428209/


All Articles