13 Code Model on How to Use Ddd to Design Microservice Code Models

13 Code Model on How to Use DDD to Design Microservice Code Models #

Hello, I am Ou Chuangxin.

In the previous lecture, we completed the design of the domain model. Now, let’s move on to the design and implementation of the microservices. The first thing to determine when implementing microservices is the code structure of the microservice, which is what I am going to talk about today - microservice code model.

Only by establishing a standardized microservice code model and code specifications can we place the code objects corresponding to the domain objects in the appropriate directory structure of the software package. A standardized code model can help project team members better understand the code, implement team collaboration according to code specifications, and ensure that the logic of each layer of the microservices does not interfere with each other, enabling focused collaboration and avoiding unnecessary code confusion. In addition, a standardized code model allows you to easily perform code refactoring when evolving your microservice architecture.

So, what does the code structure of a microservice look like in DDD? And based on what do we establish the microservice code model? These are the two main questions we will address today.

DDD Layered Architecture and Microservice Code Model #

We refer to the DDD layered architecture model to design the microservice code model. Yes! The microservice code model is designed based on the DDD layered architecture model. But why the DDD layered architecture model?

img

Let’s briefly review the DDD layered architecture model introduced in [Lecture 07]. It includes the user interface layer, application layer, domain layer, and infrastructure layer. The responsibilities of each layer in the layered architecture are very clear, allowing for well-organized collaboration among the layers.

User interface layer: Provides service adaptation for the front-end and resource adaptation for the resource layer. This layer consolidates the functionality related to interface adaptation.

Application layer responsibilities: Implements service composition and orchestration to adapt to rapidly changing business processes. This layer consolidates application services and event-related functionality.

Domain layer: Implements the core business logic of the domain. This layer consolidates domain objects such as aggregates, aggregate roots, entities, value objects, domain services, and events, as well as the business capabilities formed by their composition.

Infrastructure layer: Spans all layers and provides basic resource services to each layer. This layer consolidates various services and capabilities related to underlying resources.

The business logic is encapsulated and coordinated layer by layer, from the domain layer to the application layer and user interface layer, providing flexible services to the outside world. This achieves both the division of labor and collaboration among the layers. Therefore, there is no doubt that the DDD layered architecture model is the best basis for designing the microservice code model.

Microservice Code Model #

Now let’s take a look at what the microservice code model designed according to the DDD layered architecture looks like.

Actually, DDD does not provide a standard code model, and different people may have different interpretations. The microservice code model I’m about to talk about is established by me through thinking and practice, and it primarily considers the boundaries, layers, and architectural evolution of microservices.

Microservice Top-level Directory Structure #

The top-level directory structure of the microservice is defined according to the responsibilities of the DDD layered architecture. From the following diagram, we can see that there are four top-level code directories in the code model, which are interfaces, application, domain, and infrastructure, corresponding to the user interface layer, application layer, domain layer, and infrastructure layer, respectively.

img

Here are the functions and code forms of these directories.

Interfaces: This directory mainly stores the code related to the user interface layer and the interaction with the frontend. The frontend application obtains the data required for presentation by calling the interfaces in this layer. This layer is mainly responsible for handling Restful requests sent by users, parsing user input configuration files, and passing data to the Application layer. Code related to data assembly, data transfer format, and facade interfaces will be placed in this directory.

Application: This directory mainly stores the code related to service composition and orchestration in the application layer. The application services orchestrate and compose services based on the domain services within the microservice or external microservices, and provide various application data presentation support services to the user interface layer. Code related to application services, events, etc., will be placed in this directory.

Domain: This directory mainly stores the code related to the core business logic of the domain layer. The domain layer can contain multiple aggregate code packages, which together implement the core business logic of the domain model. Code related to aggregates, entities within aggregates, methods, domain services, and events will be placed in this directory.

Infrastructure: This directory mainly stores the code related to basic resource services, providing common technical capabilities, third-party software packages, database services, configuration, and basic resource services for other layers.

Directory Structure of Each Layer #

1. User Interface Layer

The directory structure of Interfaces consists of three types: assembler, dto, and facade.

img

Assembler: It implements the conversion and data exchange between DTOs and domain objects. Usually, the Assembler appears together with the DTO.

DTO: It is a data carrier and does not contain any business logic internally. We can use the DTO to isolate the internal domain objects from the outside world.

Facade: It provides coarse-grained calling interfaces and delegates user requests to one or more application services for processing.

2. Application Layer

The directory structure of the Application layer includes events and services.

img

