This is the fourth article in the “Domain-driven Design Practice Path” series, introducing microservices from the disadvantages of single architecture, combining domain-driven concepts to introduce how to divide microservices, design domain models, and show the overall system architecture design of microservices. Combining the ideas of hierarchical architecture, hexagonal architecture and clean architecture, this paper shows the program structure design of a microservice against the background of actual application scenarios.

First, the disadvantages of monomer architecture

Example of single structure (quoted from Internet)

Generally, in the early stage of service development, the entire application has few functional requirements and is relatively simple. Single-architecture applications are easy to deploy, test, and implement horizontal expansion.

However, as the requirements increased and more people joined the development team, the code base expanded rapidly. As a result, individual applications become more and more bulky, with lower maintainability and flexibility, and higher maintenance costs.

The following are some disadvantages of single architecture applications:

1. High complexity

At the beginning of the project, someone should be able to know everything about the functionality and implementation of the application. As business requirements increased and various business processes were intertwined, the whole system became so large and complex that few developers knew every detail of each function and business process.

This can take a lot of time to assess the requirements of new business or locate abnormal problems, and it also contains unknown risks. Worse, this extreme complexity can create a vicious cycle in which each change makes the system more complex and difficult to understand.

2. Lots of technical debt

Over time, requirements change, and people change, an application’s technical debt builds up and builds up. For example, teams must use the same technology stack for a long time, making it difficult to adopt new frameworks and programming languages. Sometimes when you want to introduce some new tools, you need to maintain multiple technical frameworks in the project, such as Hibernate and Mybatis, which makes the cost high.

3. Errors are difficult to isolate

Since all functional modules of a business project are undertaken by an application, including core and non-core modules, any module or a small detail may cause the collapse of application instances due to unreasonable design, poor code quality and other reasons, thus affecting the overall business. The root cause is that code for both core and non-core functions runs in the same environment.

4. High cost of collaboration between project teams and slower and slower business response

There are bound to be similar functional modules between multiple similar business projects. If they all adopt the single mode, it will bring repeated function construction and maintenance. In addition, sometimes interaction is required, and the cost of integration and collaboration between individual systems is additional.

Moreover, when the project is large to a certain extent, different modules may be maintained by different teams, and the conflicts of iterative coordination and code merging branches will affect the whole development progress, thus making the business response speed slower and slower.

5. High expansion costs

Along with the development of the business, business processing in the system bottlenecks, tend to be due to one or several function modules of high load, but because all functions are packaged together, when such problems, only by increasing the application examples of ways to share the load, no way to separate the expansion of the service ability of several functional modules, Thus, additional resources are consumed and the cost is high.

In view of the above pain points, in recent years, more and more Internet companies have adopted the “micro-service” architecture to build their own business platforms, and “micro-service” has been recognized by more and more technical personnel.

Microservices are an evolving form of SOA and are not fundamentally different from SOA methods and principles. The core value of SOA philosophy is that loosely coupled services bring business reuse, and micro-services are divided according to business dimension rather than technology dimension, combined with the principle of high cohesion and low coupling, which is exactly in line with the concept advocated by domain-driven design.

Second, micro-service design

1. Micro-service division

Broadly speaking, a domain is what an organization does and all that it contains. Each organization has its own scope of business and way of doing things. This scope of business and the activities carried out within it are domains.

DDD’s concept of subdomains and bounded contexts is a good match for services in microservices architecture. Also, the concept of autonomous teams responsible for service development in the microservices architecture is consistent with the DDD concept of an independent team responsible for development of each domain model. DDD advocates the division of the system by business domain, while microservices architecture emphasizes the division and conquer from the business dimension to cope with system complexity. The architecture designed by skipping business architecture is not focused on business response, and may be a big mud ball, which is painful when facing requirements iteration or responding to market changes.

The core appeal of DDD is to map the business architecture to the system architecture, and change the system architecture as the business architecture is adjusted in response to business changes. While microservices pursue reuse at the business level, the system architecture designed is consistent with business; In terms of technical architecture, the system modules are fully decoupled, and the appropriate technical architecture can be freely selected to decentralize the governance of technology and data.

Taking the resource ordering system of e-commerce as an example, typical service case scenarios include viewing resources, purchasing resources, and querying purchased resources.

Every sub-domain definition domain driven to separate the domain model, child domain is part of the field, from the perspective of the business, we need to cover the business case scenario, with the idea of high cohesion and low coupling, combined with the single responsibility principle (SRP) and the principle of closure (CCP), from the perspective of business area, divided into subdomains user management, resources management domain, The order subdomain and the payment subdomain total four subdomains.

