14 | Code Model (Part 2): How to ensure the consistency between domain model and code model?

In the previous article, we learned how to use event storms to build domain models. In the process of building domain models, we will extract many domain objects, such as aggregates, entities, commands, and domain events. According to the DDD layered architecture model, a standard microservice code model is established, and the layered and directory structures are defined for code objects.

In order to complete the design and implementation of microservices, there is actually another step after that, which is to map domain objects to the microservice code model. So why is this step so important?

DDD emphasizes building the domain model first and then designing microservices to ensure the integrity of the domain model and microservices. Therefore, we cannot talk about the design and implementation of microservices without the domain model. But when building a domain model, we often stand from a business perspective, and some domain objects also carry business language. We also need to use the domain model as the input of microservice design, design and convert domain objects, and establish a mapping relationship between domain objects and code objects.

Collation of Domain Objects

After the microservice split is completed, the boundaries of the domain model and domain objects are basically determined.

Our first important job is to sort out various domain objects generated during the event storm, such as aggregation, entity, command, and domain events, and record these domain objects and business behaviors in the table below.

As you can see, this table contains four dimensions: domain model, aggregation, domain object and domain type. A domain model will contain multiple aggregates, and an aggregate will contain multiple domain objects, each domain object has its own domain type. The domain type mainly identifies the attributes of domain objects, such as: aggregate root, entity, command, and domain event.

From domain model to microservice design

From the domain model to the implementation of microservices, we still need to do further design and analysis. The domain objects extracted from the event storm need to be analyzed by user stories or domain stories, as well as microservice design, before they can be used in the development of microservice systems.

This process will be more in-depth and detailed than the event storm. The main concerns are as follows:

  • Analyze what services are inside a microservice?
  • What layer does the service reside in?
  • What services are composed and orchestrated by application services?
  • Domain services include the business logic of which entities?
  • What are the properties and methods of entities that adopt the congestion model?
  • What value objects are there?
  • Which entity is the aggregate root etc.?
  • Finally, sort out all domain objects and their dependencies, we will design corresponding code objects for each domain object, and define their software packages and code directories.

Suggested roles for this design process are: DDD Specialist, Architect, Designer, and Development Manager.

domain object

At the end of the event storm, there will generally be domain objects in the domain model aggregate: aggregates, entities, commands, and domain events. After the story analysis and microservice design are completed, there are generally domain objects in the aggregation of microservices: aggregation, aggregation root, entity, value object, domain event, domain service, and storage.

Let's take a look at how these domain objects are obtained?

1. Design entity

In most cases, there is a one-to-one correspondence between the business entities of the domain model and the database entities of the microservices. However, some domain model entities may be designed as multiple data entities during microservice design, or some attributes of entities may be designed as value objects.

When we analyze individual customers, we also need entities such as addresses, phone numbers, and bank account numbers. They are referenced by aggregate roots and are not easy to find during domain modeling. We need to identify and design them during the microservice design process.

In the layered architecture, the entity adopts the congestion model, and all the business logic of the entity is implemented in the entity class. These different entities have their own methods and business behaviors. For example, the address entity has methods for adding and modifying addresses, and the bank account entity has methods for adding and modifying bank account numbers.

Entity classes are placed under the Entity directory structure of the domain layer. 

2. Find the aggregate root

The aggregate root comes from the domain model. In the individual customer aggregation, the individual customer entity is the aggregate root, which is responsible for managing the life cycle of the address, phone number, and bank account number. The aggregate root of individual customers implements the initialization and persistence of entity and value object data such as address and bank account number in the aggregate through the factory and storage modes.

An aggregate root is a special kind of entity with its own properties and methods. Aggregate roots can implement object references between aggregates, and can also refer to all entities within an aggregate. Aggregate root classes are placed under the Entity directory structure of the code model. The aggregate root has its own implementation methods, such as generating customer codes, adding and modifying customer information, and other methods.

3. Design value objects

Design certain properties or property sets of certain entities as value objects as needed. Value object classes are placed under the Entity directory structure of the code model. In the individual customer aggregation, the customer has a customer certificate type, which exists in the form of an enumeration value, so it is designed as a value object.

Some domain objects can be designed as value objects or entities, and we need to analyze them according to specific situations. If this domain object maintains the life cycle in other aggregates and only allows overall replacement in the entity object it is attached to, we can design it as a value object. If this object has multiple items and query statistics need to be based on it, I suggest designing it as an entity.

4. Design Field Events

If domain events in the domain model will trigger the next business operation, we need to design domain events. First determine whether domain events occur within microservices or between microservices. Then design the event entity object, the publishing and subscribing mechanism of the event, and the processing mechanism of the event. Determine whether to introduce event bus or message middleware.

In the Person Customer aggregate there is the domain event that the customer has been created, so it has the entity Customer Created Event.

Domain event entities and processing classes are placed under the Event directory structure of the domain layer. I suggest that the publishing and subscribing classes of domain events be placed under the Event directory structure of the application layer.

5. Services in the field of design

If a business action or behavior spans multiple entities, we need to design domain services. Domain services complete core business logic by combining multiple entities and entity methods. You can think of domain services as a layer of business logic above entity methods and below application services.

According to the dependencies of the strict layered architecture layer, if the method of the entity needs to be exposed to the application layer, it needs to be encapsulated into a domain service before it can be called by the application service. So if some entity method needs to be called by the front-end application, we will encapsulate it into a domain service, and then encapsulate it into an application service.

The method of creating personal customer information by the individual customer aggregate root entity is encapsulated as creating a personal customer information domain service. Then it is packaged to create personal customer information application services, which are exposed to front-end applications.

The domain service class is placed under the Service directory structure of the domain layer.

6. Design storage

