Why Many Spring Boot Projects Outgrow Their Initial Architecture: The Value of DDD and Hexagonal Design

Page content

Most Spring Boot projects start in a very similar way:

  • Controller layer
  • Service layer
  • Repository layer

This layered structure is simple, familiar, and works extremely well for small and medium-sized applications.

However, as systems grow, teams often discover that this structure alone is not enough to handle increasing complexity.

The problem is not that the architecture is “wrong” — but that it is often implicit rather than explicitly designed.


The Reality of Many Spring Boot Projects

A typical project evolves like this:

  1. Start with a simple CRUD application
  2. Add business logic in services
  3. Introduce integrations (REST, messaging, external APIs)
  4. Add more domains and workflows
  5. Complexity increases organically

At this point, the structure still looks like:

Controller → Service → Repository

But internally, the service layer often becomes:

  • too large
  • too coupled
  • responsible for multiple concerns
  • hard to test in isolation

This is where architectural design patterns start to matter.


The Limitations of the Layered Architecture

The layered approach is not inherently bad, but it has limitations:

1. Business Logic Becomes Hidden in Services

Services often turn into “god classes” containing:

  • business rules
  • orchestration logic
  • integration calls
  • data transformations

This makes the system harder to understand and maintain.


2. Tight Coupling to Frameworks

Business logic is often directly tied to:

  • Spring annotations
  • JPA entities
  • REST controllers

This reduces flexibility and testability.


3. Difficult Domain Evolution

When requirements change, modifying business logic often requires changes across multiple layers.

There is no clear boundary between:

  • domain logic
  • application logic
  • infrastructure concerns

Enter DDD: Focusing on the Domain

Domain-Driven Design (DDD) shifts the focus from layers to business capability modeling.

Instead of organizing code by technical role, you organize it by domain concepts:

  • Order
  • Payment
  • Inventory
  • User

Key idea:

The domain model is the center of the system.

DDD helps you:

  • express business rules clearly
  • isolate domain logic from infrastructure
  • align code with real-world concepts

A well-designed domain model becomes stable even when infrastructure changes.


Hexagonal Architecture: Protecting the Core

Hexagonal Architecture (also known as Ports and Adapters) complements DDD by structuring dependencies correctly.

Instead of:

Controller → Service → Repository

you design:

    [ Adapter: REST Controller ]
               ↓
    [ Application / Use Cases ]
               ↓
    [ Domain Model ]
               ↑
    [ Adapter: DB / Messaging / External APIs ]

Core idea:

The domain should not depend on external systems.


Why This Matters in Spring Boot Projects

Spring Boot makes it very easy to:

  • inject dependencies everywhere
  • mix infrastructure with business logic
  • quickly build working systems

This is powerful — but also dangerous at scale.

Without clear architectural boundaries:

  • business logic leaks into controllers or repositories
  • services become orchestration layers with mixed responsibilities
  • testing becomes tightly coupled to Spring context

DDD + Hexagonal Architecture help to:

✔ isolate business logic
✔ reduce framework dependency
✔ improve testability
✔ make systems easier to evolve


A Practical Example

Instead of:

@Service
public class OrderService {

    public void createOrder(OrderDto dto) {
        Order order = new Order(dto);
        orderRepository.save(order);
        paymentClient.charge(order);
    }
}

You move toward:

a domain model (Order) an application service (use case) ports for external systems

Example:

public class CreateOrderUseCase {

    private final OrderRepository orderRepository;
    private final PaymentPort paymentPort;

    public void execute(CreateOrderCommand command) {
        Order order = Order.create(command);
        orderRepository.save(order);
        paymentPort.charge(order);
    }
}

Now:

  • domain logic is explicit
  • dependencies are controlled
  • infrastructure is replaceable

When Should You Introduce DDD / Hexagonal?

You don’t need it from day one.

It becomes valuable when:

  • the system has multiple business domains
  • complexity starts growing in service layer
  • multiple integrations exist
  • testing becomes painful
  • changes start affecting many parts of the codebase

Final Thoughts

Most Spring Boot projects do not start with a “bad architecture”.

They start simple — and correctly so.

The issue is that as complexity grows, the architecture often does not evolve at the same pace.

DDD and Hexagonal Architecture are not over-engineering tools.

They are evolutionary design approaches that help systems stay maintainable as they grow.


Key Takeaways

  • Layered architecture is a good starting point
  • Complexity accumulates in service layers over time
  • DDD helps model business complexity explicitly
  • Hexagonal Architecture protects the domain from infrastructure
  • The goal is evolutionary architecture, not upfront perfection