Back to blog
Jun 29, 2023
6 min read

Domain-Driven Design

A software development approach that aims to align software design with the domain (business) requirements and focus on the core business logic

ddd-arc


DDD provides a set of principles and patterns for designing complex software systems, emphasizing a deep understanding of the domain and collaboration between domain experts and developers. The goal is to create software that reflects the language, concepts, and behaviors of the domain, leading to more maintainable, extensible, and effective solutions.

Key principles and patterns of DDD include:

  1. Ubiquitous Language: Establishing a common language between domain experts and developers to ensure a shared understanding of the domain. The domain language should be used consistently in code, documentation, and communication.

  2. Bounded Contexts: Defining boundaries around different parts of the domain to isolate and manage complexity. Each bounded context has its own models, language, and rules, and interactions between contexts are carefully defined.

  3. Aggregates: Aggregates are clusters of related domain objects treated as a single unit. They enforce consistency and encapsulate business rules. Aggregates control access to their internal objects and ensure that invariants are maintained.

  4. Domain Events: Events are used to capture significant changes or occurrences within the domain. They can be used to communicate changes to other parts of the system or trigger actions.

  5. Value Objects: Value objects represent concepts in the domain that have no individual identity but are defined by their attributes. They are immutable and can be shared and compared based on their attributes.

  6. Repositories: Repositories provide an abstraction for accessing and persisting domain objects. They encapsulate the details of data storage and retrieval and provide a clean interface for working with the domain.

  7. Domain Services: Domain services encapsulate complex domain operations or behaviors that do not belong to a specific entity or value object. They operate on multiple domain objects and may involve coordination and orchestration.

DDD encourages a deep understanding of the business domain and collaboration between domain experts and technical teams. It helps to build software systems that are more aligned with business requirements, easier to maintain, and adaptable to changes in the domain.

Practical Approach To DDD Pattern With Typescript and Golang

Ubiquitous Language

Explanation: Ubiquitous Language refers to a common language shared by domain experts and developers to ensure a clear understanding of the domain concepts.

  • Example in TypeScript:

    // Domain Entity in TypeScript
    class Customer {
      id: number;
      name: string;
    }
    
  • Example in Go:

    // Domain Entity in Go
    type Customer struct {
      ID   int
      Name string
    }
    

Bounded Contexts

Explanation: Bounded Contexts are explicit boundaries that define specific parts of the domain. Each bounded context has its own models, language, and rules.

  • Example in TypeScript:

    // Customer Bounded Context in TypeScript
    // Customer Entity
    class Customer {
      // ...
    }
    
    // Customer Repository
    interface CustomerRepository {
      getById(id: number): Customer;
      save(customer: Customer): void;
    }
    
  • Example in Go:

    // Customer Bounded Context in Go
    // Customer Entity
    type Customer struct {
      // ...
    }
    
    // Customer Repository
    type CustomerRepository interface {
      GetByID(id int) Customer
      Save(customer Customer)
    }
    

Aggregates

Explanation: Aggregates are clusters of related objects treated as a single unit. They enforce consistency and encapsulate business rules.

  • Example in TypeScript:

    // Order Aggregate in TypeScript
    class Order {
      id: number;
      customer: Customer;
      items: OrderItem[];
      // ...
    }
    
  • Example in Go:

    // Order Aggregate in Go
    type Order struct {
      ID       int
      Customer Customer
      Items    []OrderItem
      // ...
    }
    

Explanation

Domain Events represent significant changes or occurrences within the domain. They can be used to communicate changes to other parts of the system or trigger actions.

  • Example in TypeScript:

    // Domain Event in TypeScript
    class OrderPlacedEvent {
      orderId: number;
      // ...
    }
    
  • Example in Go:

    // Domain Event in Go
    type OrderPlacedEvent struct {
      OrderID int
      // ...
    }
    

Value Objects

Explanation: Value Objects represent concepts in the domain that have no individual identity but are defined by their attributes. They are immutable and can be shared and compared based on their attributes.

  • Example in TypeScript:

    // Value Object in TypeScript
    class Money {
      amount: number;
      currency: string;
      // ...
    }
    
  • Example in Go:

    // Value Object in Go
    type Money struct {
      Amount   int
      Currency string
      // ...
    }
    

Repositories

Explanation: Repositories provide an abstraction for accessing and persisting domain objects. They encapsulate the details of data storage and retrieval and provide a clean interface for working with the domain.

  • Example in TypeScript:

    // Customer Repository in TypeScript
    interface CustomerRepository {
      getById(id: number): Customer;
      save(customer: Customer): void;
    }
    
  • Example in Go:

    // Customer Repository in Go
    type CustomerRepository interface {
      GetByID(id int) Customer
      Save(customer Customer)
    }
    

Domain Services

Explanation: Domain Services encapsulate complex domain operations or behaviors that do not belong to a specific entity or value object. They operate on multiple domain objects and may involve coordination and orchestration.

  • Example in TypeScript:

    // Shipping Service in TypeScript
    interface ShippingService {
      shipOrder(order: Order): void;
      calculateShippingCost(order: Order): number;
    }
    
  • Example in Go:

    // Shipping Service in Go
    type ShippingService interface {
      ShipOrder(order Order)
      CalculateShippingCost(order Order) float64
    }
    

Book: “Domain-Driven Design: Tackling Complexity in the Heart of Software” by Eric Evans - This book is considered the seminal work on DDD and provides a comprehensive guide to understanding and applying DDD principles.

Book: “Implementing Domain-Driven Design” by Vaughn Vernon - This book offers practical insights and examples for implementing DDD in real-world projects.

Website: Domain-Driven Design Community - The official website for the DDD community, which includes articles, case studies, and discussions on various DDD topics: https://dddcommunity.org/

Website: Microsoft’s Domain-Driven Design Fundamentals - A series of articles by Microsoft that introduce the fundamental concepts of DDD and provide guidance for implementing DDD in software projects: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/

GitHub Repository: Domain-Driven Design in PHP - A GitHub repository with examples and code samples that demonstrate DDD concepts implemented in PHP: https://github.com/dddinphp/ddd

GitHub Repository: Go Clean Architecture - A GitHub repository with a practical example of implementing Clean Architecture and DDD principles in a Go application: https://github.com/bxcodec/go-clean-arch

These resources will provide you with a solid foundation and practical examples to delve deeper into Domain-Driven Design and its implementation in various programming languages.