Encapsulating domain models is a critical aspect of domain-driven design (DDD) that helps maintain the integrity and consistency of your application's core logic. Without proper encapsulation, the domain logic can become scattered and difficult to manage, leading to increased complexity and maintenance challenges.
When the domain model is not properly encapsulated, business rules and logic might be spread across various parts of the application, making it difficult to understand, maintain, and extend. Encapsulation ensures that the domain model is self-contained, with a clear and coherent structure.
IEnumerable<T> or IReadOnlyList<T> instead of List<T>)public class Order{public required Guid CustomerId { get; set; }public OrderStatus OrderStatus { get; set; }public decimal PaidTotal { get; set; }public Customer? Customer { get; set; }public ICollection<OrderItem> Items { get; set; } = [];}
❌ Figure: Bad example - Public setters, exposed collections, no constructor
public class Order{public Guid Id { get; private set; }public Guid CustomerId { get; private set; }public OrderStatus OrderStatus { get; private set; }public Customer Customer { get; private set; } = null!;public decimal PaidTotal { get; private set; }private readonly List<OrderItem> _items = [];public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();public static Order Create(CustomerId customerId){Guard.Against.Null(customerId);var order = new Order(){Id = new OrderId(Guid.NewGuid()),CustomerId = customerId,OrderStatus = OrderStatus.New,PaidTotal = Money.Default};return order;}}
✅ Figure: Good example - Private setters, read-only collection, factory method