Seed project for Spring Boot 3 with complete Hexagonal Architecture, including examples of all main components.
This project implements Hexagonal Architecture (also known as Ports & Adapters), which allows:
- ✅ Domain independence from frameworks
- ✅ Easy testing
- ✅ Flexibility to change implementations
- ✅ Clear separation of concerns
src/main/java/gov/justucuman/seed/
├── domain/ # Domain Layer (business core)
│ ├── model/ # Domain entities
│ │ ├── Product.java # Core entity (Java Record)
│ │ └── ProductStatus.java # Enum with custom parsing
│ └── port/ # Port interfaces
│ ├── input/ # Input ports (use cases)
│ │ ├── CreateProduct.java
│ │ ├── GetAllProduct.java
│ │ ├── GetProductById.java
│ │ ├── SearchProduct.java
│ │ ├── IndexProduct.java
│ │ ├── UpdateProduct.java
│ │ ├── DeleteProductById.java
│ │ ├── GetExternalProductById.java
│ │ └── GetAllExternalProduct.java
│ └── output/ # Output ports (repositories, events)
│ ├── ProductSavePort.java
│ ├── ProductFindByIdPort.java
│ ├── ProductFindAllPort.java
│ ├── ProductDeleteByIdPort.java
│ ├── ProductSearchPort.java
│ ├── ProductEventPublisherPort.java
│ └── ExternalProductPort.java
│
├── application/ # Application Layer (orchestration)
│ ├── usecase/ # Use case implementations
│ │ ├── CreateProductUseCase.java
│ │ ├── GetAllProductUseCase.java
│ │ ├── GetProductByIdUseCase.java
│ │ ├── SearchProductUseCase.java
│ │ ├── IndexProductUseCase.java
│ │ ├── UpdateProductUseCase.java
│ │ ├── DeleteProductByIdUseCase.java
│ │ ├── GetExternalProductByIdUseCase.java
│ │ └── GetAllExternalProductUseCase.java
│ └── exception/ # Application exceptions
│ └── ProductNotFoundException.java
│
├── infrastructure/ # Infrastructure Layer (adapters)
│ ├── adapter/
│ │ ├── input/ # Input adapters
│ │ │ ├── rest/ # REST API Controllers
│ │ │ │ ├── CreateProductController.java
│ │ │ │ ├── GetAllProductController.java
│ │ │ │ ├── GetProductByIdController.java
│ │ │ │ ├── SearchProductController.java
│ │ │ │ ├── UpdateProductController.java
│ │ │ │ ├── DeleteProductByIdController.java
│ │ │ │ ├── GetExternalProductByIdController.java
│ │ │ │ └── GetAllExternalProductController.java
│ │ │ ├── dto/ # Request/Response DTOs
│ │ │ │ ├── CreateProductRequest.java
│ │ │ │ ├── ProductResponse.java
│ │ │ │ └── UpdateProductRequest.java
│ │ │ ├── mapper/ # REST mappers
│ │ │ │ ├── CreateProductMapper.java
│ │ │ │ └── GetProductMapper.java
│ │ │ └── event/ # Kafka Consumer
│ │ │ └── ProductKafkaConsumer.java
│ │ │
│ │ └── output/ # Output adapters
│ │ ├── persistence/ # Persistence Adapters
│ │ │ ├── jpa/ # JPA Implementation
│ │ │ │ ├── ProductSaveJpaAdapter.java
│ │ │ │ ├── ProductFindByIdJpaAdapter.java
│ │ │ │ ├── ProductFindAllJpaAdapter.java
│ │ │ │ ├── entity/ProductEntity.java
│ │ │ │ └── repository/ProductJpaRepository.java
│ │ │ └── jdbc/ # JDBC Template Implementation
│ │ │ ├── ProductDeleteJdbcAdapter.java
│ │ │ └── ProductRowMapper.java
│ │ ├── search/ # OpenSearch Integration
│ │ │ ├── ProductOpenSearchAdapter.java
│ │ │ ├── ProductDocumentMapper.java
│ │ │ └── ProductDocument.java
│ │ ├── event/ # Kafka Event Publisher
│ │ │ ├── ProductKafkaPublisherAdapter.java
│ │ │ ├── ProductEventMapper.java
│ │ │ └── ProductEvent.java
│ │ └── external/ # External API Integration
│ │ ├── ProductWebClientAdapter.java
│ │ ├── ExternalProductMapper.java
│ │ └── ExternalProductResponse.java
│ │
│ └── config/ # Infrastructure Configurations
│ ├── OpenApiConfig.java
│ ├── KafkaConfig.java
│ ├── OpenSearchConfig.java
│ └── WebClientConfig.java
│
└── common/ # Common Components
├── error/ # Error Handling
│ ├── Error.java
│ ├── ErrorDetail.java
│ └── ErrorResponse.java
├── constants/ # Application Constants
│ └── ProblemType.java
└── util/ # Utility Classes
└── DateUtils.java
- Java: 21 (with Records for immutable DTOs)
- Spring Boot: 3.5.3
- Spring Framework: 6.x (included in Spring Boot)
- Spring Data JPA: 3.4.x
- JDBC Template: Included in Spring Boot
- PostgreSQL Driver: Runtime dependency
- Flyway: 11.10.1
- Spring Kafka: 3.3.1
- Auto-created Kafka topics
- OpenSearch Java Client: 3.2.0
- Spring WebFlux: (for WebClient)
- Reactive programming: Support
- MapStruct: 1.6.3 (compatible with Records)
- Lombok: (Reduced boilerplate)
- Spring Validation
- JUnit 5: Jupiter (included in Spring Boot)
- Spring Boot Test: 3.5.3
- Reactor Test: (for WebFlux)
- SpringDoc OpenAPI: 2.8.13 (Swagger UI)
- ✅ Domain Layer: Pure business logic with ports and models
- ✅ Application Layer: Use case implementations
- ✅ Infrastructure Layer: Adapters for external integrations
- ✅ Common Layer: Shared components and utilities
- ✅ JPA: Standard CRUD operations with Spring Data
- ✅ JDBC Template: Complex queries and custom mapping
- ✅ Flyway: Database migrations and versioning
- ✅ OpenSearch Integration: Full-text search with field weighting
- ✅ Multi-match queries: Search across multiple fields
- ✅ Parameter-based filtering: Dynamic search criteria
- ✅ Kafka Integration: Event publishing and consumption
- ✅ Auto-created topics: Automatic topic management
- ✅ Event DTOs: Structured event data
- ✅ WebClient: Reactive HTTP client for external APIs
- ✅ FakeStore API: Example external product integration
- ✅ Error Handling: Comprehensive external API error management
- ✅ Java Records: Immutable DTOs and entities
- ✅ Java 21: Latest language features
- ✅ MapStruct: Type-safe mapping with record support
- ✅ Spring Validation: Jakarta Bean Validation
- ✅ Global Exception Handler: Centralized error management
- ✅ Custom Error Responses: Structured error information
- ✅ Problem Types: Standardized error categorization
- ✅ WebFlux Integration: Non-blocking I/O for external APIs
- ✅ Reactor Patterns: Reactive stream processing
- ✅ Backpressure Handling: Resilient reactive streams
- ✅ OpenAPI 3.0: Comprehensive API documentation
- ✅ Swagger UI: Interactive API exploration
- ✅ Auto-generated Docs: Always up-to-date documentation
- ✅ Profile-based configs: Separate configurations per environment
- ✅ Environment-specific properties: Local, dev, test, production
- ✅ Flexible deployment: Configurable for different environments
- ✅ Single Responsibility: Each class has one purpose
- ✅ Open/Closed: Extensible without modification
- ✅ Liskov Substitution: Subtypes replaceable by base types
- ✅ Interface Segregation: Fine-grained interfaces
- ✅ Dependency Inversion: Depend on abstractions, not concretions
- Java 21 or higher
- Maven 3.9+
- Docker and Docker Compose (for local infrastructure)
- PostgreSQL (or use Docker)
- Kafka (or use Docker)
- OpenSearch (or use Docker)
git clone <repository-url>
cd seed-gradle-kotlinCreate a docker-compose.yml file in the project root:
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: seed-postgres
environment:
POSTGRES_DB: seed_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
kafka:
image: confluentinc/cp-kafka:7.6.0
container_name: seed-kafka
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
depends_on:
- zookeeper
zookeeper:
image: confluentinc/cp-zookeeper:7.6.0
container_name: seed-zookeeper
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ports:
- "2181:2181"
opensearch:
image: opensearchproject/opensearch:2.12.0
container_name: seed-opensearch
environment:
- discovery.type=single-node
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
- "DISABLE_SECURITY_PLUGIN=true"
ports:
- "9200:9200"
- "9600:9600"
volumes:
- opensearch_data:/usr/share/opensearch/data
volumes:
postgres_data:
opensearch_data:docker-compose up -d./gradlew clean build./gradlew bootRunThe application will be available at: http://localhost:8080
This project supports multiple configuration profiles:
./gradlew bootRun- Uses local configuration from
application-local.yml - Default profile for development
./gradlew bootRun --args='--spring.profiles.active=dev'- Uses development configuration from
application-dev.yml
./gradlew bootRun --args='--spring.profiles.active=test'- Uses test configuration from
application-test.yml
# Create Product
POST /api/v1/products
Content-Type: application/json
{
"name": "Laptop Dell XPS 15",
"description": "High performance laptop",
"price": 1299.99,
"stock": 10,
"category": "Electronics"
}
# Get Product by ID
GET /api/v1/products/{id}
# Get All Products
GET /api/v1/products
# Update Product
PUT /api/v1/products/{id}
Content-Type: application/json
{
"name": "Updated Product",
"description": "Updated description",
"price": 1499.99,
"stock": 15,
"category": "Electronics"
}
# Delete Product
DELETE /api/v1/products/{id}# Search Products (OpenSearch with multi-field search)
GET /api/v1/products/search?query=laptop
# Search with parameter-based filtering
GET /api/v1/products/search?query=electronics&minPrice=100&maxPrice=1000# Get External Product by ID
GET /api/v1/external/products/{id}
# Get All External Products
GET /api/v1/external/products- Product Creation: Events published to
product-eventstopic - Event Structure: Contains product data with timestamps
- Auto-created Topics: Kafka topics created automatically
Note: This project currently has minimal test coverage. The testing framework is configured but comprehensive tests need to be implemented.
SeedApplicationTests.java: Basic Spring context loading test
# Run all tests
./gradlew test
# Run tests with coverage report
./gradlew test jacocoTestReportThe following test types should be implemented:
- ✅ Use Case Tests: Test all business logic in use cases
- ✅ Service Tests: Test application layer components
- ✅ Repository Tests: Test persistence layer with in-memory database
- ✅ Adapter Tests: Test individual adapters in isolation
- ✅ Mapper Tests: Test MapStruct mappers
- ✅ Controller Tests: Test REST endpoints with MockMvc
- ✅ Database Integration: Test JPA and JDBC adapters
- ✅ Kafka Integration: Test event publishing and consumption
- ✅ OpenSearch Integration: Test search functionality
- ✅ External API Integration: Test WebClient adapters
- ✅ Full Workflow Tests: Test complete request flows
- ✅ Container Tests: Test with Testcontainers
- ✅ API Contract Tests: Test OpenAPI contracts
- JUnit 5: Testing framework
- Spring Boot Test: Spring testing utilities
- Mockito: Mocking framework
- Testcontainers: Integration testing with real containers
- Reactor Test: Testing reactive components
1. REST Request → CreateProductController (Input Adapter)
2. CreateProductController → CreateProductMapper (DTO → Domain)
3. CreateProductMapper → CreateProductUseCase (Application Layer)
4. CreateProductUseCase → ProductSavePort (Output Port)
5. ProductSaveJpaAdapter → JPA Repository (Persistence)
6. CreateProductUseCase → ProductSearchPort (Output Port)
7. ProductOpenSearchAdapter → OpenSearch (Search Index)
8. CreateProductUseCase → ProductEventPublisherPort (Output Port)
9. ProductKafkaPublisherAdapter → Kafka Topic (Event Publish)
10. Response ← Product created successfully
1. Search Request → SearchProductController (Input Adapter)
2. SearchProductController → SearchProductUseCase (Application Layer)
3. SearchProductUseCase → ProductSearchPort (Output Port)
4. ProductOpenSearchAdapter → OpenSearch (Multi-field search with weighting)
5. ProductDocumentMapper → Domain Objects
6. Response ← Search results
1. External Request → GetExternalProductController (Input Adapter)
2. GetExternalProductController → GetExternalProductByIdUseCase (Application Layer)
3. GetExternalProductByIdUseCase → ExternalProductPort (Output Port)
4. ProductWebClientAdapter → FakeStore API (Reactive WebClient)
5. ExternalProductMapper → Domain Objects
6. Response ← External product data
1. Kafka Topic → ProductKafkaConsumer (Input Adapter)
2. ProductKafkaConsumer → Process Event
3. Business Logic → Actions based on event type
Migrations are located in: src/main/resources/db/migration/
- V1_1_0__create_product_table.sql: Creates products table with UUID primary key
CREATE TABLE product (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(19,2) NOT NULL,
stock INTEGER DEFAULT 0,
category VARCHAR(100),
status VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);./gradlew flywayMigrate./gradlew flywayCleanSpring Boot Actuator is available at:
- Health: http://localhost:8080/actuator/health
- Info: http://localhost:8080/actuator/info
- Metrics: http://localhost:8080/actuator/metrics
- Domain Layer: Pure business logic, no external dependencies
- Ports: Interfaces that define contracts between layers
- Adapters: Concrete implementations of ports for external systems
- Granular Use Cases: Each use case handles one specific business operation
- Clear Separation: Input/Output ports separate concerns
- Testable Architecture: Easy to mock and test individual components
- ✅ Single Responsibility: Each class has one reason to change
- ✅ Open/Closed: Open for extension, closed for modification
- ✅ Liskov Substitution: Subtypes are replaceable by base types
- ✅ Interface Segregation: Client-specific interfaces
- ✅ Dependency Inversion: Depend on abstractions, not concretions
- ✅ Adapter Pattern: Integration with external systems
- ✅ Repository Pattern: Data access abstraction
- ✅ Publisher-Subscriber Pattern: Event-driven communication
- ✅ Strategy Pattern: Multiple persistence strategies
- ✅ Facade Pattern: Simplified external API access
The application supports multiple configuration profiles:
# application-local.yml
spring.profiles.active: local# application-dev.yml
spring.profiles.active: dev# application-test.yml
spring.profiles.active: test# Default (local)
./gradlew bootRun
# Development
./gradlew bootRun --args='--spring.profiles.active=dev'
# Test
./gradlew bootRun --args='--spring.profiles.active=test'./gradlew bootBuildImagedocker run -p 8080:8080 seed-gradle-kotlin:1.0.0-SNAPSHOTUse the provided docker-compose.yml to run all required services:
# Start all infrastructure services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down# Clean and build
./gradlew clean build
# Run application
./gradlew bootRun
# Run tests
./gradlew test
# Generate test coverage report
./gradlew test jacocoTestReport
# Check dependencies
./gradlew dependencies
# Build Docker image
./gradlew bootBuildImage
# Run with specific profile
./gradlew bootRun --args='--spring.profiles.active=dev'- Import project as Gradle project
- Set Java 21 as project SDK
- Enable annotation processing for MapStruct
- Configure code style for Spring Boot
- Install Java Extension Pack
- Install Spring Boot Extension Pack
- Configure settings for Java 21
- Set up recommended extensions
- Hexagonal Architecture
- Spring Boot Documentation
- MapStruct Documentation
- Flyway Documentation
- OpenSearch Documentation
- Spring WebFlux Documentation
- Kafka Documentation
- Follow Hexagonal Architecture principles
- Write comprehensive tests for new features
- Update documentation when adding new features
- Use meaningful commit messages
- Follow existing code style and patterns
- Fork the project
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Hexagonal architecture principles are followed
- Tests are written and passing
- Documentation is updated
- Code follows project conventions
- No breaking changes without version bump
This project is an open source seed/template, available under the MIT License.
- Ezequiel Aparicio - Initial work - Ezequiel Aparicio
- Comprehensive Test Suite: Implement unit, integration, and E2E tests
- CI/CD Pipeline: GitHub Actions for automated testing and deployment
- Code Quality: SonarQube integration and coverage reporting
- Authentication & Authorization: JWT-based security with Spring Security
- Caching Layer: Redis integration for performance optimization
- Circuit Breaker: Resilience4j for fault tolerance
- Monitoring: Micrometer metrics and Prometheus integration
- API Versioning: Support for multiple API versions
- GraphQL Support: GraphQL endpoint alongside REST APIs
- Distributed Tracing: Spring Cloud Sleuth or Micrometer Tracing
- Event Sourcing: Advanced event-driven patterns
- Multi-tenancy: Support for multiple tenants
- Background Jobs: Spring Batch for processing large datasets
- Tutorial Series: Step-by-step guides for common use cases
- Video Walkthroughs: Architecture explanation and demos
- Blog Posts: Best practices and patterns
- Sample Applications: Real-world use case examples
- Issues: Open an issue in the repository for bugs or feature requests
- Discussions: Use GitHub Discussions for questions and ideas
- Documentation: Check the comprehensive documentation first
- Contributors: All contributions are welcome!
- Feedback: Share your experience using this seed project
- Improvements: Suggest improvements or report issues
Happy Coding! 🚀
This seed project provides a solid foundation for building enterprise-grade Spring Boot applications with Hexagonal Architecture. Feel free to use it as a starting point for your projects!