Best optimized and effective template to develop services and apps with Go.
This project follows Clean Architecture (also known as Hexagonal Architecture or Ports & Adapters) principles, combined with Domain-Driven Design (DDD) concepts. The architecture is organized in layers with clear separation of concerns and dependency inversion.
The innermost layer containing business logic and domain entities. This layer has no dependencies on external frameworks or infrastructure.
- Entities: Core business objects (Company, User, Order)
- Domain Validators: Business rule validation logic
- Domain Errors: Domain-specific error types
Principles Applied:
- Domain-Driven Design (DDD): Rich domain models with business logic
- Single Responsibility Principle: Each entity has a clear, focused purpose
- Dependency Inversion: Domain layer has no external dependencies
Contains application-specific business logic and orchestrates domain entities. This layer implements the application's use cases.
- Use Cases: Application workflows (CreateCompany, CreateOrder, etc.)
- Request/Response DTOs: Data transfer objects for use case boundaries
- Orchestration: Coordinates between repositories, domain entities, and integrations
Principles Applied:
- Use Case Pattern: Each use case represents a single business operation
- Command Pattern: Use cases encapsulate operations as commands
- Dependency Injection: Use cases depend on abstractions (interfaces)
Abstracts data access logic. Provides interfaces for data persistence and implements the Repository pattern.
- Repository Interfaces: Define data access contracts
- Multiple Implementations: PostgreSQL, MongoDB, and Mock repositories
- Strategy Pattern: Different storage strategies can be swapped
Principles Applied:
- Repository Pattern: Abstracts data access from business logic
- Strategy Pattern: Multiple repository implementations
- Interface Segregation: Focused, specific repository interfaces
- Dependency Inversion: Business logic depends on repository interfaces, not implementations
Contains technical implementations and external service integrations.
Sub-layers:
- Storage (
storage/): Database clients (PostgreSQL, MongoDB, Redis, Kafka, S3, Nexus) - External Services (
external/): Clients for external APIs (Billing, Users Service, Prometheus, Sentry) - Local Services (
local/): Local infrastructure (Logging, Tracing, OAuth)
Principles Applied:
- Adapter Pattern: Adapts external services to internal interfaces
- Factory Pattern: Creates infrastructure components
- Dependency Injection: Infrastructure is injected into upper layers
Orchestrates interactions with external services and systems.
- Integration Interfaces: Define contracts for external service interactions
- Integration Implementations: Concrete implementations for billing, company sync, OAuth
- Anti-Corruption Layer: Protects domain from external service changes
Principles Applied:
- Adapter Pattern: Adapts external services to application needs
- Anti-Corruption Layer: Isolates domain from external dependencies
- Interface Segregation: Focused integration interfaces
Handles communication protocols (HTTP, gRPC, RPC).
- HTTP Transport: REST API implementation
- HTTP Middleware: Cross-cutting concerns (Logging, Tracing, Metrics, Sentry)
- gRPC Transport: gRPC server implementation
- Response Handling: Standardized response formatting
Principles Applied:
- Middleware Pattern: Chain of responsibility for cross-cutting concerns
- Decorator Pattern: Middleware decorates handlers
- Separation of Concerns: Transport logic separated from business logic
HTTP request handlers that translate HTTP requests to use case calls.
- Request Handlers: Handle HTTP requests and responses
- Route Binding: Maps HTTP routes to handlers
- Request Validation: Input validation before use case execution
Principles Applied:
- Controller Pattern: Handlers act as controllers
- Thin Controllers: Minimal logic, delegates to use cases
- Single Responsibility: Each handler focuses on one resource
Application bootstrap and composition root. Assembles all components using Dependency Injection.
- Component Assembly: Wires up all layers
- Service Orchestration: Manages service lifecycle
- Dependency Resolution: Resolves dependencies between components
Principles Applied:
- Composition Root: Single place for dependency resolution
- Dependency Injection: All dependencies injected
- Service Locator Anti-Pattern Avoided: Explicit dependency injection
Long-running services and background jobs.
- API Service: HTTP API server
- Jobs Service: Background job processing
- CDC Service: Change Data Capture service
- Service Controller: Manages multiple services lifecycle
Principles Applied:
- Service Pattern: Encapsulates service lifecycle
- Concurrent Execution: Services run concurrently
- Graceful Shutdown: Proper resource cleanup
- Repository Pattern: Abstracts data access
- Factory Pattern: Component creation (
Prepare*functions) - Strategy Pattern: Multiple repository/storage implementations
- Adapter Pattern: External service integration
- Dependency Injection: Loose coupling through interfaces
- Middleware Pattern: Cross-cutting concerns in HTTP layer
- Use Case Pattern: Application business logic encapsulation
- Command Pattern: Use cases as commands
- DTO Pattern: Data transfer objects for layer boundaries
- Anti-Corruption Layer: Protection from external dependencies
-
Clean Architecture: Dependency rule - dependencies point inward
-
SOLID Principles:
- Single Responsibility: Each component has one reason to change
- Open/Closed: Open for extension, closed for modification
- Liskov Substitution: Implementations are substitutable
- Interface Segregation: Focused, specific interfaces
- Dependency Inversion: Depend on abstractions, not concretions
-
Domain-Driven Design (DDD):
- Rich domain models
- Domain validation
- Ubiquitous language
-
Separation of Concerns: Clear boundaries between layers
-
Dependency Inversion: High-level modules don't depend on low-level modules
-
Interface-Based Design: Program to interfaces, not implementations
HTTP Request
↓
Transport Layer (HTTP Middleware)
↓
Handler Layer (Request/Response Translation)
↓
Use Case Layer (Business Logic Orchestration)
↓
Domain Layer (Business Rules & Validation)
↓
Repository Layer (Data Access)
↓
Infrastructure Layer (Database/Storage)
The architecture includes comprehensive observability:
- Logging: Structured logging with Zap
- Tracing: OpenTelemetry with Jaeger/OTLP support
- Metrics: Prometheus metrics middleware
- Error Tracking: Sentry integration
- Fallback Mechanisms: Graceful degradation when observability tools unavailable
The architecture supports multiple storage backends:
- PostgreSQL: Relational database
- MongoDB: Document database
- Redis: Caching layer
- Kafka: Message queue
- S3: Object storage
- Nexus: Artifact repository
Repository pattern allows switching between storage implementations without changing business logic.
- Mock Repositories: For testing without database
- Interface-Based Testing: Easy to mock dependencies
- Domain Testing: Test business logic in isolation
- Integration Testing: Test component interactions
Describes common patterns for working with data models when using clean architecture in Go. The goal is to unify the design approach and avoid mixing responsibilities.
Purpose: represent key business concepts of the application (e.g., User, Order). Contain core business logic and invariants.
Characteristics:
- independent of external details (DB, frameworks, HTTP);
- located in the
domainlayer of clean architecture; - may contain methods for validation and business rules.
Example (Go):
package domain
import "errors"
type User struct {
ID string
Name string
Email string
}
func NewUser(name, email string) (*User, error) {
if name == "" {
return nil, errors.New("name is required")
}
// ... other business validation
return &User{Name: name, Email: email}, nil
}Purpose: describe characteristics or composite values without their own identifier (e.g., Email, Money, Address).
Characteristics:
- immutable
- compared by value, not by reference
- often used as fields in entities
package domain
import (
"errors"
"strings"
)
type Email struct {
value string
}
func NewEmail(value string) (*Email, error) {
if !strings.Contains(value, "@") {
return nil, errors.New("invalid email format")
}
return &Email{value: value}, nil
}
func (e Email) Value() string {
return e.value
}Purpose: transfer data between application layers or via API (e.g., for HTTP requests/responses).
Characteristics:
- simple structure, often without methods
- may differ from the domain model (e.g., contain fields for serialization)
- located in the interface adapters or transport layer
package transport
type UserResponse struct {
ID string `json:"id"`
Name string `json:"name"`
}Purpose: convert data between formats (e.g., from domain entity to DB row and back).
Characteristics:
- separates domain logic from persistence details
- implements mapping functions between domain models and data storage formats
- often used with Repository pattern
package infrastructure
import "your-app/domain"
type UserMapper struct{}
func (m UserMapper) ToDB(user *domain.User) map[string]interface{} {
return map[string]interface{}{
"id": user.ID,
"name": user.Name,
"email": user.Email,
}
}
func (m UserMapper) FromDB(row map[string]interface{}) *domain.User {
return &domain.User{
ID: row["id"].(string),
Name: row["name"].(string),
Email: row["email"].(string),
}
}Purpose: act as an intermediary between the domain model and the data source (DB, external API).
Characteristics:
- hides storage details from business logic
- provides a clean interface for data operations
- allows easy switching between different storage implementations
package repository
import "your-app/domain"
type UserRepository interface {
Create(user *domain.User) error
FindByID(id string) (*domain.User, error)
Update(user *domain.User) error
Delete(id string) error
}Purpose: adapt data specifically for a particular interface (e.g., frontend framework requirements). Characteristics:
- similar to DTO but focused on UI needs
- may include additional computed fields for display
- located in presentation layer
Typical data flow when processing an HTTP request:
- Incoming HTTP request → DTO (transport layer)
- DTO → mapped to Domain Entity (interface adapters layer)
- Business logic operates on the Entity (use cases layer)
- Entity passed to Repository → Data Mapper converts to DB format
- Response: domain data mapped to DTO/View Model → serialized to JSON/XML
- Separate responsibilities: DTO for transfer, Entity for logic, Data Mapper for conversion.
- Prefer immutability: make models immutable where possible for thread safety.
- Use mapping libraries: consider using libraries like mapstructure to reduce boilerplate.
- Plan versioning: design DTO/View Model versioning early for API evolution.
- Validate early: perform input validation at the transport layer.
- Keep domain pure: avoid importing infrastructure packages in domain models.
your-app/
├── domain/ # Entities and Value Objects
│ ├── user.go
│ └── email.go
├── use-cases/ # Business logic
├── interface-adapters/
│ ├── transport/ # DTOs and HTTP handlers
│ │ └── user_dto.go
│ └── repository/ # Repository interfaces
├── infrastructure/ # Data Mappers and DB implementations
│ ├── mappers/
│ └── db/
└── main.goThis project is open to development and welcomes proactive pull requests! We encourage contributions that:
- Improve code quality and maintainability
- Add new features following the established architecture
- Enhance documentation
- Fix bugs and improve error handling
- Optimize performance
- Add new integrations or storage backends
- Improve observability and monitoring
Please ensure that any contributions follow the architectural principles and patterns established in this template. We're looking forward to your contributions!