Domain Model Purity emphasizes separating representation from identity and keeping domain logic free from infrastructure concerns.

Separate Representation from Identity

  • Is an EmailAddress just a string? No.
  • Is a CustomerId just an integer? No. Can you add two CustomerIds together? No.

While CustomerId may be represented by an integer internally, in domain modeling we create wrapper types:

F# example:

type EmailAddress = EmailAddress of string
type PhoneNumber = PhoneNumber of string
type CustomerId = CustomerId of integer

C# example:

public readonly struct Name
{
    public Name(string value)
    {
        _value = value;
    }
 
    private readonly string _value;
    public string Value => _value;
}

Domain Logic Free from Infrastructure

Pure Domain

public class Order
{
    public void Submit()
    {
        // Pure domain logic - no database, no email, no infrastructure
        if (!_lines.Any())
            throw new InvalidOperationException("Cannot submit empty order");
 
        Status = OrderStatus.Submitted;
        AddDomainEvent(new OrderSubmitted(Id));
    }
}

Infrastructure Concerns Separated

// Infrastructure layer
public class OrderRepository
{
    public void Save(Order order)
    {
        _dbContext.SaveChanges();  // Database concern
    }
}
 
public class OrderSubmittedHandler
{
    public void Handle(OrderSubmitted @event)
    {
        _emailService.SendConfirmation(@event.CustomerId);  // Email concern
    }
}

Key Considerations

  • Domain model purity and the current time
  • Domain model purity and lazy loading
  • Separation of domain logic from infrastructure concerns

References