Each subfield corresponds to a bounding context. A bounded context is a conceptual boundary in which the domain model works, and each bounded context has its own common language. A bounded context allows you to place an explicit, clear boundary around the domain model. Of course, bounded contexts encompass more than just the domain model. When using the microservice architecture, there is one microservice for each bounding context.

2. Domain model

An aggregation is a cluster of domain objects within boundaries, which can be thought of as a unit consisting of a root entity and possibly one or more other entity and value objects. Aggregations break the domain model into chunks, each of which can be treated as a unit.

The aggregate root is the only part of the aggregate that can be referenced by an external class, and the client can update the aggregate only by calling methods on the aggregate root.

An aggregation represents a consistent boundary, and for a well-designed aggregation, all the invariants in the aggregation are consistent within a single transaction, regardless of what business requirements change. An important empirical design principle for aggregation is that only one instance of aggregation is modified in a transaction. When you update an aggregate, you need to update the entire aggregate, not just a part of it, otherwise consistency problems can occur.

Such as A and B to buy things on the Internet at the same time, using the same order, at the same time to realize what they buy more than the budget, A reduced number of dim sum, B reduce the amount of bread, two concurrent execution consumer affairs, so the total order limit may be less than the minimum order requirements, but for A consumer is to meet minimum requirements. Therefore, update should be performed from the perspective of the aggregation root, which enforces consistent business rules.

In addition, we should not be too much design of polymerization, processing large aggregate constitute the “big MAC” objects, it’s easy to have a different use cases need to modify one part at the same time, because of the aggregation design when considering the consistency of constraint is for the whole aggregation effect, so the aggregate changes can cause changes of the whole aggregation, if use optimistic concurrency, This can lead to scenarios where some use cases are rejected, and it can affect the performance and scalability of the system.

With large aggregations, hundreds or thousands of objects are often loaded into memory to complete a basic operation, resulting in a waste of resources. Small aggregations should be used as much as possible, on the one hand representing aggregations with root entities containing only the minimum number of attributes or value type attributes, where the minimum number represents the minimum set of attributes required, no more, no less. Properties that must be consistent with other properties are required properties.

In an aggregation, if you think that some of the included parts should be modeled as an entity, think about whether that part will change over time, or whether that part can be replaced entirely. If you can replace them all, you can model them as value objects rather than entities. Value objects are generally preferred because they are immutable and can only be replaced completely, making them safer to use. In many cases, many concepts modeled as entities can be reconstructed as value objects. Small aggregation also contributes to the successful execution of transactions, that is, it reduces transaction commit conflicts, which not only improves system performance and scalability, but also improves system availability.

In addition, aggregate direct references are implemented through unique identifiers rather than object references, which not only reduces the usage space of the aggregate, but more importantly, enables the direct loose coupling of the aggregate. If the aggregation is part of another service, there is no problem with object references across services, although objects can refer to each other within the aggregation.

The main usage principles for aggregation can be summarized as follows:

  1. Only the aggregate root is referenced.
  2. Reference other aggregations by unique identity.
  3. Only one aggregation can be created or modified in a transaction.
  4. Use final consistency outside the aggregate boundary.

, of course, in the process of actual use, such as a business use case need to get to the aggregation of a domain object, but the object in the field of access path was complicated, to compatible with the special scene, can be aggregated in the attribute or value object (entity) directly returned to the application layer, application layer directly makes the operating objects in the area.

It is often the case that executing command methods on one aggregation requires additional business rules on other aggregations, and try to use final consistency because final consistency can improve system throughput by taking steps along the aggregation dimension. For a business use case, if data consistency should be guaranteed by the user executing the use case, then transactional consistency can be considered, although other aggregation principles need to be followed. If other users or systems are required to ensure data consistency, use final consistency. In fact, the final consistency can support most business scenarios.

Ordering system based on the face of electricity resources business sub-domains division, design resource polymerization, polymerization of orders, the payment of polymerization and user polymerization, resource aggregation and orders through the resource ID connection between aggregation, order polymerization and payment between aggregate associated by order ID and user ID, pay polymerization and user polymerization between the associated with the user ID. The resource aggregation root contains multiple resource bundle value objects, and one resource bundle value object contains multiple preview graph value objects. Of course, in the actual development process, the aggregation root can also contain entity objects according to the actual situation. Each aggregation corresponds to one microservice, and for particularly complex systems, a subdomain may contain multiple aggregations and therefore microservices.

