Language: English | 中文
A common module template for golang.
-
adapter/
Adaptation layer code for external interaction, such as RESTful API definition and command line definition. -
config/
Configuration files (app.toml) and the SQLite schema (schema_sqlite.sql). -
dataSqlite data file. -
docs/
Document directory generated by swagger. -
domain/Domain model definition and implementation. -
infra/
Infrastructure code that implements external dependencies, such as database access object, etc. -
pkg/
Functional packages, such as util functions, injection functions, dto definitions, etc. -
service/
Service layer code implements the main business processes. -
CLAUDE.md
Guidance for Claude Code (and other AI coding assistants) working in this repository: project overview, build/test commands, and non-obvious implementation details worth knowing before making changes. -
Dockerfile
Two-stage build: a builder stage compiles the app, and a final stage runs it on a slim base image. -
go.mod,go.sum
Go module definition and dependency lock files. -
main.goMain program, code entrance. -
README.md
Project documentation.
This template follows the standard Go project layout, making it easy to start new Go modules with best practices.
main.go doesn't do any setup itself — it just declares an ordered list of
components and hands them to bootstrap.New()...Run(), which is the
common startup/shutdown orchestrator shared by every service built on
common-library-golang. Each component has an Init() (called in
registration order at startup) and a Close() (called in reverse order
at shutdown). Understanding this chain is the fastest way to understand what
a new service generated from this template actually does when it starts.
bootstrap.New().
Add(envComp.Component("config/app.toml")). // 1st - env
Add(logComp.Component()). // 2nd - log
AddParallel(dbComp.Component()).
PreReady(initServices).
Add(ginComp.Component(func(r *gin.Engine) {
router = r
adapter.Mount(r)
})).
Add(httpComp.Component(func() http.Handler { return router })).
PostReady(func() { log.Infof("%s start successfully, ...", ...) }).
Run()Step by step:
-
envComp.Component("config/app.toml")— must always be the firstAdd()call. Loadsconfig/app.tomlinto the koanf config singleton (env.Env()), which every other component reads from during its ownInit().Close()is a no-op. -
logComp.Component()— must always be the secondAdd()call. Reads the[log]section (log.level, and optionallylog.file.*for file-based rotation) and initializes the process-wide logger. Because it's closed last (LIFO), shutdown log messages are never lost. -
AddParallel(dbComp.Component())— opens the database connection. This template usesdbsqlx/sqlite/component, which readsdb.sqlite.pathfromconfig/app.tomland registers the connection asdbsqlx.Default().AddParallellets multiple independent components (e.g. a second data store, a cache client) initialize concurrently in the same phase — here there's only one, but a real project might add more alongside it. Swap this single line fordbsqlx/postgres/componentto switch the template to Postgres. -
PreReady(initServices)— a custom hook (defined in this file, not a library component) that runs once env/log/db are ready but before the HTTP server starts accepting traffic. This is where dependency injection happens: DAOs are constructed on top ofdbsqlx.Default(), wrapped in services, and published to the adapter layer viaadapter.Svcs. Add a new entity to the template by extending this function the same wayservice.NewUserService(dao.NewUserDao(dbsqlx.Default()))is wired here.PreReadysteps have noClose()— they're pure setup, not a component. -
ginComp.Component(func(r *gin.Engine) {...})— creates the*gin.Engine(with CORS configured fromcors.allow.origins.dev/.proddepending onapp.env.value), then immediately calls the supplied closure. That closure captures the router into thevar router *gin.Enginedeclared at the top ofmain()and callsadapter.Mount(r)to register every REST route.Close()is a no-op — the Gin engine itself holds no resources; the actual listener is owned by the next component. -
httpComp.Component(func() http.Handler { return router })— starts the real HTTP listener onhttp.server.port, serving whateverhttp.Handlerthe closure returns. The closure is evaluated lazily atInit()time (not whenAdd()is called), which is why it's safe even thoughrouteris only assigned inside step 5's closure — by the time this component'sInit()runs, step 5 has already completed.Close()gracefully shuts the server down withinhttpserver.DefaultShutdownTimeout. -
PostReady(func() {...})— runs once, after every component above has initialized successfully and just before the process blocks waiting for a shutdown signal. This template logs the app name/version/environment/local IP here; use it for anything that should only happen once the service is fully up (e.g. registering with a service discovery system). -
Run()— executes all of the above in order, then callsshutdown.Wait()to block until an OS signal (or an internalshutdown.Trigger(), e.g. if the HTTP server dies unexpectedly) arrives. On shutdown, every component'sClose()runs in LIFO order: HTTP server → Gin (no-op) → SQLite → log → env (no-op) — so in-flight requests get a chance to finish, the DB connection closes cleanly, and the very last thing written is the shutdown log line.
If Init() fails at any step (including a PreReady function returning a
non-nil error), bootstrap logs the failure, rolls back every
already-started component in LIFO order, and exits the process with code 1 —
so a new service never ends up half-initialized.
1. Update dependency
- go mod tidy
2. Generate swagger files
- swag init
visit http://localhost:8001/swagger/index.html
3. Test
- go test ./... -cover
4. Build
- go build
5. Run
- ./template