Skip to content

Latest commit

 

History

History
251 lines (210 loc) · 8.61 KB

File metadata and controls

251 lines (210 loc) · 8.61 KB

Hexagonal Architecture Template

A production-ready hexagonal architecture template for reactive Spring Boot applications using R2DBC.

🚀 Quick Start

# 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 cases

📦 Architecture Overview

com.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

🎯 Key Principles

Dependency Rule

Adapters → Application → Domain

Dependencies always flow inward. Domain has no dependencies.

Layer Responsibilities

  • Domain: Pure business logic, entities, domain services
  • Application: Use cases, orchestration, defines ports
  • Adapters: External world interaction, implements ports
  • Infrastructure: Configuration, bean wiring, migrations

Correct Structure (Fixed)

✅ 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

🔄 Request Flow

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)

🛠️ Technology Stack

  • 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)

📋 Implementation Steps

1. Define Domain (Pure Business Logic)

// 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
  }
}

2. Define Ports (Interfaces)

// 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);
}

3. Implement Services (Orchestration)

@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);
  }
}

4. Create Adapters

// 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);
  }
}

🧪 Testing Strategy

// 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();

📚 Documentation Structure

File Purpose Priority
README.md Overview & quick start 🔴 Essential
ARCHITECTURE.md Detailed concepts & patterns 🟡 Important
IMPLEMENTATION.md Step-by-step guide 🟡 Important

🏗️ New Infrastructure Organization

The infrastructure layer has been completely refactored into specialized configuration categories:

Configuration Categories

  • config/ - Core application configuration (ObjectMapper, Validation, Reactor)
  • persistence/ - Database, transactions, migrations, ID generation
  • messaging/ - Kafka configuration, topics, serialization
  • cache/ - Redis configuration and caching policies
  • http/ - WebClient, observability, CORS, rate limiting
  • security/ - Authentication, authorization, data masking
  • observability/ - Metrics, tracing, logging configuration
  • resilience/ - Circuit breakers, retry policies, bulkheads
  • errorhandling/ - Global exception handling and error responses
  • profiles/ - Environment-specific configurations

Key Features

  • 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

✨ Benefits

  • 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

🎓 Next Steps

  1. Read this README for overview
  2. Study code examples in template
  3. Follow implementation steps
  4. Customize for your domain
  5. Write tests and deploy

Template Status: ✅ Production Ready
Architecture: Hexagonal (Ports & Adapters)
Pattern: Reactive with R2DBC
Java Version: 21 | Spring Boot: 3.5.3