Domain Driven Design in a Nutshell

What is Domain-Driven Design (DDD)?

Domain-Driven Design (DDD) is a software development approach that emphasizes the importance of the domain, or the field of study that the software serves, in driving the design of the software. The approach was pioneered by Eric Evans in his book “Domain-Driven Design: Tackling Complexity in the Heart of Software” and has since become a popular methodology for complex systems.

DDD is a problem-solving approach that enables developers to understand the complexity of business processes, map them into the software’s design, and establish a common language between the development team and the domain experts. The goal is to develop a system that accurately models the problem domain.

Domain-Driven Design in an E-commerce Context

Let’s consider an e-commerce platform to better understand DDD.

Account Management

This domain manages user registration, login, authentication, profile updates, and account deletion. It communicates with the Orders and Billing domains to get a user’s history and current orders.

Products

This domain handles everything related to product management including creation, updates, categorization, and deletion of products. It works closely with the Orders and Discounts domains for pricing and ordering processes.

Orders

This domain covers all the operations related to placing an order, tracking an order, and canceling an order. It communicates with Account Management to know who placed the order, with Products to know what’s being ordered, and with Billing to process the payment.

Billing

This domain handles payment processing, invoice generation, and payment status tracking. It interacts with Account Management for user details, and with Orders for order details.

This pattern continues across all the other domains like Shipments, Returns, Discounts, Exchanges, Payments, Tax, etc., each with its own responsibilities and interactions with other domains.

DDD Goals

The key goals of DDD include:

  • Understanding the Domain: To build a successful software system, developers need to understand the problem they’re trying to solve. DDD encourages close collaboration between technical experts and domain experts to facilitate this understanding.

  • Ubiquitous Language: DDD promotes creating a common language between developers and domain experts. This language is used throughout the project, in code, diagrams, and discussions.

  • Reducing Complexity: DDD aims to deal with complex domains by dividing them into different bounded contexts, each with its own unified model.

  • Reflecting Domain Logic in Code: The design and structure of the software should reflect the domain. This makes the software more intuitive and maintainable.

Tactical Design in DDD

Tactical design involves the practical implementation of the domain model. It provides a set of patterns and principles to translate domain models into highly flexible and maintainable systems. The key elements of tactical design are:

Entities

Entities are objects with a distinct identity that persists over time, even though their attributes might change. For example, in an e-commerce platform, a ‘User’ would be an entity.

Value Objects

Value objects are objects that don’t have an identity. They are immutable and their equality is based on their state, not on their identity. For example, ‘Money’ could be a value object.

Associations

Associations represent the relationships between entities and value objects. For example, a ‘User’ entity might be associated with an ‘Order’ entity.

Services

Services encapsulate operations that don’t naturally belong to an entity or value object. For example, ‘Payment Processing’ could be a service.

Aggregates and Factories

An aggregate is a cluster of associated objects that we treat as a unit for the purpose of data changes. Each aggregate has a root, the only member of the aggregate that outside objects are allowed to hold references to. Factories are used for creating complex objects and aggregates.

Let’s consider the Order domain. An Order is composed of multiple Line Items, where each Line Item represents a specific product and quantity in the order. Additionally, each Order has a status (e.g., pending, shipped, delivered) and is associated with a specific User. Here, the Order is an Aggregate, and the Line Items are part of this Aggregate. The Order (Aggregate Root) is the only entity accessible from outside. If we want to change anything within the Order, such as adding or removing a Line Item, we should do this through the Order. Why is this useful? This concept provides a great way to maintain data consistency. For example, suppose we have a rule stating that an Order must have at least one Line Item. By enforcing all changes to go through the Order, we can easily enforce this rule.

Factories come into play when creating complex objects, like our Order Aggregate. For instance, let’s say that the process of creating an Order involves complex validation or setting default values. Instead of bloating the constructor of the Order class with this complexity, we can delegate the creation of an Order to a Factory.

Repositories

Repositories are used to store, retrieve, and delete aggregates. They provide the illusion of an in-memory collection of all objects of a certain type.

Strategic Design in DDD

Strategic design deals with the bigger picture, providing a way to deal with large models and teams. It includes:

  • Bounded Contexts: These are logical boundaries within a system where a particular model is defined and applicable. Each bounded context can be seen as a micro-universe within the system, having its own ubiquitous language, entities, value objects, etc.

  • Context Maps: These help to visualize and manage the relationships between different bounded contexts.

  • Distillation: This is the practice of identifying the core domain and separating it from supporting and generic subdomains, ensuring that the focus and resources are concentrated on the most valuable parts of the system.

DDD for Microservices

DDD can be particularly beneficial for microservices architecture. Bounded contexts align well with microservices, allowing each service to encapsulate a specific domain of the application. This encourages loose coupling between services and high cohesion within a service.

The use of ubiquitous language enhances communication between different teams working on different services, and repositories provide a neat way to manage persistence within a service. Aggregates ensure consistency within a service, and domain events can be used to maintain consistency between services.

Overall, DDD provides a set of valuable guidelines and practices for designing well-structured, maintainable, and business-centric software systems, especially in complex domains like e-commerce. It’s a journey of continuous learning, exploration, and refinement.

Thanks,
Onur Baskirt

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.