DEV Community

DevCorner2
DevCorner2

Posted on

🧱 Hexagonal Architecture in Spring Boot Microservices: A Complete Guide with Folder Structure

Hexagonal Architecture, also known as Ports and Adapters, is a powerful pattern for building modular, testable, and adaptable microservices. In this blog, we’ll walk through:

  • What Hexagonal Architecture is
  • How to structure your Spring Boot microservice using this pattern
  • Folder-by-folder explanation
  • Real-world code snippets
  • Why it matters

🧠 What Is Hexagonal Architecture?

Hexagonal Architecture is about keeping the core business logic independent from external systems like databases, HTTP servers, messaging systems, etc.

Your application should talk to the world through well-defined "ports", and the world connects to your application via "adapters".


πŸ–ΌοΈ Diagram: High-Level View

               +---------------------------+
               |    Inbound Adapters       |
               |  (REST, Kafka, GraphQL)   |
               +-------------+-------------+
                             |
                    +--------β–Ό--------+
                    |    Inbound Port  |
                    +--------+--------+
                             |
                   +---------β–Ό----------+
                   |   Application /    |
                   |     Use Cases      |
                   +---------+----------+
                             |
                    +--------β–Ό--------+
                    |  Domain Layer    |
                    +--------+--------+
                             |
                    +--------β–Ό--------+
                    |  Outbound Port   |
                    +--------+--------+
                             |
               +-------------β–Ό-------------+
               |     Outbound Adapters     |
               | (DB, REST APIs, Files, etc)|
               +---------------------------+
Enter fullscreen mode Exit fullscreen mode

🧳 Real-World Folder Structure

src
└── main
    └── java
        └── com.example.orderms
            β”œβ”€β”€ domain
            β”‚   β”œβ”€β”€ model
            β”‚   β”œβ”€β”€ repository         ← Outbound Ports
            β”‚   └── service
            β”‚
            β”œβ”€β”€ application
            β”‚   └── usecase            ← Inbound Ports
            β”‚
            β”œβ”€β”€ adapter
            β”‚   β”œβ”€β”€ inbound            ← REST, Kafka, etc.
            β”‚   └── outbound           ← DB, REST Clients, etc.
            β”‚
            β”œβ”€β”€ config                 ← Spring Configuration
            β”‚
            └── OrdermsApplication.java
Enter fullscreen mode Exit fullscreen mode

πŸ” Detailed Folder Breakdown

1. πŸ“¦ domain/ – Core Business Logic (Pure Java)

Contains your business entities, domain services, and interfaces that define what your application depends on. No Spring, no annotations β€” just clean domain logic.

βœ… Structure:

domain
β”œβ”€β”€ model
β”‚   └── Order.java
β”œβ”€β”€ repository
β”‚   └── OrderRepository.java
└── service
    └── OrderService.java
Enter fullscreen mode Exit fullscreen mode

βœ… Order.java

public class Order {
    private String id;
    private String productId;
    private int quantity;
    private OrderStatus status;
    // constructors, getters, setters
}
Enter fullscreen mode Exit fullscreen mode

βœ… OrderRepository.java

public interface OrderRepository {
    Order save(Order order);
    Optional<Order> findById(String id);
}
Enter fullscreen mode Exit fullscreen mode

This is a Port β€” we will create an adapter to implement it using JPA.


2. βš™οΈ application/usecase/ – Application Logic (Use Cases)

This layer orchestrates domain logic. It's the command center of the app, delegating to domain services or calling outbound ports.

βœ… Example:

application
└── usecase
    β”œβ”€β”€ PlaceOrderUseCase.java
    └── CancelOrderUseCase.java
Enter fullscreen mode Exit fullscreen mode

βœ… PlaceOrderUseCase.java

public interface PlaceOrderUseCase {
    Order execute(OrderRequestDTO request);
}
Enter fullscreen mode Exit fullscreen mode

βœ… PlaceOrderService.java

@Service
public class PlaceOrderService implements PlaceOrderUseCase {

    private final OrderRepository repository;

    public PlaceOrderService(OrderRepository repository) {
        this.repository = repository;
    }

    public Order execute(OrderRequestDTO request) {
        Order order = new Order(UUID.randomUUID().toString(), request.getProductId(), request.getQuantity(), OrderStatus.NEW);
        return repository.save(order);
    }
}
Enter fullscreen mode Exit fullscreen mode

