A comprehensive, production-ready Go boilerplate implementing Hexagonal Architecture (Ports & Adapters) with best practices for building scalable, maintainable microservices.
- Architecture
- Features
- Project Structure
- Getting Started
- API Documentation
- Configuration
- Best Practices
- Testing
- Observability
- Design Patterns
- Alternative Adapters
- Extending
- Contributing
- License
# Clone the repository
git clone https://github.com/ubaidillahhf/hexagonal-boilerplate.git
cd hexagonal-boilerplate
# Set up environment
cp .env.example .env
# Start infrastructure (MongoDB, Redis, RabbitMQ, Jaeger)
docker-compose up -d
# Run the application
go run cmd/main.goThe server will start on http://localhost:8080
Test it:
curl http://localhost:8080/healthThis boilerplate follows true Hexagonal Architecture principles with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Primary Adapters β
β (REST API, gRPC, CLI, etc.) β
β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β HTTP β β gRPC β β CLI β β
β β Handlers β β Handlers β β Commands β β
β ββββββββ¬βββββββ ββββββββ¬βββββββ ββββββββ¬βββββββ β
βββββββββββΌββββββββββββββββββΌββββββββββββββββββΌββββββββββββββββ
β β β
βββββββββββββββββββΌββββββββββββββββββ
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Application Layer (Ports) β
β β
β ββββββββββββββββββββββββββββββββββ β
β β Input Ports (Use Cases) β β
β β - OrderUseCase β β
β ββββββββββββββ¬ββββββββββββββββββββ β
β β β
β ββββββββββββββΌββββββββββββββββββββ β
β β Use Case Implementations β β
β β (Application Services) β β
β ββββββββββββββ¬ββββββββββββββββββββ β
β β β
β ββββββββββββββΌββββββββββββββββββββ β
β β Output Ports (Interfaces) β β
β β - OrderRepository β β
β β - CacheRepository β β
β β - EventPublisher β β
β ββββββββββββββ¬ββββββββββββββββββββ β
βββββββββββββββββΌββββββββββββββββββββββ
β
βββββββββββββββββΌββββββββββββββββββββββ
β Domain Layer β
β β
β ββββββββββββββββββββββββββββββββββ β
β β Entities & Value Objects β β
β β - Order, OrderItem β β
β β - Money (Value Object) β β
β ββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββ β
β β Domain Errors β β
β β - Enhanced error tracking β β
β ββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββ
β²
β
βββββββββββββββββ΄ββββββββββββββββββββββ
β Secondary Adapters β
β β
β ββββββββββββ ββββββββββββ β
β β MongoDB β β Redis β β
β βRepositoryβ β Cache β β
β ββββββββββββ ββββββββββββ β
β β
β ββββββββββββ ββββββββββββ β
β βRabbitMQ β β Feature β β
β βPublisher β β Flags β β
β ββββββββββββ ββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββ
- β Pure Hexagonal Architecture - True ports & adapters pattern
- β SOLID Principles - Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion
- β Domain-Driven Design - Rich domain models with business logic
- β Dependency Injection - Clean dependency management
- β Protocol Agnostic - Domain layer independent of HTTP/gRPC
- β MongoDB - Document database with optimistic locking
- β Redis - Caching with distributed locks
- β RabbitMQ - Event-driven messaging with auto-reconnection
- β Transaction Manager - Database-agnostic transaction abstraction (PostgreSQL, MongoDB, Elasticsearch)
- β ACID Transactions - Optimistic locking for concurrency control
- β OpenTelemetry Tracing - Distributed tracing with Jaeger
- β Structured Logging - Zap logger with context propagation
- β Error Tracking - Enhanced error handling with stack traces
- β Health Checks - Readiness and liveness endpoints
- β Race Condition Prevention - Distributed locks and optimistic locking
- β Rate Limiting - Redis-backed rate limiter
- β Connection Pooling - Optimized database connections
- β Graceful Shutdown - Clean resource cleanup
- β Circuit Breaker Ready - Resilient external service calls
- β Feature Flags - Runtime feature toggling
- β Hot Reload Ready - Environment-based configuration
- β Docker Compose - Local development environment
- β Makefile - Common development tasks
- β Clean Code - Well-documented and maintainable
hexagonal-boilerplate/
βββ cmd/
β βββ main.go # Application entry point
β
βββ internal/
β βββ domain/ # Domain Layer (Core Business Logic)
β β βββ entity/ # Domain entities
β β β βββ order.go # Order aggregate with business rules
β β βββ valueobject/ # Value objects
β β β βββ money.go # Money value object
β β βββ error/ # Domain errors
β β βββ error.go # Enhanced error with stack trace
β β
β βββ application/ # Application Layer (Use Cases)
β β βββ port/
β β β βββ input/ # Input ports (use case interfaces)
β β β β βββ order_usecase.go
β β β βββ output/ # Output ports (repository interfaces)
β β β βββ order_repository.go
β β β βββ cache_repository.go
β β β βββ event_publisher.go
β β β βββ feature_flag.go
β β β βββ logger.go # Logger abstraction
β β β βββ tracer.go # Tracer abstraction
β β β βββ transaction_manager.go # Transaction abstraction
β β βββ service/ # Use case implementations
β β βββ order_service.go # Order business logic
β β βββ observability_helper.go # Tracing & logging helpers
β β
β βββ infrastructure/ # Infrastructure Layer (Adapters)
β βββ adapter/
β β βββ primary/ # Inbound adapters
β β β βββ rest/
β β β βββ handler/ # HTTP handlers
β β β βββ middleware/ # Rate limiting, tracing, recovery
β β β βββ router.go # Route definitions
β β β
β β βββ secondary/ # Outbound adapters
β β βββ persistence/
β β β βββ mongodb/ # MongoDB repository
β β β βββ postgres/ # PostgreSQL repository
β β β βββ elasticsearch/ # Elasticsearch repository
β β βββ cache/ # Redis/Valkey cache
β β βββ messaging/ # RabbitMQ/NATS publisher
β β βββ feature/ # Feature flag service
β β
β βββ config/ # Configuration
β βββ database/ # Database clients
β βββ observability/ # Logging & tracing adapters
β β βββ zap_logger.go # Zap logger adapter
β β βββ otel_tracer.go # OpenTelemetry tracer adapter
β βββ transaction/ # Transaction manager adapters
β βββ postgres_transaction_manager.go
β βββ mongodb_transaction_manager.go
β βββ noop_transaction_manager.go
β
βββ docker-compose.yml # Local infrastructure
βββ Makefile # Development commands
βββ go.mod # Go dependencies
βββ .env.example # Environment variables template
- Go 1.21+
- Docker & Docker Compose
- Make (optional)
- Clone the repository:
git clone https://github.com/ubaidillahhf/hexagonal-boilerplate.git
cd hexagonal-boilerplate- Set up environment:
cp .env.example .env
# Edit .env with your configuration- Start infrastructure services:
make docker-up
# or
docker-compose up -d- Install dependencies:
make deps
# or
go mod download- Run the application:
make run
# or
go run cmd/main.goThe server will start on http://localhost:8080
curl http://localhost:8080/healthcurl -X POST http://localhost:8080/api/v1/orders \
-H "Content-Type: application/json" \
-d '{
"customer_id": "customer-123",
"items": [
{
"product_id": "product-456",
"quantity": 2,
"price": 29.99
}
]
}'curl http://localhost:8080/api/v1/orders/{order_id}curl "http://localhost:8080/api/v1/orders?page=1&limit=20&customer_id=customer-123"curl -X POST http://localhost:8080/api/v1/orders/{order_id}/confirmcurl -X POST http://localhost:8080/api/v1/orders/{order_id}/paymentcurl -X POST http://localhost:8080/api/v1/orders/{order_id}/cancelAll configuration is managed through environment variables. See .env.example for available options:
- Server: Port, timeouts, shutdown timeout
- MongoDB: Connection URI, pool sizes
- Redis: Address, pool configuration
- RabbitMQ: Connection URL, exchange, queue
- Feature Flags: Enable/disable features at runtime
- Rate Limiting: Requests per window
- Tracing: Jaeger endpoint, sampling
- Logging: Level (debug/info/warn/error), format (json/console)
// Domain errors with stack traces and context
err := domainErr.Wrapf(
domainErr.ErrCodeNotFound,
originalErr,
"order not found: %s",
orderID,
)
err.WithField("customer_id", customerID)// Distributed locks with Redis
lockKey := fmt.Sprintf("lock:order:%s", orderID)
acquired, err := cache.SetNX(ctx, lockKey, "1", 10*time.Second)
// Optimistic locking with version control
err := repo.UpdateWithOptimisticLock(ctx, order, expectedVersion)ctx, span := tracer.Start(ctx, "OrderService.CreateOrder")
defer span.End()
span.SetAttributes(
attribute.String("order_id", order.ID),
attribute.Float64("amount", order.TotalAmount),
)// Database-agnostic transaction abstraction
// Application layer controls transaction boundaries
err := txManager.ExecuteInTransaction(ctx, func(txCtx context.Context) error {
if err := orderRepo.Create(txCtx, order); err != nil {
return err
}
if err := inventoryRepo.Reserve(txCtx, items); err != nil {
return err
}
return nil // Auto-commit on success, auto-rollback on error
})
// Works with PostgreSQL, MongoDB, or no-op for Elasticsearch
// No database-specific code in application layerif !featureFlag.IsEnabled(ctx, FeatureOrderCreation) {
return domainErr.ErrFeatureDisabled
}// Redis-backed rate limiter per IP
// Configured via RATE_LIMIT_REQUESTS and RATE_LIMIT_WINDOW// Coordinated shutdown with timeout
// Closes HTTP server, databases, message brokers cleanly# Run tests
make test
# Run tests with coverage
make test-coverage
# Run with race detector
go test -race ./...Access distributed traces at: http://localhost:16686
Access RabbitMQ console at: http://localhost:15672
- Username:
guest - Password:
guest
Structured JSON logs with trace IDs for correlation:
{
"level": "info",
"timestamp": "2024-03-12T08:00:00Z",
"caller": "service/order_service.go:45",
"message": "Order created successfully",
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"customer_id": "customer-123",
"total_amount": 59.98,
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}- Single Responsibility: Each layer has one reason to change
- Open/Closed: Extend behavior via interfaces, not modification
- Liskov Substitution: Implementations are interchangeable
- Interface Segregation: Small, focused interfaces
- Dependency Inversion: Depend on abstractions, not concretions
- Hexagonal Architecture (Ports & Adapters)
- Repository Pattern (Data access abstraction)
- Transaction Manager Pattern (Database-agnostic transaction control)
- Dependency Injection (Loose coupling)
- Factory Pattern (Object creation)
- Strategy Pattern (Feature flags)
- Observer Pattern (Event publishing)
- Adapter Pattern (Logger, Tracer, Transaction abstractions)
- Connection pooling for MongoDB and Redis
- Caching with TTL and invalidation
- Optimistic locking for minimal blocking
- Efficient JSON serialization
- Context-aware timeouts
- Resource cleanup on shutdown
make help # Show all available commands
make build # Build the application
make run # Run the application
make test # Run tests
make test-coverage # Run tests with coverage
make clean # Clean build artifacts
make docker-up # Start Docker services
make docker-down # Stop Docker services
make docker-logs # View Docker logs
make lint # Run linter
make deps # Download dependenciesThis boilerplate includes multiple adapter implementations for each infrastructure component, demonstrating the true power of Hexagonal Architecture. Switch adapters without changing your domain logic!
| Component | Default | Alternatives | Switch Guide |
|---|---|---|---|
| HTTP Framework | Fiber | Echo | Guide |
| Cache | Redis | Valkey | Guide |
| Message Broker | RabbitMQ | NATS | Guide |
| Database | MongoDB | PostgreSQL, Elasticsearch | Guide |
From: Fiber + MongoDB + Redis + RabbitMQ
To: Echo + PostgreSQL + Valkey + NATS
Just update cmd/main.go dependency injection - domain layer stays unchanged!
// Before
import "github.com/ubaidillahhf/hexagonal-boilerplate/internal/infrastructure/adapter/primary/rest"
app := rest.NewRouter(...)
// After
import echoAdapter "github.com/ubaidillahhf/hexagonal-boilerplate/internal/infrastructure/adapter/primary/echo"
app := echoAdapter.NewRouter(...)- SWITCHING_GUIDE.md - Step-by-step switching instructions
- ADAPTERS.md - Complete adapter reference and comparison
- EXAMPLES.md - Copy-paste examples for different stacks
β
Demonstrates Hexagonal Architecture - Shows true adapter swapping
β
Technology Freedom - Choose the best tool for your needs
β
Migration Path - Easy to switch as requirements evolve
β
Learning Resource - See different implementations of same interface
- Create entity in
internal/domain/entity/ - Define repository interface in
internal/application/port/output/ - Implement repository in
internal/infrastructure/adapter/secondary/persistence/ - Create use case interface in
internal/application/port/input/ - Implement use case in
internal/application/service/ - Create HTTP handler in
internal/infrastructure/adapter/primary/rest/handler/ - Register routes in
internal/infrastructure/adapter/primary/rest/router.go
// 1. Define in output port
const FeatureNewFeature = "new_feature"
// 2. Add to .env
FEATURE_NEW_FEATURE_ENABLED=true
// 3. Use in service
if !s.featureFlag.IsEnabled(ctx, output.FeatureNewFeature) {
return domainErr.ErrFeatureDisabled
}MIT License - see the LICENSE file for details.
This boilerplate is free to use for both personal and commercial projects.
Contributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow the existing code style and architecture patterns
- Add tests for new features
- Update documentation as needed
- Ensure all tests pass before submitting PR
- π Documentation: Check the guidelines folder for detailed guides
- π¬ Issues: Open an issue for bugs or feature requests
- π Star: If you find this helpful, please star the repository!
This boilerplate is inspired by:
- Hexagonal Architecture by Alistair Cockburn
- Clean Architecture by Robert C. Martin (Uncle Bob)
- Domain-Driven Design by Eric Evans
- Best practices from companies like Uber, Netflix, Google, and Spotify
Built with β€οΈ following Hexagonal Architecture best practices
β If this boilerplate helps you, please consider giving it a star!