Clean Hexagonal Architecture
Documents a Spring Boot project that implements Clean/Hexagonal Architecture with domain, application, and infrastructure layers for product management.
What this file does
Documents a Spring Boot project that implements Clean/Hexagonal Architecture with domain, application, and infrastructure layers for product management.
When to use it
- You are building a new Spring Boot service and want to enforce layer separation
- You need to explain Clean Architecture to a team with a concrete example
- You are refactoring a monolithic controller-heavy app into ports and adapters
- You want a reference for dependency inversion and mapper patterns in Java
Assumes this stack
Clean Hexagonal Architecture
This project demonstrates the implementation of Clean Architecture (also known as Hexagonal Architecture or Ports and Adapters) using Spring Boot for managing products.
Architecture Overview
Clean Architecture organizes code into layers with clear dependency rules:
- Dependencies point inward: Outer layers depend on inner layers, never the reverse
- Domain Layer: Contains business logic and is independent of frameworks
- Application Layer: Contains use cases that orchestrate domain objects
- Infrastructure Layer: Contains adapters for external systems (Web, DB, etc.)
Project Structure
src/main/java/com/example/product/
├── domain/ # Domain Layer (Enterprise Business Rules)
│ └── model/
│ ├── Product.java # Product aggregate root
│ └── Money.java # Money value object
│
├── application/ # Application Layer (Use Cases)
│ ├── dto/
│ │ ├── ProductCreateCommand.java # Input DTO for creating products
│ │ └── ProductResponse.java # Output DTO for product data
│ ├── port/
│ │ ├── in/
│ │ │ ├── CreateProductUseCase.java # Input port (use case interface)
│ │ │ └── GetProductQuery.java # Input port (query interface)
│ │ └── out/
│ │ └── ProductRepositoryPort.java # Output port (repository interface)
│ └── service/
│ └── ProductService.java # Use case implementation
│
└── infrastructure/ # Infrastructure Layer (Adapters)
├── adapter/
│ ├── in/
│ │ └── web/
│ │ └── ProductController.java # REST API adapter (Input)
│ └── out/
│ └── persistence/
│ ├── ProductJpaEntity.java # JPA entity
│ ├── ProductJpaRepository.java # Spring Data repository
│ ├── ProductMapper.java # Domain/Entity mapper
│ └── ProductRepositoryAdapter.java # Repository adapter (Output)
└── config/ # Spring configuration (if needed)
Layer Descriptions
1. Domain Layer
The innermost layer contains:
- Aggregates: Cluster of domain objects (e.g.,
Product) - Value Objects: Immutable objects defined by their values (e.g.,
Money) - Domain Services: Business logic that doesn't naturally fit in entities
Key Principles:
- No dependencies on outer layers
- No framework dependencies (no Spring, JPA annotations)
- Pure business logic
2. Application Layer
Contains application-specific business rules:
- Use Cases: Application services implementing business operations
- Ports: Interfaces defining contracts
- Input Ports: Interfaces for use cases (e.g.,
CreateProductUseCase) - Output Ports: Interfaces for external dependencies (e.g.,
ProductRepositoryPort)
- Input Ports: Interfaces for use cases (e.g.,
- DTOs: Data Transfer Objects for communication
Key Principles:
- Orchestrates domain objects to fulfill use cases
- Depends only on the domain layer
- Defines ports (interfaces) that infrastructure implements
3. Infrastructure Layer
The outermost layer contains:
- Input Adapters: Receive requests from external sources
- REST Controllers, GraphQL resolvers, CLI, etc.
- Output Adapters: Implement output ports to interact with external systems
- Database repositories, external APIs, message queues, etc.
- Configuration: Framework-specific configurations
Key Principles:
- Implements ports defined by application layer
- Contains all framework-specific code
- Depends on application and domain layers
Dependency Rule
The Dependency Rule states that source code dependencies must point only inward:
- Domain ← Application ← Infrastructure
This is achieved through:
- Dependency Inversion Principle: Application defines interfaces (ports), infrastructure implements them
- Dependency Injection: Spring injects implementations at runtime
Benefits
- Independence: Business logic is independent of frameworks, UI, databases
- Testability: Easy to test business rules without external dependencies
- Flexibility: Easy to change infrastructure without affecting business logic
- Maintainability: Clear separation of concerns makes code easier to understand and maintain
Key Patterns Used
Ports and Adapters
- Ports: Interfaces defining contracts (
CreateProductUseCase,ProductRepositoryPort) - Adapters: Implementations of ports (
ProductController,ProductRepositoryAdapter)
Dependency Inversion
- High-level modules (Application) don't depend on low-level modules (Infrastructure)
- Both depend on abstractions (Ports)
Mappers
- Convert between layers to maintain separation
ProductMapperconverts between domainProductand JPAProductJpaEntity
Example Flow
Creating a Product
- HTTP Request arrives at
ProductController(Infrastructure - Input Adapter) - Controller calls
CreateProductUseCase.createProduct()(Application - Port) - ProductService (Application - Use Case Implementation):
- Creates domain
ProductwithMoneyvalue object - Calls
ProductRepositoryPort.save()(Application - Port)
- Creates domain
- ProductRepositoryAdapter (Infrastructure - Output Adapter):
- Converts domain
ProducttoProductJpaEntityusingProductMapper - Saves via
ProductJpaRepository(Spring Data JPA) - Converts saved entity back to domain model
- Converts domain
- ProductService returns
ProductResponseDTO - Controller returns HTTP response
Running the Application
# Build the project
mvn clean install
# Run the application
mvn spring-boot:run
The application will start on http://localhost:8080
API Endpoints
Create a Product
POST /api/products
Content-Type: application/json
{
"name": "Laptop",
"description": "High-performance laptop",
"price": 999.99,
"currency": "USD"
}
Get a Product
GET /api/products/{id}
Database Console
H2 Console is available at: http://localhost:8080/h2-console
- JDBC URL:
jdbc:h2:mem:productdb - Username:
sa - Password: (empty)
Testing
The architecture makes testing straightforward:
- Unit Tests: Test domain logic in isolation
- Integration Tests: Test use cases with mock repositories
- End-to-End Tests: Test complete flows through controllers
Further Reading
What's inside
6 sections, 1 project tree, 3 layer descriptions, 1 example flow, 2 API endpoints, 1 test overview
Change this for your project
- Replace
com.example.productwith your own base package - Replace
Product,Money,ProductCreateCommand,ProductResponsewith your own domain and DTO names - Replace
jdbc:h2:mem:productdbwith your actual database connection string
Where it goes
Save in docs/ or the repository root. Gives agents and new contributors a map of the codebase.
Worth borrowing
- Define input and output port interfaces in the application layer, not the infrastructure layer
- Use a dedicated mapper class to convert between domain models and JPA entities
Related Documents
Design Document: BharatSeva AI
Describes a 10-agent AWS system that helps India's informal workers access government schemes via voice-first, serverless architecture.
OpenClaw Enterprise Transformation Plan
Transforms a single-user AI agent into a dual-mode platform supporting both viral open-source and Fortune 500 enterprise deployments through phased security, IAM, audit, multi-tenancy, and Kubernetes features.
Qwen Image and Edit: Open-sourcing and Local GGUF Generations with Lightning
Documents the Qwen-Image and Qwen-Image-Edit models, covering architecture, training, benchmarks, ComfyUI setup, and prompting techniques for local GGUF deployment.
University of Guelph Rocketry Club - Complete Tech Stack
Documents the full tech stack of a university rocketry club website with AI chatbot, member management, and project showcases.