Template for Go backends using:
- DDD (rich domain model)
- CQRS-lite (read/write path separation)
- Anti-Corruption Layer (external contract isolation)
agents.md is the architectural source of truth for contributors and AI agents. This README is a practical, developer-focused guide aligned with those rules.
- Keep business rules and invariants in
internal/model. - Keep application orchestration in
internal/service. - Keep transport concerns (HTTP/gRPC/consumer DTOs and mapping) in
internal/handler. - Keep persistence concerns in
internal/repositorywith explicit storage-to-domain/query mapping. - Keep external vendor contracts inside
internal/integrationadapters only. - Keep dependencies flowing downward only:
cmd -> handler -> service -> modelservice -> repository(via ports/interfaces)service -> integration(via ports/interfaces)handler -> read_repositoryis allowed for CQRS-lite reads
.
|- cmd/ # app entry points, wiring, startup only
|- db/
| |- migrations/ # SQL migrations
| `- schema.sql # schema snapshot/base schema
|- internal/
| |- config/ # config model, load, validation
| |- di/ # dependency injection container
| |- errors/ # shared sentinel errors
| |- handler/ # transport layer
| |- integration/ # ACL for external systems
| |- model/ # domain entities, value objects, invariants
| |- repository/ # storage implementations
| `- service/ # use-case orchestration
|- logs/ # runtime logs (local/dev)
|- Taskfile.yaml # format/lint/test/migration tasks
`- Makefile # build/run helpers
- Development is supported on Unix-like systems only (Linux, macOS, BSD).
- Go
1.25.0(seego.mod) taskCLI (https://taskfile.dev)make
- Clone the repository.
- Adjust
config.yamlif needed. - Run the app:
make runEquivalent command:
go run ./cmd/main.goYou can pass a custom config path:
go run ./cmd/main.go -config ./config.yamlCurrent config root is:
logger
Config loading behavior:
- Reads YAML from a path provided via CLI flag (
-config). - Expands environment templates using
os.ExpandEnv($VARor${VAR}). - Validates the resulting struct using
go-playground/validator.
Important note:
- Missing environment variables are expanded to an empty string by
os.ExpandEnv. - Any critical settings should be validated as required in config validation tags.
Logging in this template uses zerolog.
- Documentation: https://github.com/rs/zerolog
- Commented logging configuration example:
config.yaml
Quality and tooling commands are defined in Taskfile.yaml.
The same task commands are also used in CI pipelines to keep local and CI checks consistent.
task format # gofumpt + gci
task lint # golangci-lint
task test # go test ./...
task vuln # govulncheck
task generate # go generate ./...Hooks include:
pre-commit: YAML check, trailing whitespace, EOF fix, private key detection,task format,task lintpre-push:task test,task vuln
Setup:
# install pre-commit via your package manager
pre-commit install
pre-commit install --hook-type pre-pushRun manually:
pre-commit run --all-files
pre-commit run --all-files --hook-stage pre-pushBuild and run commands are in Makefile.
make build
make build-all
make run
make cleanPull request workflow is defined in .github/workflows/pr.yml.
It runs on PR events:
openedsynchronizereopenedready_for_review
Checks executed for non-draft PRs:
lint->task linttest->task testquality->task format+git diff --exit-code(no formatting drift)task generate+git diff --exit-code(generated files are up to date)
security->task vuln(govulncheck)
Release workflow is defined in .github/workflows/release.yml and starts only on tag push.
Supported tag patterns:
v*.*.*(stable), for example:v1.2.3v*.*.*-alpha.*, for example:v1.2.3-alpha.1v*.*.*-beta.*, for example:v1.2.3-beta.1v*.*.*-rc.*, for example:v1.2.3-rc.1
Important behavior:
- Tags can point to commits from any branch (no
main-only restriction). - Any tag containing
-is treated as prerelease. - Stable tags additionally publish Docker tag
latest. - Workflow builds and pushes:
- multi-arch Docker image (
linux/amd64,linux/arm64) toghcr.io/<owner>/<repo> - compiled binaries from
dist/*to GitHub Release
- multi-arch Docker image (
How to cut a release:
git tag v0.0.1-beta.1
git push origin v0.0.1-beta.1Set DATABASE_URL in Go DSN format before running migration commands.
PostgreSQL example:
export DATABASE_URL='postgres://user:password@localhost:5432/dbname?sslmode=disable'Migration workflow:
task migration-new -- add_orders_table
task migration-up
task migration-downMigration quality requirements:
- Each migration must include both
-- migrate:upand-- migrate:down. downmust be meaningfully reversible.- Validate apply and rollback before merge.
If you are bootstrapping a new project from this template, setup.sh can replace the module path safely with a dry-run + confirmation workflow.