Skip to content

Chapter 3: Domain-Driven Design (DDD) for Microservices


3.1 Introduction to Domain-Driven Design (DDD)

Domain-Driven Design (DDD) is a software development approach that focuses on understanding and modeling the core business domain. It emphasizes collaboration between technical teams and domain experts to create a shared understanding of the problem space.

  • Why DDD for Microservices?
    • Helps in identifying bounded contexts (natural boundaries for microservices).
    • Ensures that microservices align with business capabilities.
    • Promotes a clear separation of concerns and modularity.

3.2 Key Concepts of DDD

  1. Domain:
    • The specific business area or problem space (e.g., e-commerce, healthcare, finance).
  2. Bounded Context:
    • A boundary within which a particular model is defined and applicable.
    • Each bounded context corresponds to a microservice.
  3. Ubiquitous Language:
    • A shared language between developers and domain experts to ensure clear communication.
  4. Entities and Value Objects:
    • Entities: Objects with a unique identity (e.g., a User with a UserId).
    • Value Objects: Objects without identity, defined by their attributes (e.g., an Address).
  5. Aggregates:
    • A cluster of related objects (entities and value objects) treated as a single unit.
    • Ensures consistency and enforces business rules.
  6. Repositories:
    • Provides access to aggregates, abstracting the underlying data storage.
  7. Domain Events:
    • Events that represent significant changes in the domain (e.g., OrderPlaced, PaymentProcessed).

3.3 Strategic Patterns in DDD

Strategic patterns help in defining the high-level structure of the system.

  1. Bounded Contexts:
    • Identify natural boundaries within the domain.
    • Example: In an e-commerce system, bounded contexts could be Product CatalogOrder Management, and User Management.
  2. Context Mapping:
    • Defines the relationships between bounded contexts.
    • Common patterns:
      • Partnership: Two contexts collaborate closely.
      • Shared Kernel: Shared code or data between contexts.
      • Customer-Supplier: One context depends on another.
      • Conformist: One context conforms to the model of another.
      • Anti-Corruption Layer: Protects a context from changes in another.

3.4 Tactical Patterns in DDD

Tactical patterns focus on the implementation details within a bounded context.

  1. Entities:
    • Example: A Product entity in the Product Catalog context.
public class Product
{
public int Id { get; private set; }
public string Name { get; private set; }
public decimal Price { get; private set; }

public Product(int id, string name, decimal price)
{
Id = id;
Name = name;
Price = price;
}
}

2. Value Objects:

  • Example: An Address value object in the Order Management context.
    public class Address
    {
    public string Street { get; }
    public string City { get; }
    public string ZipCode { get; }

    public Address(string street, string city, string zipCode)
    {
    Street = street;
    City = city;
    ZipCode = zipCode;
    }
    }

    3. Aggregates:

    • Example: An Order aggregate in the Order Management context.
      public class Order
      {
      public int Id { get; private set; }
      public List<OrderItem> Items { get; private set; }
      public decimal TotalPrice => Items.Sum(item => item.Price * item.Quantity);

      public Order(int id)
      {
      Id = id;
      Items = new List<OrderItem>();
      }

      public void AddItem(int productId, string productName, decimal price, int quantity)
      {
      Items.Add(new OrderItem(productId, productName, price, quantity));
      }
      }

      public class OrderItem
      {
      public int ProductId { get; }
      public string ProductName { get; }
      public decimal Price { get; }
      public int Quantity { get; }

      public OrderItem(int productId, string productName, decimal price, int quantity)
      {
      ProductId = productId;
      ProductName = productName;
      Price = price;
      Quantity = quantity;
      }
      }

      4. Repositories:

      • Example: A repository for managing Order aggregates.
        public interface IOrderRepository
        {
        Order GetById(int id);
        void Save(Order order);
        }

        public class OrderRepository : IOrderRepository
        {
        private readonly List<Order> _orders = new List<Order>();

        public Order GetById(int id)
        {
        return _orders.FirstOrDefault(o => o.Id == id);
        }

        public void Save(Order order)
        {
        var existingOrder = _orders.FirstOrDefault(o => o.Id == order.Id);
        if (existingOrder != null)
        {
        _orders.Remove(existingOrder);
        }
        _orders.Add(order);
        }
        }

        5. Domain Events:

        • Example: An OrderPlaced event in the Order Management context.
          public class OrderPlaced
          {
          public int OrderId { get; }
          public DateTime PlacedOn { get; }

          public OrderPlaced(int orderId, DateTime placedOn)
          {
          OrderId = orderId;
          PlacedOn = placedOn;
          }
          }

          3.5 Applying DDD to Microservices

          1. Identify Bounded Contexts:
            • Break down the system into bounded contexts based on business capabilities.
            • Example: In an e-commerce system:
              • Product Catalog: Manages product information.
              • Order Management: Handles order creation and tracking.
              • User Management: Manages user authentication and profiles.
          2. Define Aggregates and Entities:
            • Design aggregates and entities within each bounded context.
            • Example: In the Order Management context:
              • Order (Aggregate Root)
              • OrderItem (Entity)
          3. Use Ubiquitous Language:
            • Ensure that the terms used in the code match the business terminology.
            • Example: Use OrderOrderItem, and Product consistently across the system.
          4. Implement Repositories:
            • Use repositories to manage aggregates and abstract data access.
            • Example: IOrderRepository for managing Order aggregates.
          5. Publish Domain Events:
            • Use domain events to communicate changes between bounded contexts.
            • Example: Publish an OrderPlaced event when an order is created.

          3.6 Example: E-Commerce System with DDD

          Below is a diagram illustrating the bounded contexts and their relationships in an e-commerce system:

          +-------------------+       +-------------------+       +-------------------+
          |  Product Catalog  |       |  Order Management |       |  User Management  |
          |  (Bounded Context)|<----->|  (Bounded Context)|<----->|  (Bounded Context)|
          +-------------------+       +-------------------+       +-------------------+
                  |                         |                         |
                  |                         |                         |
                  v                         v                         v
          +-------------------+       +-------------------+       +-------------------+
          |  Product Entity   |       |  Order Aggregate  |       |  User Entity      |
          |  (Tactical Pattern)|       |  (Tactical Pattern)|       |  (Tactical Pattern)|
          +-------------------+       +-------------------+       +-------------------+
          • Explanation:
            • Each bounded context (Product Catalog, Order Management, User Management) is implemented as a microservice.
            • Tactical patterns (entities, aggregates) are used within each bounded context.

          3.7 Key Takeaways

          • DDD helps in designing microservices that align with business capabilities.
          • Bounded contexts define the boundaries of microservices.
          • Tactical patterns (entities, aggregates, repositories) provide a structured way to implement microservices.
          • Ubiquitous language ensures clear communication between developers and domain experts.

          In the next chapter, we’ll explore Designing Microservice Architectures, focusing on decomposing monoliths, defining service boundaries, and implementing communication patterns.

          Leave a Reply