Events: This directory mainly stores the code related to events. It includes two subdirectories: publish and subscribe. The former mainly stores the code related to event publishing, and the latter mainly stores the code related to event subscription (the core business logic related to event handling is implemented in the domain layer). Here are some tips: Although both the application layer and the domain layer can publish and handle events, I suggest that you unify the publishing and subscription handling of all events within the microservice at the application layer to achieve centralized event management. The core business logic related to events should be implemented in the domain layer. Use the application layer to invoke domain layer services to complete the entire event publishing and subscription handling process.

Service (Application Service): This layer provides application services, which encapsulate, orchestrate, and combine multiple domain services or external application services to provide coarse-grained services externally. Application services mainly implement service composition and orchestration, and represent independent business logic. You can put all application services in one application service class, or design one application service as one class to avoid having too much code in the application service class.

3. Domain Layer

The Domain layer consists of one or more aggregates that jointly implement the core business logic of the domain model. The code within an aggregate follows a standardized and unified model, including four subdirectories: entity, event, repository, and service.

img

The internal code structure of an aggregate in the domain layer is as follows:

Aggregate: This is the root directory of the aggregate package. You can name it based on the name of your actual project aggregate, such as “Permission”. Define the relationship and boundaries between the aggregate root, entities, value objects, and domain services within the aggregate. The aggregate implements highly cohesive business logic that can be independently divided into microservices.

The main purpose of putting code on a per-aggregate basis in one package is to achieve business cohesion, but the larger goal is to allow for easy restructuring of aggregates between microservices in the future. Clear code boundaries between aggregates facilitate the easy recombination of microservices based on aggregates, which plays an important role in the evolution of microservices architecture.

Entity: This directory stores the aggregate root, entities, value objects, and code related to the Factory pattern. The entity classes follow the rich model, where the business logic related to a specific entity is implemented within the entity class. Cross-entity business logic is implemented in the domain services.

Event: This directory stores event entities and business logic related to event activities.

Service (Domain Service): This directory stores domain service code. A domain service represents a piece of business logic composed of multiple entities. You can put all domain services within an aggregate in one domain service class, or design each domain service as a separate class. If the business logic within a domain service is relatively complex, I recommend designing each domain service as a separate class to avoid bloating a single domain service class with all domain service code. Domain services encapsulate multiple entities or methods and provide application service invocation to the upper layer.

Repository: This directory stores the code for querying or persisting domain objects within the corresponding aggregate. It usually includes the repository interface and implementation methods. To facilitate the splitting and composition of aggregates, we have set a principle: one repository corresponds to one aggregate.

Special note: According to the DDD layered architecture, the repository implementation should belong to the infrastructure layer. However, in order to ensure the ease of code splitting and recombination during the evolution of microservices architecture, I have placed the implementation of aggregate repositories within the aggregate package. This way, when the requirements or design change, and the aggregate needs to be split or recombined, we can easily migrate the entire aggregate package, including the core business logic and repository code, to achieve a smooth transition in microservices architecture.

4. Infrastructure Layer

The Infrastructure layer has two subdirectories: config and util.

img

Config: This directory primarily stores configuration-related code.

Util: This directory primarily stores basic code such as platforms, development frameworks, messaging, databases, caching, files, buses, gateways, third-party libraries, and general algorithms. You can create different subdirectories for different categories of resources.

Overall Directory Structure of the Code Model #

After completing the first-level and second-level code model designs, you will see the overall directory structure of the microservices code model as shown in the following diagram:

img

Summary #

Today, we established a standard microservice code model based on the DDD layered architecture model. In the code model, each code object has its own position and responsibilities, working together to complete the business logic of the microservices.

There are two points I would like to emphasize regarding the code model.

First: The boundaries between aggregates must be clear. The service calls and data associations between aggregates should be as loosely coupled and lowly associated as possible. Service calls between aggregates should be implemented through composition in the upper application layer, and it is generally not allowed for aggregates to directly call domain services. This loosely coupled code association will be very important in the future business development and requirement changes, as it will facilitate the reorganization of business functions and aggregate code in the evolution of the microservice architecture.

Second: You must have a concept of code layering. When writing code, it is essential to understand the responsibilities of the code and place it in the corresponding code directory. The application layer code mainly handles service composition and coordination between aggregates. It is a thin layer and should not contain core domain logic code. The domain layer is the core of the business, and the core logic code of the domain model must be implemented in the domain layer. If you place core domain logic code in the application layer, your microservice based on the DDD layered architecture model will slowly evolve into a traditional three-tier architecture model.