Tactical Domain-Driven Design: Patterns for Complex Systems

Tactical Domain-Driven Design: Patterns for Complex Systems

Welcome back to our Domain-Driven Design (DDD) series! In our previous discussions, we introduced DDD’s core concepts and strategic design principles. Now, it’s time to get tactical by exploring key patterns that help us model complex domains effectively.



Core Building Blocks in DDD

At the heart of Tactical DDD are three fundamental concepts:

? Entities – Objects with a unique identity that persists over time.

? Value Objects – Immutable objects defined by their attributes, not identity.

? Aggregates – Groups of related objects that form a consistency boundary.

Let’s break them down with some examples!



1. Entities – Objects with Identity

An Entity is an object that has a unique identity and a lifecycle. Even if its attributes change, its identity remains the same.

  • Example: Customer Entity

In an e-commerce system, a Customer is an entity because it has a unique ID, and its details (name, email) can change over time.

@Getter
@Setter
public class Customer {

    private final Long id;  // Unique identity
    
    private String name;

    private String email;

    public Customer(Long id, String name, String email) {

        this.id = id;

        this.name = name;

        this.email = email;

       }
}        


When to Use Entities?

?? Use Entities when an object has a unique identity that must persist over time.

?? Ideal for objects like Users, Orders, Employees, Accounts, etc.



2. Value Objects – Immutable & Identity-Free

Unlike entities, Value Objects do not have a unique identity. Instead, they are defined by their attributes and should be immutable.

  • Example: Money as a Value Object

Money is a perfect example of a Value Object because $100 USD is equal to another $100 USD, regardless of where it comes from.

@Getter
public final class Money {

    private final double amount;

    private final String currency;

    public Money(double amount, String currency) {

        this.amount = amount;

        this.currency = currency;

    }


    // Example of a method to add amounts

    public Money addAmount(Money other) {

        if (!this.currency.equals(other.currency)) {

            throw new IllegalArgumentException("Currency mismatch");

        }

        return new Money(this.amount + other.amount, this.currency);

       }
}        


Key Characteristics of Value Objects

?? No unique identity – They are compared by their attributes, not a unique ID.

?? Immutable – Once created, their state cannot change.

?? Reusable – Used to represent concepts like money, addresses, coordinates, dates.

---

3. Aggregates – A Cluster of Related Objects

An Aggregate is a collection of related Entities and Value Objects that form a single unit of consistency. The Aggregate Root ensures all business rules are enforced.

  • Example: Order Aggregate

In an e-commerce system, an Order consists of multiple OrderItems. Instead of allowing direct modification of OrderItems, we define Order as an Aggregate Root to manage them safely.


// Order Aggregate Root

import java.util.ArrayList;

import java.util.List;

public class Order {

    private final String id;

    private final String customerId;

    private final List<OrderItem> items = new ArrayList<>();

    public Order(String id, String customerId) {

        this.id = id;

        this.customerId = customerId;

    }

    public void addItem(String productId, int quantity) {

        items.add(new OrderItem(productId, quantity));

    }public List<OrderItem> getItems() {

        return new ArrayList<>(items); // Defensive copy

       }
}        


@Getter
@Setter
public class OrderItem {

    private final String productId;

    private final int quantity;

    public OrderItem(String productId, int quantity) {

        this.productId = productId;

        this.quantity = quantity;

      }
}        


Why Use Aggregates?

?? Ensures consistency – The Aggregate Root controls changes to its members.

?? Reduces complexity – Provides a clear structure for handling related objects.

?? Improves performance – Prevents unnecessary database queries by managing related data together.



When to Use What?

  • Entity: Objects with unique identity that persists over time (e.g., Customer, Order)
  • Value Object: Immutable objects without identity (e.g., Money, Address)
  • Aggregate: Groups of related objects for consistency (e.g., Order & OrderItem)



Conclusion

In this article, we explored the tactical design patterns that form the foundation of Domain-Driven Design (DDD) in Java:

? Entities represent objects with a unique identity.

? Value Objects are immutable and defined by their attributes.

? Aggregates group related entities to ensure consistency.

By applying these principles, we can better structure complex domains and make our business logic more maintainable, scalable, and testable.

Next Up: In the next article, we’ll explore Repositories and Unit of Work—patterns that help us manage persistence and transactions seamlessly.

?? Stay tuned, and happy coding! ????

Igor Venturelli

Back-end Software Engineer | Software Integration | API Security | OAuth2 | Content Creator

1 个月

Thank you for this article! I am not that proficient on DDD and this was crystal clear for me! Thanks a lot!

Amir Goalmoradi

Backend Developer | Java | Spring | DDD | Hexagonal | Clean Architecture | TDD | SQL/NoSQL | Docker

1 个月

Great news Everyone - the next part is now available! You can find it here: https://www.dhirubhai.net/pulse/repository-unit-work-domain-driven-design-ddd-amir-goalmoradi-zvp4f/?trackingId=DapfJPZAQFmZzjzIbuxyAA%3D%3D ?? Looking forward to hearing your thoughts on the next installment! ??

回复
Ali Bayat

Senior .NET Developer | C#, ASP.NET Core, EF Core | Microservices, Azure

1 个月

Clear, concise, & practical :) A gr8 breakdown of DDD with solid examples. Looking forward to the next one ??

要查看或添加评论,请登录

Amir Goalmoradi的更多文章

社区洞察

其他会员也浏览了