Updated: Jul 1
Domain-Driven Design (DDD) is a popular pattern commonly used in microservices architecture. It emphasizes the interaction of microservices with domain models to capture and manage the business state. Domain models are data models specific to each microservice, representing the core concepts and entities within the domain.
For instance, let's consider three microservices: authorization, order, and shipping. Each of these microservices may have its own domain model related to the customer business object but applied in different contexts. The authorization microservice's domain model could include attributes such as user_id and other authentication details. The order microservice's domain model might include attributes like customer_id and other customer-specific details related to orders. Similarly, the shipping microservice's domain model could include attributes like customer_id and address.
Domain-Driven Design (DDD) for Microservices Data Architecture
In DDD, it is acceptable to duplicate data across microservices to establish a bounded context for data within each microservice. Each microservice defines its own boundary for the data it manages, allowing for autonomy and independence. This means that the concept of a customer can have different boundaries and representations within each microservice, tailored to the specific requirements of that microservice.
A bounded context of data can serve multiple microservices if they all depend on the same bounded context. It is also possible to implement a logical "business microservice" as multiple physical microservices, where each physical microservice handles a specific aspect of the business functionality.
One of the principles of DDD is that each microservice should be cohesive and self-sufficient with its data. It should not depend on external data from outside the bounded context, nor should it update data outside its own bounded context. This encapsulation ensures that each microservice has control over its domain data and can operate independently.
The domain data models in DDD can be implemented on any suitable type of database. The choice of database technology depends on factors such as scalability, performance, and specific requirements of the microservice. By employing DDD principles, microservices can effectively manage their domain-specific data and ensure proper separation of concerns within a complex system.
Data Propagation Beyond Bounded Context in DDD
In Domain-Driven Design (DDD), there may be situations where data needs to propagate across bounded contexts, bypassing the idea of strict boundaries. This can occur when actions such as retiring a customer or making updates require data to be shared across domain models. Eventual consistency is often employed to address this challenge.
Eventual Consistency Models
Eventual consistency models allow changes to be propagated across bounded contexts through asynchronous messaging using protocols like AMQP (e.g., RabbitMQ) or through synchronous HTTP requests to update another microservice. Asynchronous messaging is typically recommended as it is non-blocking in nature, providing a more suitable approach for change propagation.
It is important to avoid daisy-chaining HTTP requests between microservices as it can lead to an anti-pattern, resulting in a service-oriented architecture (SOA) without proper microservices granularity. For instance, if the shipping service issues an HTTP request to the order service, which then issues an HTTP request to the authorization service, it creates a chain of dependencies that can become complex and inefficient.
Instead, when using HTTP for change propagation across domain models, it is advisable to leave an asynchronous message and let the messaging queue's publish-subscribe or event-driven model handle it. Meanwhile, the microservice can respond back to the client's HTTP request. This approach ensures loose coupling between microservices and promotes scalability and responsiveness.
Repository — Aggregate pattern
Regarding persistence, domain models are not responsible for the persistence layer or data infrastructure. They primarily maintain the application state but do not persist in the overall business logic. Business logic is typically persisted in a normalized relational database, which microservices do not need to interact with directly. Instead, a repository pattern is commonly used, where a repository service (referred to as the repository) is responsible for performing CRUD (Create, Read, Update, Delete) operations on persistent data.
Microservices that need to persist data utilize the repository to interact with the persistence layer. The repository, following best practices, uses aggregates for insert, update, and delete operations. Aggregates represent related data elements within a cohesive domain. For example, in the context of an order domain, an aggregate may encompass various data elements related to an order.
By employing these patterns and practices, DDD allows for effective data propagation across bounded contexts while maintaining loose coupling between microservices and ensuring a clear separation of concerns in the overall architecture.
In the context of domain-driven design (DDD), when dealing with entities and value objects, it is important to consider the relationships and integrity within aggregates. For example, in the case of an Order entity and OrderItem value objects, you cannot treat an OrderItem as a simple value object because it has a strong relationship with the Order entity and possesses its own identity. Similarly, deleting an Order without considering the implications for the associated OrderItems would violate the integrity of the Order aggregate.
Likewise, if there is a strong relationship between Address and Customer (not depicted in the picture), within the context of the Order aggregate, Address can be treated as a value object. In this scenario, the aggregate root for Orders would be the Order_id from the Order table.
To achieve an ideal design, it is recommended to use one repository per aggregate. This ensures that insert, update, and delete operations are performed as atomic transactions, maintaining the integrity of the aggregate. By adhering to this approach, a consistent mechanism is established to handle reference data within complex microservices.
This design principle helps to preserve data consistency and ensures that the relationships and boundaries defined within aggregates are maintained. It allows for cohesive management of related entities and value objects while avoiding data inconsistencies and preserving the integrity of the domain model.