This is an attempt to create a boilerplate for a Go Rest API for an easy start to a new project and less time to market. The boilerplate is based on clean architecture (without fanaticism). There aren't strong rules. These are just recommendations, you can feel free to change them for yourself.
- goose: Install or
brew install goose(macOS) - taskfile: Install or
brew install go-task(macOS) - golangci-lint: Install or
brew install golangci-lint(macOS)
Create .env file from template:
cp .env.example .envRun containers:
docker compose up -dRun the dev server:
task http:devTo see all available commands:
taskThis project follows Clean Architecture principles with a focus on maintainability and testability. The structure is inspired by the golang-standards/project-layout and this YouTube video, with some modifications for practical use.
.
├── cmd/ # Application entrypoints
├── internal/ # Private application code
│ ├── adapter/ # External service integrations
│ ├── config/ # Configuration management
│ ├── domain/ # Domain models and errors
│ ├── handler/ # Routes and middleware setup
│ └── {feature}/ # Feature-based use cases
│ └── {use_case}/ # Individual use case
│ ├── usecase.go
│ ├── dto.go
│ ├── http_v1.go
│ └── kafka_v1.go
└── pkg/ # Reusable libraries
Application entrypoints for different services. Each subdirectory represents a separate executable (e.g., http/, kafka/). Following the golang-standards/project-layout convention, each entrypoint initializes its transport layer and shares the same business logic.
Private application code that cannot be imported by other projects. This follows the Go convention enforced by the compiler, as recommended in golang-standards/project-layout.
Adapters for external systems (Postgres, Redis, external APIs, etc.). This is auxiliary code that the main logic calls. Adapters do not know about use cases and live independently. Each adapter is a reusable building block. Requirements for adapters: they must implement the interface contract and perform atomic operations (get, save, update, delete, check, send). Name adapters specifically (e.g., postgres, valkey), not generically (e.g., repository, cache).
Configuration setup for the application. Loads environment variables from .env file and structures them for all components.
Domain models and business errors. This is shared and reusable code across multiple use cases. You can also add constructors, validation methods, and domain functions here as needed.
Setup and configuration layer for entry points. This is where you wire up your use cases to the outside world and configure common behavior. Set up routing rules, apply cross-cutting concerns (middleware), and define how incoming requests reach your use cases.
Feature-based organization of use cases (e.g., post/, user/). Each use case is a separate program with its own main logic. Use cases cannot be reused - each endpoint has unique logic.
Main logic. This is the essence of what your program does, fitting on one screen. Contains validation, work with domain models and functions, network interactions (database, cache, message brokers), transactions, loops, branching. Main logic works with adapters through interfaces, not directly. This code is independent and portable.
Input/Output structures. This is the contract of the main logic - shows input data and output data.
Transport layer handlers. Each file represents an entry point from a specific transport (HTTP, Kafka, gRPC, CLI, etc.). Their job is to translate between the transport's world and your use case: decode incoming data into DTO, call main logic, encode result back to transport format. You can add your own transport handlers as needed (e.g., grpc_v1.go, cli_v1.go).
Reusable packages that can be imported by other projects. These are infrastructure utilities with no business logic dependencies. Following golang-standards/project-layout guidelines, these packages are designed to be copied and reused in other Go projects.