3. Design of microservice system architecture

Based on the analysis of the subdomain of the resource ordering system of e-commerce, the server background uses four micro-services, namely user service, resource service, order service and payment service. The API Gateway in the figure above is also a service and can be viewed as an application layer in DDD, similar to the Facade pattern in object-oriented design.

As a unified facade of the entire back-end architecture, encapsulating the internal architecture of the application, it is responsible for the task coordination of business use cases, each corresponding to a service method that calls multiple microservices and returns the aggregated results to the client. It may also have other responsibilities, such as authentication, access authorization, caching, rate limiting, and so on. For example, to query purchased resources, the API Gateway needs to query the order service to obtain the resource IDS list purchased by the current user, then query the resource service to obtain the details about purchased resources based on the resource IDS list, and finally return the aggregation result to the client.

Of course, in the actual application process, we can also divide API Gateway into multiple different services according to the complexity of API requests from the business perspective, to prevent the return to the single BOTTLENECK of API Gateway.

In addition, sometimes some of the subdomains are small from a business domain perspective and are a little thin to fit into a microservice from a resource utilization perspective. At this time, we can break the concept that one limited context corresponds to one microservice, and merge multiple subdomains into the same microservice, and realize the coordination of multi-subdomain tasks by the application layer of the microservice itself.

Therefore, in our system architecture, there may be scenarios of small application layer at micro service level and large application layer at API Gateway level. Theory is theory, but flexible application should be combined with actual situation.

Application of domain-driven concepts in the design of individual microservices

1. Analysis of architecture selection

Layered Architecture Diagram (cited from Internet)

Hexagonal architecture diagram (quoted from Internet)

Clean Architecture Diagram (quoted from Internet)

The concentric circles in the neat architecture diagram above represent different layers in a software system, usually higher the closer to the center they are.

The dependency rules of clean architecture tell us that dependencies in source code must only point to the inner layers of concentric circles, that is, from low-level mechanisms to high-level policies. In other words, any code belonging to the inner circle should not refer to the code in the outer circle. In particular, the code in the inner circle should not refer to the names declared by the code in the outer circle, including functions, classes, variables, and any other named software entities. Similarly, the data format used by the outer circle should not be used by code in the inner circle, especially if the data format is generated by the framework of the outer circle.

In summary, you should not let any changes that occur in the outer circle affect the code in the inner circle. Business entities in this layer encapsulation is the most common in the area of the entire business, at the highest level of business logic, they should belong to the system the most part is not susceptible to outside influence and change, that is usually the core of our domain model part is relatively stable, should not because the outer layer of infrastructure such as the change of the data storage technology selection, Or changes in the way the UI is presented, etc., are affected and need to be changed accordingly.

In previous project experience, most students are familiar with layered architecture, which generally includes display layer, application layer, domain layer and infrastructure layer. An important benefit of the hexagonal architecture is that it separates the business logic from the presentation and data access layer logic contained in the adapter. The business logic does not depend on the presentation or data access layer logic, and because of this separation, it is much easier to test the business logic separately.

Another benefit is that the business logic can be invoked through multiple adapters, each implementing a specific API or user interface. The business logic can also invoke multiple adapters, each calling a different external system. So a hexagonal architecture is a good way to describe the architecture of each service in a microservice architecture.

According to our practical experience, for example, MySQL and Redis are the most common storage structures in our daily projects, and they are rarely changed to other storage structures. Here, hierarchical architecture and hexagonal architecture are integrated in order to make our microservice design structure more beautiful on the one hand, and to make it easier to accept new and clean architecture ideas on the basis of existing programming habits.

The implementation of microservices in our project combines the ideas of hierarchical architecture, hexagonal architecture and clean architecture, and takes the actual use scene as the background. The application structure diagram is as follows.

As can be seen from the figure above, an application consists of application layer, domain layer and infrastructure layer. The facade interface of the domain service needs to be exposed to other three-party systems, so it is packaged separately as a module. Because we are used to building systems in a hierarchical architecture pattern, we name the layers according to the hierarchical architecture.

From the perspective of hexagonal architecture, the application layer is equivalent to the inbound adapter, while the infrastructure layer is equivalent to the outbound adapter. Therefore, in fact, the application layer and the infrastructure layer belong to the outer layer and can be considered to be in the same layer.

