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