Idiomatic Go + Domain-Driven Design - A production-ready Go template combining idiomatic Go practices with DDD architecture patterns.
- Overview
- Features
- Architecture
- Project Structure
- Getting Started
- Configuration
- Development
- API Documentation
- Testing
- Deployment
- Contributing
- License
idiogo is a modern, production-ready Go boilerplate that combines idiomatic Go practices with Domain-Driven Design (DDD) principles. It provides a solid foundation for building scalable, maintainable REST APIs with clean architecture.
This template is designed to help you kickstart Go projects with best practices baked in, including:
- Clean DDD architecture
- Type-safe request/response handling
- Built-in validation and i18n support
- Database migrations with GORM
- Docker-ready deployment
- Middleware patterns
- ποΈ Domain-Driven Design: Clean separation of concerns with domain, application, and infrastructure layers
- π Fiber Framework: High-performance HTTP framework built on Fasthttp
- ποΈ GORM ORM: Powerful ORM with PostgreSQL support and auto-migrations
- β Validation: Comprehensive request validation with go-playground/validator
- π Internationalization: Multi-language support with go-i18n
- π Type-Safe Handlers: Generic handler patterns for type-safe request/response handling
- π¦ Dependency Injection: Clean dependency management pattern
- π³ Docker Support: Production-ready Docker and Docker Compose configurations
- π§ Hot Reload Ready: Easy integration with development tools
- π Structured Logging: Clean, readable log output
- π― Graceful Shutdown: Proper resource cleanup on termination
- π Security: Built-in security headers and proxy detection
- π Database Migrations: Automatic schema management with GORM
idiogo follows a Layered DDD Architecture with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββββββββββ
β cmd/ (Entry Points) β
β β’ serve: REST API server β
β β’ cron: Background jobs β
βββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββ
β internal/app (Application) β
β β’ Application initialization β
β β’ Module composition β
β β’ Dependency wiring β
βββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββ
β internal/domain (Business Logic) β
β β’ Entities & Aggregates β
β β’ Services (Business Logic) β
β β’ Repository Interfaces β
β β’ Handlers (HTTP) β
βββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββ
β internal/infra (Infrastructure) β
β β’ Database connections β
β β’ External service integrations β
β β’ Repository implementations β
βββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββ
β pkg/ (Shared Packages) β
β β’ Reusable utilities β
β β’ Common types & helpers β
βββββββββββββββββββββββββββββββββββββββββββββββ
- Domain Layer (
internal/domain/): Contains business logic, entities, and domain services - Application Layer (
internal/app/): Orchestrates domain logic and handles use cases - Infrastructure Layer (
internal/infra/): Technical implementations (DB, external APIs) - Interface Layer (
internal/rest/,internal/port/): HTTP handlers and protocol adapters - Shared Kernel (
pkg/): Reusable utilities and common abstractions
idiogo/
βββ cmd/ # Application entry points
β βββ serve/ # REST API server
β β βββ main.go
β β βββ Dockerfile
β βββ cron/ # Background jobs
β βββ main.go
βββ internal/ # Private application code
β βββ app/ # Application layer
β β βββ serve/ # Server initialization
β β βββ cron/ # Cron initialization
β βββ domain/ # Business logic layer
β β βββ todo/ # Example domain (Todo)
β β βββ todo.go # Entity
β β βββ service.go # Business logic
β β βββ repo.go # Repository implementation
β β βββ handler.go # HTTP handlers
β β βββ filters.go # Query filters
β βββ infra/ # Infrastructure layer
β β βββ db/ # Database
β β βββ pg.go # PostgreSQL connection
β β βββ migration/ # DB migrations
β βββ rest/ # REST API implementation
β β βββ rest.go # Server setup
β β βββ service.go # REST service utilities
β β βββ handler.go # Generic handlers
β β βββ middleware/ # HTTP middlewares
β βββ port/ # Ports (interfaces)
β β βββ rest.go # REST port interface
β βββ config/ # Configuration
β βββ config.go # Config structures
β βββ bind.go # Config loader
βββ pkg/ # Public shared packages
β βββ entity/ # Base entities
β βββ i18np/ # Internationalization
β βββ validation/ # Validation utilities
β βββ query/ # Query builders
β βββ state/ # State management
β βββ list/ # List/pagination
β βββ server/ # Server utilities
βββ assets/ # Static assets
β βββ locales/ # Translation files
β βββ en.toml
β βββ tr.toml
βββ deployments/ # Deployment configurations
β βββ compose.yml # Docker Compose
β βββ config.yml # Application config
βββ docs/ # Documentation
βββ tmp/ # Temporary files (gitignored)
βββ go.mod # Go modules
βββ README.md
- Go: 1.25.5 or higher
- Docker: Latest version (for containerized deployment)
- PostgreSQL: 15+ (or use Docker Compose)
- Clone the repository (or use as a GitHub template):
git clone https://github.com/salihguru/idiogo.git my-project
cd my-project- Install dependencies:
go mod download- Update module name:
Replace github.com/salihguru/idiogo with your module path in:
go.mod- All import statements
# Use a script or IDE refactoring tools
find . -type f -name "*.go" -exec sed -i '' 's/github.com\/salihguru\/idiogo/your-module-path/g' {} +- Configure the application:
Copy the example configuration:
cp deployments/config.yml config.yamlEdit config.yaml with your settings.
- Start the database:
docker compose -f deployments/compose.yml up -d idiogo-pg-
Run migrations (automatic on startup with
migrate: truein config) -
Start the server:
go run cmd/serve/main.goThe API will be available at http://localhost:4041
Configuration is managed through YAML files. See deployments/config.yml for an example.
rest:
host: 0.0.0.0 # Server host
port: 4041 # Server port
db:
host: localhost # Database host
port: "5432" # Database port
user: idiogo # Database user
pass: idiogo # Database password
name: idiogo # Database name
ssl_mode: disable # SSL mode (disable, require, verify-ca, verify-full)
migrate: true # Auto-run migrations on startup
debug: false # Enable SQL query logging
i18n:
locales: # Supported locales
- en
- tr
default: en # Default locale
dir: "./assets/locales" # Locale files directoryYou can override configuration values with environment variables (when implemented):
export DB_HOST=localhost
export DB_PORT=5432
export REST_PORT=8080- Create domain directory:
mkdir -p internal/domain/yourdomein- Define the entity (
internal/domain/yourdomain/entity.go):
package yourdomain
import "github.com/salihguru/idiogo/pkg/entity"
type YourEntity struct {
entity.Base
Name string `json:"name"`
Description string `json:"description"`
}- Create the repository (
internal/domain/yourdomain/repo.go):
package yourdomain
import (
"context"
"github.com/google/uuid"
"gorm.io/gorm"
)
type Repo struct {
db *gorm.DB
}
func NewRepo(db *gorm.DB) *Repo {
return &Repo{db: db}
}
func (r *Repo) Save(ctx context.Context, entity *YourEntity) error {
return r.db.WithContext(ctx).Save(entity).Error
}
func (r *Repo) View(ctx context.Context, id uuid.UUID) (*YourEntity, error) {
var entity YourEntity
err := r.db.WithContext(ctx).First(&entity, "id = ?", id).Error
return &entity, err
}- Implement the service (
internal/domain/yourdomain/service.go):
package yourdomain
import "context"
type Service struct {
repo *Repo
}
func NewService(repo *Repo) *Service {
return &Service{repo: repo}
}
func (s *Service) Create(ctx context.Context, req CreateReq) (*YourEntity, error) {
entity := &YourEntity{
Name: req.Name,
Description: req.Description,
}
if err := s.repo.Save(ctx, entity); err != nil {
return nil, err
}
return entity, nil
}- Create HTTP handlers (
internal/domain/yourdomain/handler.go):
package yourdomain
import (
"github.com/gofiber/fiber/v2"
"github.com/salihguru/idiogo/internal/port"
"github.com/salihguru/idiogo/internal/rest"
)
type Handler struct {
srv Service
}
func NewHandler(srv Service) *Handler {
return &Handler{srv: srv}
}
func (h *Handler) RegisterRoutes(srv port.RestService, router fiber.Router) {
group := router.Group("/yourdomains")
group.Post("/",
srv.Timeout(rest.Handle(rest.WithBody(rest.WithValidation(srv.ValidateStruct(),
rest.CreateResponds(h.srv.Create))))))
}- Register the module in
internal/app/serve/modules.go:
yourDomainRepo := yourdomain.NewRepo(d.DB)
yourDomainService := yourdomain.NewService(yourDomainRepo)
yourdomainModule := rest.Module[*yourdomain.Repo, *yourdomain.Service]{
Repo: yourDomainRepo,
Service: yourDomainService,
Router: yourdomain.NewHandler(*yourdomainModule.Service),
}# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run tests for specific package
go test ./internal/domain/todo/...# Format code
go fmt ./...
# Lint code
golangci-lint run
# Security scan
gosec ./...POST /todos
Content-Type: application/json
{
"title": "Buy groceries",
"description": "Milk, eggs, bread"
}Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Buy groceries",
"description": "Milk, eggs, bread",
"status": "pending",
"created_at": "2025-12-18T10:00:00Z"
}GET /todos?page=1&limit=10&status=pendingGET /todos/{id}PATCH /todos/{id}
Content-Type: application/json
{
"title": "Buy groceries and fruits",
"status": "completed"
}DELETE /todos/{id}All API responses follow a consistent format:
Success Response:
{
"data": { /* response data */ }
}Error Response:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "title",
"message": "title is required"
}
]
}
}The project includes examples for:
- Unit tests
- Integration tests
- Repository tests
Example test structure:
func TestService_Create(t *testing.T) {
// Setup
db := setupTestDB(t)
repo := NewRepo(db)
service := NewService(repo)
// Test
result, err := service.Create(context.Background(), CreateReq{
Title: "Test Todo",
})
// Assert
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, "Test Todo", result.Title)
}# Start all services
docker compose -f deployments/compose.yml up -d
# View logs
docker compose -f deployments/compose.yml logs -f
# Stop services
docker compose -f deployments/compose.yml down# Build image
docker build -f cmd/serve/Dockerfile -t idiogo:latest .
# Run container
docker run -p 4041:4041 \
-e DB_HOST=postgres \
-e DB_PORT=5432 \
idiogo:latestFor production deployment:
- Set environment-specific configuration
- Use secrets management for sensitive data
- Enable SSL/TLS for database connections
- Configure reverse proxy (nginx, traefik)
- Set up monitoring and logging
- Enable health checks
Example production config:
rest:
host: 0.0.0.0
port: 4041
db:
host: ${DB_HOST}
port: ${DB_PORT}
user: ${DB_USER}
pass: ${DB_PASSWORD}
name: ${DB_NAME}
ssl_mode: require
migrate: false
debug: false
i18n:
locales: [en, tr]
default: en
dir: "/app/assets/locales"Contributions are welcome! Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests (
go test ./...) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Fiber - Web framework
- GORM - ORM library
- go-playground/validator - Validation
- go-i18n - Internationalization
- rescode - Response code management
- π§ Email: [email protected]
- π Issues: GitHub Issues
- π¬ Discussions: GitHub Discussions
Built with β€οΈ using idiomatic Go and DDD principles