In fact, the facade module is detached from the domain layer. From the perspective of clean architecture, the domain layer is the core business entity, which encapsulates the most common and top-level business logic in the whole business domain. In general, the core domain model is relatively stable and not subject to changes from outside influences. Facade is the domain service capability exposed by microservices to the outside world. In general, the interface setting should conform to the boundary definition of the current domain service, so the facade module belongs to the kernel domain layer.

The facade interface is implemented in the IMPL part of the application layer, which conforms to the idea that the outer layer of the clean architecture depends on the inner layer. For the IMPL input port and inbound adapter, different protocols and technical frameworks can be used, such as DUBBo or HSF. The composition of each module is explained one by one below.

2. Domain layer Domain

The Factory Factory

The creation of an object is a major operation in itself, but the object being created is not suitable for complex assembly operations. Mixing these responsibilities can produce poor design that is hard to understand. Putting the customer directly in charge of creating the object confuses the customer’s design, breaks the encapsulation of the assembly object, and leads to an overly tight coupling between the customer and the implementation of the object being created.

The creation of complex objects is the responsibility of the domain layer, but this task does not belong to the objects used to represent the model. A separate factory class or an interface to construct domain objects in a domain service is typically used to create domain objects.

Here, we chose to add a domain object creation interface to the domain service to assume the role of factory.

/** * description: ** @author Gao Ju * @date 2020/7/27 */ public class ResourceServiceImpl implements ResourceService {/** * Create a resource aggregation model * * @param resourceCreateCommand Create a resource * @return */ @override public ResourceModel createResourceModel(ResourceCreateCommand resourceCreateCommand) { ResourceModel resourceModel = new ResourceModel(); Long resId = SequenceUtil.generateUuid(); resourceModel.setResId(resId); resourceModel.setName(resourceCreateCommand .getName()); resourceModel.setAuthor(resourceCreateCommand .getAuthor()); List<PackageItem> packageItemList = new ArrayList<>(); . resourceModel.setPackageItemList(packageItemList); return resourceModel; }}Copy the code

Resource Repository

Aggregation instances are typically stored in a repository and the same instances are later retrieved from that repository.

If an aggregation is modified, the change is persisted by the repository, and if an instance is removed from the repository, it cannot be retrieved from the repository.

Resource libraries are created for aggregation dimensions, and there is a one-to-one relationship between aggregation types and resource libraries.

Simply put, a repository is an encapsulation of aggregated CRUD operations. The domain layer is not aware of which storage facilities are used in the repository such as MySQL, MongoDB or Redis.

Resource Repository diagram

In our project, MySQL is used as the persistent storage of resource Repository. In the figure above, each DO corresponds to a database table. Of course, you can use other storage structures or design other table structures. CRUD operations aware only of Resource aggregation dimensions for domain services are shown in the following code.

