Domain-Driven Design 🎯

DDD is about making your code speak the same language as your business. When a product manager says "order," your code should have an Order — not a DataRecord or a TransactionEntity. When the business process changes, the code should change in the same place and in the same terms.

Where Clean and Hexagonal architectures tell you how to structure your application, DDD tells you what to put inside — how to model your domain so it reflects reality rather than database schemas.

Key Elements

  • Application layer — Contains business logic corresponding to use cases, coordinating interactions between the domain layer and the infrastructure layer.
public class OrderUseCase {
    @Transactional
    public void createOrder(@Valid Order order) {
        // paymentService.validate(order);
        orderRepository.create(order);
        shippingContext.prepare(order);
    }
}
  • Ubiquitous Language - Use the language of the business in the code
  public Customer findProductByUPC(String upc){
        // Find product by UPC
        }
  • Bounded Context - The model applies to a specific context
// I'm doing one thing "Shipping" but doing it well.
public class ShippingContext {
 
    public void ship(Order order) {
        // Shipping logic
    }
 
}
  • Value Objects - Immutable objects identified by attributes
// Reusable objects across entities
record Money(BigDecimal amount, String currency) {
}
  • Entities - Domain objects with unique identity
// Object having meaningful representation for the domain
record Warehouse(String warehouseId, List<Inventory> inventories) {
}
  • Aggregates - Cluster of domain objects that belong together
// Aggregate of entities required by a specific context
record CustomerAggregate(Customer customer, List<Address> addresses, List<Order> orders) {
}

Project structure

  • 📁 interfaces
    • 📄 OrderController.java (Entrypoint)
  • 📁 application
    • 📄 OrderUseCase.java
  • 📁 domain
    • 📁 customers
      • 📄 Address.java
      • 📄 Customer.java
    • 📁 orders
      • 📄 Order.java
      • 📄 OrderService.java
      • 📄 OrderRepository.java (Interface)
  • 📁 infrastructure
    • 📁 repositories
      • 📄 InMemoryRepository.java
      • 📁 orders
        • 📄 RemoteOrderRepository.java
        • 📄 LocalOrderRepository.java
    • 📁 logging
    • 📁 security
  • OrderRepository belongs in domain because it works with domain objects without knowledge of any database or storage.

When DDD pays off

DDD adds value when the business domain is genuinely complex — multiple bounded contexts, non-trivial rules, evolving requirements. For a CRUD application with simple data flows, the overhead of aggregates, value objects, and bounded contexts doesn't justify itself.

The biggest trap is applying DDD vocabulary without the substance. Having a class called OrderAggregate that's really just a data container doesn't make your architecture domain-driven — it makes it over-labeled. DDD is a mindset about understanding the business first, then modeling it in code. If you skip the understanding part, the structure won't save you.

Sources