Each aggregation has a warehouse, which is mainly used to complete data query and persistence operations. Warehouse includes warehouse interface and warehouse implementation, and realizes the decoupling of application business logic and database resource logic through dependency inversion.

The storage code is placed under the Repository directory structure of the domain layer.

Domain Objects in the Application Layer

The main domain objects of the application layer are the publication and subscription of application services and events.

During event storm or domain story analysis, we often design services or entity methods based on commands initiated by users or systems. In response to this command, we need to parse and log:

  • What business behaviors will occur at the application layer and domain layer;
  • Which services or methods need to be designed for each layer;
  • The layering of these methods and services and domain types (such as entity methods, domain services and application services, etc.), the calls and composition dependencies between them.

In the strict layered architecture mode, cross-layer calls of services are not allowed, and each service can only call its next layer service. The services from bottom to top are: entity method, domain service and application service. What should we do if we need to implement cross-layer invocation of services? I suggest that you adopt the service layer-by-layer encapsulation approach.

Let's take a look at the picture above. There are mainly the following ways to encapsulate and call services.

1. Encapsulation of entity methods

Entity methods are the bottom-level atomic business logic. If the method of a single entity needs to be called across layers, you can encapsulate it into a domain service, so that the encapsulated domain service can be called and orchestrated by the application service. If it still needs to be called by the user interface layer, you also need to encapsulate this domain service as an application service. After layer-by-layer service encapsulation, entity methods can be exposed to different layers above to achieve cross-layer calls.

When encapsulating, the name in front of the service can be kept the same, and you can use *DomainService or *AppService suffix to distinguish domain service or application service.

2. Combination and encapsulation of domain services

Domain services combine and orchestrate multiple entities and entity methods for application service calls. If it needs to be exposed to the user interface layer, the domain service needs to be encapsulated as an application service.

3. Composition and orchestration of application services

Application services combine and orchestrate services in multiple domains, and expose them to the user interface layer for front-end application calls.

When composing and orchestrating application services, you need to pay attention to a phenomenon: multiple application services may repeatedly combine and orchestrate the same business logic for multiple services in the same domain. When this happens, you need to analyze whether domain services can be integrated. You can combine these domain services that are repeatedly combined into one domain service for implementation. This not only saves the repeated orchestration of application services, but also realizes the evolution of services. In this way, the domain model will become more and more refined and better adapt to business requirements.

The application service class is placed under the Service directory structure of the application layer. The publishing and subscribing classes of domain events are placed under the Event directory structure of the application layer.

Mapping between domain objects and microservice code objects

After completing the above analysis and design, we can establish the mapping relationship between domain objects and microservice code objects as shown in the figure below.

typical domain model

The personal customer aggregation in the personal customer domain model is a typical domain model, from which multiple entities and value objects and its aggregate root can be extracted. Let's take a look at the picture below. We have further analyzed the aggregation of individual customers. The aggregate root of the personal customer form is extracted to form customer type value objects, as well as entities such as phone numbers, addresses, and bank account numbers. Encapsulation and layering are performed for entity methods and services, and domain object associations and dependencies are established. Storage and other design. The key is this process, we have established the mapping relationship between domain objects and microservice code objects. 

Below I make a brief description of each column of the table.

  • Layer: Defines which layer the domain object is located in the layered architecture, such as: interface layer, application layer, domain layer, and base layer.
  • Domain object: the specific name of the domain object in the domain model.
  • Domain type: The type of domain object defined according to the DDD knowledge system, including: bounded context, aggregation, aggregate root, entity, value object, domain event, application service, domain service and storage service and other domain types.
  • Dependent domain objects: According to the dependencies of business objects or hierarchical calls, the dependencies of domain objects are established, such as: service call dependencies, associated object aggregation, etc.
  • Package name: The package name in the code model, which corresponds to the package where the domain object is located.
  • Class name: The class name in the code model, which corresponds to the class name of the domain object.
  • Method name: The method name in the code model, which corresponds to the method name of the domain object implementation or operation.

After establishing this mapping relationship, we can get the microservice code structure as shown in the figure below. 

atypical domain model

Some business scenarios may not be as you wish, and you may not be able to design a typical domain model. There are multiple entities in this type of business, and the entities are independent of each other and are in a loosely coupled relationship. These entities are mainly involved in analysis or calculation. You cannot find the aggregate root, but they are highly cohesive in terms of the business itself. And the business and other aggregates they combine are in a bounded context, and it is unlikely that you will design it as a microservice alone.

This kind of business scenario is actually very common. For example, there is an aggregation of customer merging in the personal customer domain model, which scans all customers, judges whether they are duplicate customers according to business rules such as ID number and phone number, and then merges the duplicate customers.

You can't find the aggregate root in this business scenario.

So what do we do with such atypical models? We can still learn from the idea of ​​aggregation, still use aggregation to define this part of the function, and use the same analysis method as the typical domain model to establish the attributes and methods of entities, encapsulate and hierarchically design methods and services, design storage, and establish Dependencies between domain objects. The only pity is that we still can't find the aggregate root, but it doesn't matter, in addition to the aggregate root management function, we can also use other design methods of DDD.

Summarize

This article talks about the design process from domain model to microservice, which is very critical in the microservice design process. From the perspective of the microservice system, you need to do an in-depth and detailed analysis of the domain model, layer the domain objects, find out the dependencies of each domain object, and establish the mapping relationship between the domain object and the microservice code object, so as to ensure the domain model The consistency with the code model finally completes the design of microservices.

After establishing the relationship between the business model and the microservice system architecture, the entire project team can work under a unified common language. Even developers who are not familiar with business or business personnel who are not familiar with code can quickly locate to the code location. 

Guess you like

Origin blog.csdn.net/zgz15515397650/article/details/131499623