This implements the Inbound Port PlaceOrderUseCase.


3. 🌐 adapter/inbound/ – Adapters That Call the App (REST, Kafka)

Contains external-facing logic that invokes use cases, such as HTTP controllers, Kafka consumers, CLI commands, etc.

βœ… Structure:

adapter
└── inbound
    └── rest
        β”œβ”€β”€ OrderController.java
        └── dto
            β”œβ”€β”€ OrderRequestDTO.java
            └── OrderResponseDTO.java
Enter fullscreen mode Exit fullscreen mode

βœ… OrderController.java

@RestController
@RequestMapping("/orders")
public class OrderController {

    private final PlaceOrderUseCase placeOrder;

    public OrderController(PlaceOrderUseCase placeOrder) {
        this.placeOrder = placeOrder;
    }

    @PostMapping
    public ResponseEntity<OrderResponseDTO> place(@RequestBody OrderRequestDTO request) {
        return ResponseEntity.ok(OrderMapper.toResponseDTO(placeOrder.execute(request)));
    }
}
Enter fullscreen mode Exit fullscreen mode

4. πŸ—ƒοΈ adapter/outbound/ – Adapters the App Calls (Database, APIs)

Implements Outbound Ports using Spring Data JPA, Feign, WebClient, etc.

βœ… Structure:

adapter
└── outbound
    └── persistence
        β”œβ”€β”€ OrderEntity.java
        β”œβ”€β”€ JpaOrderRepository.java
        β”œβ”€β”€ OrderMapper.java
        └── OrderPersistenceAdapter.java
Enter fullscreen mode Exit fullscreen mode

βœ… JpaOrderRepository.java

public interface JpaOrderRepository extends JpaRepository<OrderEntity, String> {
}
Enter fullscreen mode Exit fullscreen mode

βœ… OrderPersistenceAdapter.java

@Component
public class OrderPersistenceAdapter implements OrderRepository {

    private final JpaOrderRepository jpaRepo;

    public OrderPersistenceAdapter(JpaOrderRepository jpaRepo) {
        this.jpaRepo = jpaRepo;
    }

    @Override
    public Order save(Order order) {
        return OrderMapper.toDomain(jpaRepo.save(OrderMapper.toEntity(order)));
    }

    @Override
    public Optional<Order> findById(String id) {
        return jpaRepo.findById(id).map(OrderMapper::toDomain);
    }
}
Enter fullscreen mode Exit fullscreen mode

This adapter implements the outbound port from the domain.


5. πŸ› οΈ config/ – Spring Boot Configs

Wires beans and defines application-wide settings.

βœ… Structure:

config
β”œβ”€β”€ WebConfig.java
β”œβ”€β”€ AdapterConfig.java
└── SwaggerConfig.java
Enter fullscreen mode Exit fullscreen mode

βœ… AdapterConfig.java

@Configuration
public class AdapterConfig {

    @Bean
    public PlaceOrderUseCase placeOrderUseCase(OrderRepository repository) {
        return new PlaceOrderService(repository);
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”„ Data Flow in Action

User β†’ REST API β†’ UseCase β†’ Domain Logic β†’ Outbound Port β†’ Persistence Adapter β†’ DB


πŸ§ͺ Testing Strategy

Layer Test Type Tools
Domain Unit Tests JUnit, Mockito
UseCase Unit/Mock Tests JUnit
Adapters Integration TestContainers
Controller Web Test Spring MockMvc

βœ… Benefits of This Architecture

Feature Benefit
Separation of concerns Clear boundaries between logic and tech
Framework independence Domain is pure Java
Adapter pluggability Swap DB, APIs, UIs easily
Easier testing Domain logic tested without Spring
Scalability & maintainability Perfect for microservices

πŸ“¦ Bootstrap Project Template

I can generate this folder structure into a ready-to-run Maven or Gradle Spring Boot project, complete with:

  • Swagger
  • JPA
  • Flyway
  • Testcontainers
  • Profiles

Let me know if you want that.


🧠 Summary

Hexagonal architecture helps you design robust, clean, and future-proof microservices by enforcing clear boundaries. With Spring Boot, it's easy to adopt this pattern by structuring your app with:

  • Domain (core business logic)
  • Application (use cases)
  • Ports (interfaces)
  • Adapters (framework integrations)

If you’re building microservices in a modern, test-driven, and maintainable wayβ€”this structure will take you far.

Top comments (0)