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
- Domain:
- The specific business area or problem space (e.g., e-commerce, healthcare, finance).
- Bounded Context:
- A boundary within which a particular model is defined and applicable.
- Each bounded context corresponds to a microservice.
- Ubiquitous Language:
- A shared language between developers and domain experts to ensure clear communication.
- 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).
- Aggregates:
- A cluster of related objects (entities and value objects) treated as a single unit.
- Ensures consistency and enforces business rules.
- Repositories:
- Provides access to aggregates, abstracting the underlying data storage.
- 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.
- Bounded Contexts:
- Identify natural boundaries within the domain.
- Example: In an e-commerce system, bounded contexts could be Product Catalog, Order Management, and User Management.
- 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.
- Entities:
- Example: A
Product
entity in the Product Catalog context.
- Example: A
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
- 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.
- Define Aggregates and Entities:
- Design aggregates and entities within each bounded context.
- Example: In the Order Management context:
Order
(Aggregate Root)OrderItem
(Entity)
- Use Ubiquitous Language:
- Ensure that the terms used in the code match the business terminology.
- Example: Use
Order
,OrderItem
, andProduct
consistently across the system.
- Implement Repositories:
- Use repositories to manage aggregates and abstract data access.
- Example:
IOrderRepository
for managingOrder
aggregates.
- 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.