A production-ready hexagonal architecture template for reactive Spring Boot applications using R2DBC.
# 1. Copy template
cp -r com/example/payments src/main/java/io/f8a/yourmodule
# 2. Update package names
find src/main/java/io/f8a/yourmodule -type f -name "*.java" \
-exec sed -i '' 's/com.example.payments/io.f8a.yourmodule/g' {} +
# 3. Customize domain models and implement your use casescom.example.payments/
βββ domain/ # π΅ Pure Business Logic (No dependencies)
β βββ model/ # Domain entities with business rules
β βββ service/ # Complex business logic
β
βββ application/ # π’ Use Cases & Orchestration
β βββ ports/in/ # Input ports (what app does)
β βββ ports/out/ # Output ports (what app needs)
β βββ handler/ # Request handlers
β βββ service/ # Use case implementations
β
βββ adapter/ # π‘ External World
β βββ in/ # Inbound (web, kafka, jobs)
β β βββ web/
β β βββ dto/ # β
HTTP DTOs belong here
β β βββ mapper/ # β
HTTP mappers belong here
β β βββ PaymentController.java
β βββ out/ # Outbound (db, clients, kafka)
β βββ db/
β β βββ entity/ # β
DB entities organized
β β βββ mapper/ # β
DB mappers organized
β β βββ PaymentPersistenceAdapter.java
β βββ restclient/
β βββ dto/ # β
External service DTOs
β βββ mapper/ # β
External service mappers
β
βββ infrastructure/ # π£ Configuration ONLY
βββ config/ # Core application configuration
βββ persistence/ # Database & transaction configuration
βββ messaging/ # Kafka & event streaming
βββ cache/ # Redis & caching policies
βββ http/ # WebClient & HTTP configuration
βββ security/ # Security & authentication
βββ observability/ # Monitoring & tracing
βββ resilience/ # Fault tolerance patterns
βββ errorhandling/ # Global error handling
βββ profiles/ # Environment configurations
Adapters β Application β Domain
Dependencies always flow inward. Domain has no dependencies.
- Domain: Pure business logic, entities, domain services
- Application: Use cases, orchestration, defines ports
- Adapters: External world interaction, implements ports
- Infrastructure: Configuration, bean wiring, migrations
β DTOs and Mappers belong to Adapters (not Infrastructure)
- HTTP DTOs β
adapter/in/web/dto/ - DB entities β
adapter/out/db/entity/ - External DTOs β
adapter/out/restclient/dto/ - Infrastructure β Configuration ONLY
HTTP POST /api/v1/payments
β
PaymentController (adapter/in/web)
β
CreatePaymentRequestHandler (application/handler)
β
CreatePaymentService (application/service)
ββ PaymentDomainService (domain/service) - Validation
ββ SavePaymentPort β PaymentPersistenceAdapter (adapter/out/db)
ββ PublishEventPort β PaymentEventProducer (adapter/out/kafka)
- Java 21 + Spring Boot 3.5.3 (Reactive WebFlux)
- R2DBC (Reactive database) + PostgreSQL
- Project Reactor (Reactive programming)
- MapStruct (Object mapping) + Lombok
- Kafka (Event streaming) + Redis (Caching)
- Flyway (Database migrations)
// domain/model/Payment.java
@Data @Builder
public class Payment {
private String id;
private BigDecimal amount;
// Business rules as methods
public boolean canBeCancelled() {
return status == PaymentStatus.PENDING;
}
}
// domain/service/PaymentDomainService.java
public class PaymentDomainService {
public ValidationResult validate(Payment payment) {
// Pure business validation logic
}
}// Input ports (what app does)
public interface CreatePaymentUseCase {
Mono<Payment> createPayment(CreatePaymentCommand command);
}
// Output ports (what app needs)
public interface SavePaymentPort {
Mono<Payment> save(Payment payment);
}@Service
@RequiredArgsConstructor
public class CreatePaymentService implements CreatePaymentUseCase {
private final PaymentDomainService domainService;
private final SavePaymentPort savePaymentPort;
public Mono<Payment> createPayment(CreatePaymentCommand command) {
return buildPayment(command)
.flatMap(domainService::validate)
.flatMap(savePaymentPort::save);
}
}// Inbound: REST Controller
@RestController
public class PaymentController {
@PostMapping("/payments")
public Mono<ApiResponse<PaymentResponse>> create(@RequestBody CreatePaymentRequest req) {
return handler.handle(req).map(ApiResponse::success);
}
}
// Outbound: Database Adapter
@Component
public class PaymentPersistenceAdapter implements SavePaymentPort {
public Mono<Payment> save(Payment payment) {
return repository.save(toEntity(payment)).map(this::toDomain);
}
}// Domain (Pure unit tests)
PaymentDomainService service = new PaymentDomainService();
assertThat(service.validate(payment).isValid()).isTrue();
// Application (Mock ports)
@Mock SavePaymentPort savePort;
StepVerifier.create(service.createPayment(command))
.assertNext(payment -> assertThat(payment).isNotNull())
.verifyComplete();
// E2E (Full stack)
webClient.post().uri("/api/v1/payments")
.exchange().expectStatus().isOk();| File | Purpose | Priority |
|---|---|---|
| README.md | Overview & quick start | π΄ Essential |
| ARCHITECTURE.md | Detailed concepts & patterns | π‘ Important |
| IMPLEMENTATION.md | Step-by-step guide | π‘ Important |
The infrastructure layer has been completely refactored into specialized configuration categories:
config/- Core application configuration (ObjectMapper, Validation, Reactor)persistence/- Database, transactions, migrations, ID generationmessaging/- Kafka configuration, topics, serializationcache/- Redis configuration and caching policieshttp/- WebClient, observability, CORS, rate limitingsecurity/- Authentication, authorization, data maskingobservability/- Metrics, tracing, logging configurationresilience/- Circuit breakers, retry policies, bulkheadserrorhandling/- Global exception handling and error responsesprofiles/- Environment-specific configurations
- Production Ready: Comprehensive observability, security, and resilience
- Environment Management: Separate configs for dev/staging/prod
- Fault Tolerance: Circuit breakers, retries, timeouts, bulkheads
- Security: JWT authentication, data masking, CORS protection
- Monitoring: Metrics, distributed tracing, structured logging
- Caching: Redis integration with configurable policies
- Error Handling: RFC 7807 Problem Details with correlation IDs
- Testability: Test each layer independently
- Flexibility: Swap implementations easily (DB, API, messaging)
- Maintainability: Clear separation of concerns
- Scalability: Reactive programming for high throughput
- Production Ready: Enterprise-grade configuration and monitoring
- Organized: Specialized configuration categories for better maintenance
- Read this README for overview
- Study code examples in template
- Follow implementation steps
- Customize for your domain
- Write tests and deploy
Template Status: β
Production Ready
Architecture: Hexagonal (Ports & Adapters)
Pattern: Reactive with R2DBC
Java Version: 21 | Spring Boot: 3.5.3