Domain Driven Design: Strategic Microservices Boundaries
Domain driven design provides the strategic and tactical patterns needed to decompose complex systems into well-bounded microservices. Therefore, service boundaries align with business capabilities rather than technical layers. As a result, teams own coherent business domains and can evolve their services independently.
Bounded Contexts and Context Maps
Bounded contexts define explicit boundaries where a particular domain model applies consistently. Moreover, the same business concept may have different representations in different contexts — a “Customer” in billing differs from a “Customer” in shipping. Consequently, each microservice owns its bounded context with its own data model and ubiquitous language.
Context maps document relationships between bounded contexts including upstream/downstream dependencies and integration patterns. Furthermore, patterns like Anti-Corruption Layer, Shared Kernel, and Customer/Supplier describe how contexts interact.
Domain Driven Design Tactical Patterns
Aggregates enforce consistency boundaries within a bounded context by grouping related entities under a single root. Additionally, domain events capture meaningful state changes that other contexts may need to react to. For example, an Order aggregate publishes an OrderPlaced event that the Inventory context consumes to reserve stock.
// DDD Aggregate Root with domain events
public class Order extends AggregateRoot {
private OrderId id;
private CustomerId customerId;
private List<OrderLine> lines = new ArrayList<>();
private OrderStatus status;
private Money totalAmount;
public static Order create(CustomerId customerId, List<OrderLine> lines) {
Order order = new Order();
order.id = OrderId.generate();
order.customerId = customerId;
order.lines = List.copyOf(lines);
order.status = OrderStatus.PENDING;
order.totalAmount = lines.stream()
.map(OrderLine::subtotal)
.reduce(Money.ZERO, Money::add);
order.registerEvent(new OrderPlaced(
order.id, customerId, order.totalAmount, Instant.now()
));
return order;
}
public void confirm() {
if (status != OrderStatus.PENDING)
throw new IllegalStateException("Only pending orders can be confirmed");
this.status = OrderStatus.CONFIRMED;
registerEvent(new OrderConfirmed(id, Instant.now()));
}
}Value objects encapsulate domain concepts like Money, Address, and OrderId with equality based on attributes rather than identity. Therefore, they make domain logic explicit and self-documenting.
Strategic Design for Service Decomposition
Event storming workshops help teams discover bounded contexts by mapping business processes as sequences of domain events. However, initial context boundaries often need refinement as understanding deepens. In contrast to technical decomposition, DDD ensures services reflect actual business structure and communication patterns.
Anti-Corruption Layers
Anti-corruption layers translate between different domain models at context boundaries preventing model pollution. Additionally, they isolate your core domain from external system quirks and legacy data formats. Specifically, ACLs convert external representations into your domain’s ubiquitous language at the integration boundary.
Related Reading:
Further Resources:
In conclusion, domain driven design provides the strategic foundation for building well-bounded microservices that align with business capabilities. Therefore, invest in understanding your domain before decomposing systems into services.