This article was first published on short book of the floating purpose : https://www.jianshu.com/u/204b8aaab8ba
Version | date | Remark |
---|---|---|
1.0 | 2021.8.1 | First article |
1.1 | 2021.8.3 | Improved part description |
1 Introduction
Recently I found that the complexity of a project of the team is getting higher and higher (a typical three-tier structure), which is specifically expressed as:
- Poor code readability: complex calls between services and unclear processes
- Modifying a certain service business code causes a large number of test cases of irrelevant services to fail, and it is difficult for a single function developer to quickly locate related problems
- Test cases are particularly difficult to write, and a large amount of data is required to mock up the entire service
Based on these circumstances, I started looking for a solution to reduce complexity, so I have this article on DDD again.
1.1 Specific issues
1.1.1 Macro view
From a macro perspective, the evolution of the software architecture model has gone through three stages.
- The first stage is the stand-alone architecture: the process-oriented design method is adopted. The system includes the client UI layer and the database layer. The C/S architecture mode is adopted. The entire system is designed and developed around the database, and always starts from the design of the database and fields. .
- The second stage is a centralized architecture: an object-oriented design method is adopted. The system includes a business access layer, a business logic layer, and a database layer. The classic three-tier architecture is used, and some applications use the traditional SOA architecture. This architecture easily makes the system bloated, with poor scalability and elasticity.
- The third stage is the distributed microservice architecture: with the introduction of the microservice architecture concept, the centralized architecture is evolving to the distributed microservice architecture. The microservice architecture can well realize the decoupling between applications and solve the problem of insufficient scalability and elastic scalability of single applications. We know that in the era of stand-alone and centralized architecture, system analysis, design, and development are often carried out independently and in stages.
For example, in the process of system construction, we often see such a situation: A is responsible for proposing requirements, B is responsible for requirements analysis, C is responsible for system design, and D is responsible for code implementation. This process is very long, and there are many people who handle it. Easily lead to loss of information. Finally, it is easy to lead to inconsistencies between requirements, design and code implementation. Often after the software is online, we find that many functions are not what we want, or the functions we have made are too far from our own requirements.
Moreover, in the two modes of stand-alone and centralized architecture, the software cannot quickly respond to the rapid changes in demand and business, and ultimately missed opportunities for development. At this time, the emergence of distributed microservices is a bit just right.
The above part comes from geek time. It points out that DDD is generally used in microservice design and splitting, but I think it is also possible and recommended to split modules in a single application, which can make your modules It can be split out immediately when needed-into an independent microservice. Related can refer to [ZStack] 4. In-process service , this is an open source, and a good case implemented in production.
1.1.2 Microscopic view
This problem is very simple. The service code will inevitably become more and more piles, and more and more businesses will be gathered.
2. Getting started with DDD
Let's first look at a picture:
Start from the outermost layer-what is a domain? In the vernacular, it is the aggregation of a series of questions. for example:
In the e-commerce domain of the e-commerce platform, a series of problems you have to solve are:
- User Authentication
- Mobile payment
- Order
- Quotation
- ...
It can be seen that the domain presents a series of business domain problems.
In different domains, the abstract form of the same data entity is often different. For example, books in the Bookstore application focus on price in the sales field, inventory quantity in the warehousing field, and book introduction information in the merchandise display field.
2.1 Context boundary
Going inside, what we should see is the bounded context. In fact, this translation is not good. The original text is called bounded context
, and it is called context boundary more appropriate. Essentially, it defines the boundary. To be more specific, that is: used to encapsulate common language and domain objects, provide context, and ensure that some terms, business-related objects, etc. (universal language) in the domain have an exact meaning without ambiguity.
2.2 Aggregation
Next, we saw aggregation. Aggregation is a combination of entities and value objects closely related to business and logic. Aggregation is the basic unit of data modification and persistence. Each aggregation corresponds to a storage to realize data persistence.
An aggregation has an aggregation root and a context boundary. This boundary defines which entities and value objects should be included in the aggregation based on the single responsibility of the business and the principle of high cohesion, and the boundaries between aggregations are loosely coupled. Microservices designed in this way are naturally "high cohesion and low coupling".
What is the aggregate root?
The main purpose of the aggregation root is to avoid the problem of data inconsistency between aggregation and entities due to the lack of unified business rule control in the complex data model.
Every entity in the traditional data model is equivalent. If the entity is allowed to call and modify data uncontrollably, it is likely to cause inconsistencies in the data logic between the entities. However, if a lock is used, it will increase the complexity of the software and reduce the performance of the system.
If you compare aggregation to an organization, the root of the aggregation is the person in charge of the organization. The aggregate root is also called the root entity, which is not only an entity, but also the manager of the aggregate.
First of all, as the entity itself, it has the attributes and business behaviors of the entity, and realizes its own business logic.
Secondly, as the manager of the aggregation, it is responsible for coordinating entities and value objects within the aggregation to complete the common business logic in accordance with fixed business rules.
Finally, between the aggregations, it is also the external interface person of the aggregation, accepting external tasks and requests in the manner of the aggregation root ID association, and realizing the business collaboration between the aggregations within the context. In other words, aggregations are referenced by the aggregation root ID. If you need to access other aggregated entities, you must first visit the aggregation root, and then navigate to the internal entity of the aggregation. External objects cannot directly access the entity within the aggregation.
2.3 Entity and Value Object
There is such a type of object in DDD, which has a unique identifier, and the identifier can remain consistent after various state changes. For these objects, what is important is not their attributes, but their continuity and identification. The continuity and identification of the objects will span or even exceed the life cycle of the software. We call such an object entity . In fact, it is very similar to the rows of business data with constant id in the database.
The value object is relatively unimportant, because it is a set of attributes used to describe an entity. The implementation in many systems will be implemented in json, such as [ZStack] 7. Label system .
In order to facilitate understanding, here is a summary. The purpose of entities and value objects is to abstract and aggregate several attributes to simplify design and communication. With this level of abstraction, we will not have ambiguity when using personnel entities. When referring to address value objects, we do not need to list all of its attributes. In the same bounded context, greatly reducing misunderstandings and narrowing deviations. The difference between the two is as follows:
- Both are formed by attribute clustering, the entity has uniqueness, and the value object does not. In the bounded context of the case in this article, a person is unique. Once a person is managed by the system, it is given the ability to be uniquely identified in events, processes, and operations, and the value object does not and does not have to be unique.
- The entity focuses on uniqueness and continuity, and does not care about the change of attributes. When the attributes are all changed, it remains the original one; the value object emphasizes descriptiveness and is sensitive to the changes of attributes. When the attribute changes, it is not that it (meaning Because of immutability, it may be queried from outside).
- The strategic thinking framework is stable and unchanged, but the tactical model design is flexible and changeable. The entities and value objects may also change positions according to the different business concerns of the system. For example, if you change to a special bounding context, this context pays more attention to the address, and not so much to the people who have contact with this address, then the address should be designed as an entity, and people should be designed as value objects.
3. Getting started with DDD
3.1 From three-layer model to DDD
Here is a brief introduction to a change from the three-layer model to DDD.
It can be seen that the service is mainly split. Generally, it can be divided into three layers:
- Application service layer: multiple domain services or external application services are encapsulated, orchestrated, and combined to provide coarse-grained services to the outside. Application services mainly implement service composition and orchestration, and are a piece of independent business logic.
- Domain service layer: It is composed of multiple entities, and a method may be called across entities. When the code is too complex, you can split each domain service into a domain service class instead of putting all domain service codes into one domain service class.
- Entity: is a hyperemia model. The logic related to the same entity is implemented in the entity code.
3.2 Introduction to Modeling
We can use three steps to delineate the boundary between the domain model and microservices.
- The first step: sort out user operations, events and external dependencies in the business process in the event storm, sort out domain entities and other domain objects based on these elements.
- Step 2: According to the business relevance between the domain entities, the business closely related entities are combined to form an aggregation, and the aggregation root, value objects and entities in the aggregation are determined at the same time. In the figure in Chapter 2, the boundary between aggregates is the first level boundary. They run in the same microservice instance. This boundary is a logical boundary, so it is represented by a dashed line.
- The third step: According to factors such as business and semantic boundaries, delimit one or more aggregates within a bounded context to form a domain model. In the above figure, the boundary between bounded contexts is the second layer boundary. This layer of boundary may be the boundary of future microservices. The domain logic in different bounded contexts are isolated and run in different microservice instances. Physically They are separated from each other, so they are physical boundaries, and the boundaries are represented by solid lines.
3.3 Practice: Design a MiniStack
In order to make it easier for everyone to understand, I will design a very simple Iaas platform here and substitute the most basic DDD concepts in it.
3.3.1 Product Vision
- In order to: the internal developers, operation and maintenance personnel of the enterprise
- Theirs: computing, storage, network resource management
- This: MiniStack
- Is one: private cloud platform
- It can: manage computing, storage, network resource management, and help users create virtual machines simply and quickly
- Unlike: OpenStack
- Our products: simple, robust and intelligent
The string together is: in order to satisfy the enterprise's internal developers and operation and maintenance personnel, their hardware resource management, we built this MiniStack, it is a private cloud platform, it can manage computing, storage, network resource management, to help users simple Create virtual machines quickly. Unlike OpenStack, our products are simple, robust, and flexible.
3.3.2 Scenario analysis
Due to space reasons, let's talk about the most typical scenario-creating a virtual machine in order to sort out related domain models.
Here we need to pay attention that we should sort out the operations, commands, domain time, and dependency changes that occur in the entire system as much as possible.
3.3.2.1 Create a virtual machine
- User login system: verify the information from the database and complete the login authentication
Create a virtual machine: fill in the virtual machine name, cluster, computing specifications, L3 network, and mirror. If necessary (simple manifestation), you can specify the physical machine and network segment.
- The VM service needs to provide an interface to create a virtual machine
Submit to the MiniStack engine, and the engine starts to do related scheduling:
Find a low-load physical machine that meets computing and storage resources, and update the physical machine to which the vm belongs
- The physical machine service needs to provide a query interface
Allocate idle IPs in the L3 network and update vm-related network information
- Network service needs to provide IP distribution interface
Tell the physical machine agent: pull the image from the mirror server to the physical machine found in step 1
- The physical machine service needs to provide a pull mirror interface
Tell the physical machine agent to start the parameters and pull up the vm
- VM service needs to provide a startup interface
- The interface returns the creation success, the user can see the vm
But after the virtual machine is created, it's not the end. What if the physical machine is carsh someday? When will the CPU be full due to a strange process? Therefore, for our goal-intelligence, after creating vm, MiniStack collects a series of monitoring information every 5 minutes:
- Send a heartbeat packet to the physical machine agent to ensure that the physical machine is in normal state
- Send a heartbeat packet to the virtual machine agent and return: computing, storage, and network related status
3.3.3 Macro Design: Domain Modeling
In this step, we need to analyze the business and establish a domain model. The general steps are:
- Find domain objects such as domain entities and value objects
- Find out the aggregate root, and establish the aggregate based on the dependency relationship between the entity, value object and the aggregate root
- The third step is to define the bounding context based on factors such as business and semantic boundaries
3.3.3.1 Define entities
We can roughly find several entities:
virtual machine
- start up
- stop
Storage resources of the physical machine
- Inquire
- distribute
- freed
Computing resources of the physical machine
- Inquire
- distribute
- freed
L3 network
- Assign IP
Mirror server
- Query mirror
- Add mirror
- Publish mirror
3.3.3.2 Defining aggregation and bounding context
Before looking for aggregation, we must first find out the aggregation root. It can be divided into physical machines, networks, mirroring servers, and virtual machines. They are all independent contexts. If needed, they can be disassembled into microservices. If it is a monolithic application, it is recommended to use modular means for logical isolation.
3.3.4 Micro: Domain Objects and Code Structure Analysis
When we have completed the macroscopic modeling, we can start to do microscopic things: sort out the domain objects in the microservices, sort out the relationships between domain objects, determine their position in the code model and layered architecture, and establish the domain model The mapping relationship with the microservice model, and the dependency relationship between services.
Roughly, there are two steps for quantification:
- Analyze domain objects
- Design code structure
3.3.4.1 Analyze domain objects
At this step, we need to confirm:
- Layering of services
- What services consist of application services
- What entities and entity methods are included in the domain service
- Which entity is the aggregate root
- What are the attributes and methods of the entity
- Which objects are value objects
Since our use case is relatively simple, it is organized as follows:
Application Service:
- VM creation service: Responsible for creating VMs and scheduling a large number of underlying domain services
Domain services: VM services, physical machine services, network services, mirroring services
- VM Service: Manage the life cycle of VM, such as create, delete, start, stop, etc.
- Physical machine services: physical machine related services, such as add, delete, state change, heartbeat awareness, resource RUD, etc.
- Network services: network-related services, such as creating and deleting L2 and L3 networks, IP management, etc.
- Mirroring service: mirroring server related services, such as adding, deleting, changing state, adding mirroring, etc.
Entity: VM entity, physical machine entity, local storage entity (physical machine storage)
- VM entity: start, stop, etc.
- Physical machine entity: state change, heartbeat perception, etc.
- L3 entity: IP segment addition, deletion, IP allocation, release, etc.
- Local storage entity: storage occupation and release
- Mirror: Query the size of the mirror
Next, take a look at the objects in the aggregation. We identify the aggregation and the aggregation root:
- The aggregate root of the physical machine aggregation is the physical machine
- The aggregation root in network aggregation is the L2 network
- The aggregation root in the mirror aggregation is the mirror server
- The aggregate root in the virtual machine aggregation is the virtual machine entity
The above-mentioned entity properties and methods have been shown in the figure.
For value objects, please refer to [ZStack] 7. Label system . This design is used in real production.
3.3.4.2 Design code structure
When we have completed the analysis of the domain objects, we will begin to design the way in which each domain object is presented in the code model—that is, to establish the mapping relationship between the domain objects and the code objects. According to this mapping relationship, service personnel can quickly locate the code location where the business logic is located.
At a macro level, we can refer to the following hierarchical model:
For microscopic implementation, we can refer to COLA .
4. Summary
This article went through DDD with everyone, and designed a project "out of thin air" in the article. In fact, this project is not out of thin air. I referred to the open source project ZStack I participated in and simplified it. The project is currently running in the private cloud of a large number of enterprise users, and it has been iterated for more than 6 years. Therefore, both in terms of design and implementation, there is a certain reference experience.
For your convenience, I combined the examples in the article with the ZStack code to understand, I made a mapping here.
Of course, the content of this article can only be used as an introduction. The related concepts are not in-depth, such as: subdomain,
core domain,
general domain,
support domain, 1610b535533eeb domain events, etc.; for the actual combat article, only a relatively simple example is designed
. If I have the opportunity later, I will continue to dig into relevant directions.
4.1 Reference
Information about ZStack
- Although ZStack is a worthy reference project, its DDD design is not particularly obvious. Therefore, you can also refer to COLA
- "Domain Driven Design"
- Geek Time-DDD Practical Class
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。