/** * description: Repository * * @author Gao Ju * @date 2020/08/23 */ @repository ("resourceRepository") public class ResourceRepositoryImpl Implements ResourceRepository {/** * ResourceMapper */ @resource private ResourceMapper ResourceMapper; /** * Mapper */ @resource private PackageMapper PackageMapper; /** */ @resource Private PackagePreviewMapper PackagePreviewMapper; /** @override public void add(resourceModel resourceModel) {@override public void add(resourceModel resourceModel) { ResourceDO resourceDO = new ResourceDO(); resourceDO.setName(resourceModel.getName()); resourceDO.setAuthor(resourceModel.getAuthor()); List<PackageDO> packageDOList = new ArrayList<>(); List<PackagePreviewDO> packagePreviewDOList = new ArrayList<>(); for (PackageItem packageItem : resourceModel.getPackageItemList()) { PackageDO packageDO = new PackageDO(); packageDO.setResId(resourceModel.getResId()); Long packageId = SequenceUtil.generateUuid(); packageDO.setPackageId(packageId); for (PreviewItem previewItem: packageItem.getPreviewItemList()) { PackagePreviewDO packagePreviewDO = new PackagePreviewDO(); . packagePreviewDOList.add(packagePreviewDO); } packageDOList.add(packageDO); } resourceMapper.insert(resourceDO); packageMapper.insertBatch(packageDOList); packagePreviewMapper.insertBatch(packagePreviewDOList); }}Copy the code

You might wonder if, according to the idea of a clean architecture, the Repository interface is defined at the domain layer, and the Repository implementation should be defined at the infrastructure layer, so that it fits into the inner layer, which is more stable than the outer layer.

Combined with our actual development process, it is not easy to make great adjustments after the storage structure is selected or the table structure is set, so we use the customary hierarchical structure. The domain layer is directly dependent on the infrastructure layer to reduce the extra customary cost brought by coding.

Domain Service

Domain-driven emphasizes that we should create congested domain models that encapsulate data and behavior, and map domain models to business objects in the real world. Each class has a clear division of responsibilities to disperse domain logic into various domain objects.

A service in a domain represents a stateless operation that implements a domain-specific task. When an operation does not fit on a domain object, it is best to use a domain service.

A brief summary of the responsibilities of domain service itself is to provide interactive interfaces for the upper application layer by connecting domain objects and resource libraries, generating and publishing domain events, implementing transaction control and other behaviors of a series of objects in the domain.

/** * description: ** @author Gao Ju * @date 2020/8/24 */ Public class UserOrderServiceImpl implements UserOrderService {/** * */ @autowired private OrderRepository OrderRepository; /** * @autowired Private MessagePublisher MessagePublisher; ** @override public void createOrder(userOrder userOrder) { orderRepository.add(userOrder); OrderCreatedEvent orderCreatedEvent = new OrderCreatedEvent(); orderCreatedEvent.setUserId(userOrder.getUserId()); orderCreatedEvent.setOrderId(userOrder.getOrderId()); orderCreatedEvent.setPayPrice(userOrder.getPayPrice()); messagePublisher.send(orderCreatedEvent); }}Copy the code

In the process of practice, for the sake of simplicity and convenience, we still adopt the anaemic domain model, and put the behaviors of domain objects themselves and those not belonging to domain objects into domain services.

Most scenario domain services return an aggregate root or simple type, and some special scenarios can also return an entity or value object contained in the aggregate root to the caller. Domain services can also operate on multiple domain objects at the same time, multiple aggregations, and turn them into another output.

In our actual usage scenario, the domain is relatively simple, the domain service only operates on the objects of one domain, only operates on the aggregation, and the application service coordinates multiple domain objects.

3. Domain events DomainEvent

In the context of domain-driven design, aggregations publish domain events when they are created or when other significant changes occur, and domain events are triggered when the state of the aggregation changes.

When naming domain events, the past participle of the verb is generally chosen, because when the state changes, the current event has occurred. Each attribute of the domain event is a primitive type value or value object, such as event ID and creation time, etc. Event ID can also be used as idempotent.

Conceptually, domain events are published by aggregations, which know when their state changes and thus know what events to publish.

Since the aggregation cannot use dependency injection, the message publisher needs to be passed to the aggregation in the form of method parameters, but this intertwines the infrastructure and business logic and goes against our principle of decoupling design.

A better approach is to put event publishing in the domain service, because the service can easily publish events using dependency injection to get a reference to the message publisher. Whenever the state changes, the aggregation generates events, including a list of events in the return value of the aggregation method, and returns them to the domain service.

Saga is a mechanism for maintaining data consistency in a microservice architecture. Sage consists of a series of local transactions, each of which is responsible for updating the private database of its service, coordinating a series of local transactions through asynchronous messaging to maintain the final consistency of data across multiple services. Saga includes syndication and choreography,

We implement distributed transactions with a collaborative approach, where published domain events are sent as imperative messages to Saga participants. If domain events are self-published and self-consumed, independent of message-oriented implementations, they can be managed using the event bus pattern. The following uses the process of purchasing resources as an example.

The process of purchasing resources

  • By submitting the create order request, OrderService creates a UserOrder in the PAYING state and issues the OrderCreated event.
  • The UserService consumes the OrderCreated event, verifies that the user can place an order, and publishes the UserVerified event.
  • The PaymentService consumes the UserVerified event, performs the actual payment operation, and publishes the PaySuccess event.
  • OrderService receives the PaySuccess event and changes the UserOrder state to PAY_SUCCESS.

The compensation process

  • The PaymentService consumes the UserVerified event and performs the actual payment operation. If the payment fails, the PayFailed event is published.
  • OrderService receives the PayFailed event and changes the UserOrder state to PAY_FAILED.

In the Saga concept,

Step 1 is called a compensable transaction because subsequent steps may fail.

Step 3 is called a critical transaction because it is followed by steps that cannot fail. Step 4 is called repeatable transactions because it always succeeds.

/** * description: ** @author Gao Ju * @date 2020/7/27 */ public class BaseEvent {/** * messageId */ private String messageId; /** * eventType */ private Integer eventType; /** * private Date createTime; /** * private Date modifiedTime; } /** * description: Order Creation event ** @author Gao Ju * @date 2020/8/24 */ Public Class OrderCreatedEvent extends BaseEvent {/** * User ID */ private String userId; /** * orderId */ private String orderId; /** * payPrice */ private Integer payPrice; }Copy the code

4. The Facade module

The facade and domain belong to the same layer. Some classes for use by three parties are defined on the facade, such as the resource type enumeration CategoryEnum that limits the scope of use by three parties, and the domain depends on the enum definition in the facade.

In addition, according to Demeter’s law and the tell not ask principle, the client should know as little as possible about the internal structure of the service object and tell the service object what to do by calling its public interface.

Therefore, we should not leak the domain model outside the microservice. When providing facade services externally, a Data Transfer Object (DTO) is wrapped according to the domain Object to realize the interaction with external three-party systems, such as ResourceDTO in the figure above.

5. Application layer

The application layer is the entry point to the business logic, invoked by the inbound adapter. The facade implementations, scheduled task execution, and message listener handlers are all part of the inbound adapter, so they are all in the application layer.

Normally, one microservice corresponds to one aggregation. In practice, in some scenarios, one microservice can contain multiple aggregations, and the application layer is responsible for task coordination of the use case flow. Domain services inject dependency into the application layer, which executes domain business rules through domain services. The application layer also handles non-domain operations such as authorization, caching, and anti-corruption layer transformations between Dtos and domain objects.

/** * description: Public class UserOrderFacadeImpl implements UserOrderFacade {/** * */ @resource private UserOrderService UserOrderService; @override public FacadeResponse<UserOrderPurchase> createOrder(OrderPurchaseParam orderPurchaseParam ) { UserOrder userOrder = new UserOrder(); userOrder.setUserId(request.getUserId()); userOrder.setResId(request.getResId()); userOrder.setPayPrice(request.getPayAmount()); userOrder.setOrderStatus(OrderStatusEnum.Create.getCode()); userOrderService.handleOrder(userOrder); userOrderPurchase.setOrderId(userOrderDO.getId()); userOrderPurchase.setCreateTime(new Date()); return FacadeResponseFactory.getSuccessInstance(userOrderPurchase); }}Copy the code

6. Infrastructure

The infrastructure’s job is to provide technical support for the rest of the application. The DAO module that interacts with the database, the cache module that interacts with the Redis cache, the local cache, the parameter center, the three-party RPC service, the message framework and the message publisher are all encapsulated in the infrastructure layer.

In addition, the utility class util module and exception class are encapsulated in the infrastructure layer.

From a hierarchical architecture perspective, the domain layer can rely on the infrastructure layer to interact with other peripherals. In addition, both the application layer at the top of the hierarchical architecture and the input port and adapter application from the hexagon perspective can rely on the infrastructure layer as the output port and adapter at the bottom or in the same layer, such as calling util or exception modules.

Fourth, concluding remarks

In fact, the purpose of SOA, microservices, domain-driven, or mid-platform is to say that when we do architecture design, we should start from the business perspective and divide the business domain involved based on the idea of high cohesion and low coupling, so as to maximize and reasonably achieve business reuse.

This is not only convenient to provide professional and stable business services, but also conducive to business precipitation and sustainable development. Under business is the implementation of systems based on technology. Technology creates business and business leads technology. The two complement each other and contribute to social progress together.

5. References

  • [1] Solutions to The Core Complexity of Domain-Driven Design Software, Eric Evans, Translated by Zhao Lishenghai, Yan, Liu Xia, et al., Posts and Telecommunications Press
  • [2] “Implementing Domain-Driven Design” by Vaughn Vernon, Teng Yunyi, Zhang Yishen, Publishing House of Electronics Industry
  • [3] Microservices Architecture Design Patterns [US] Chris. By Chris Richardson. Translated by Yu Yong. China Machine Press
  • [4] Robert C.Martin, The Way to Clean Architecture, translated by Sun Yucong, Publishing House of Electronics Industry
  • [5] “Transformation of Enterprise IT Architecture: Alibaba’s Strategic Thought and Architecture Practice in Taiwan”, Zhong Hua, China Machine Press
  • [6] Domain Driven Design (DDD) Practice Road (ii) : Event Driven and CQRS, Vivo Internet Technology
  • [7] The Practice of Domain Driven Design in Internet Business Development, Meituan Technical team

By Angel Gao