Example Go service using go-swagger and The Clean Architecture
This project shows an example of how to use go-swagger accordingly to Uncle Bob's "Clean Architecture".
Also it includes go-swagger JSON Schema support cheatsheet, which list all validations/annotations for JSON body actually implemented by go-swagger v0.18.0.
Table of Contents
The Clean Architecture
It's not a complete example of The Clean Architecture itself
(business-logic of this example is too trivial, so "Use Cases" layer in
app embeds "Entities" layer), but it does show the most relevant
part: how to create "API Controller" layer in package
between code auto-generated by go-swagger and "Use Cases" layer in package
app. Also it includes "DB Gateway" layer in packages
trivial in-memory implementation is "DB" and "Gateway" layers at once) and
dal/mysql (just "Gateway" layer).
The hexagonal architecture, or ports and adapters architecture
It may be even easier to understand implemented architecture as "ports and adapters":
- "ports" are defined as interfaces in
app/app.go- they make it possible to easily test business-logic in
appwithout any external dependencies by mocking all these interfaces.
- "adapters" are implemented in
srv/*(serve project APIs),
dal/*(access DB) and
svc/*(use external services) packages - they can't be tested that easy, so they should be as thin and straightforward as possible and try hard to do nothing than convert ("adapt") data between format used by external world and our business-logic (package
Structure of Go packages
api/*- definitions of own and 3rd-party (in
api/ext-*) APIs/protocols and related auto-generated code
cmd/*- main application(s)
internal/config- configuration(s) (default values, env, flags) for application(s) subcommands and tests
internal/app- define interfaces ("ports") and implements business-logic
internal/srv/*- adapters for served APIs/UI
internal/dal/*- adapters for data storage
internal/migrations/*- DB migrations (in both SQL and Go)
internal/svc/*- adapters for accessing external services
pkg/*- helper packages, not related to architecture and business-logic (may be later moved to own modules and/or replaced by external dependencies), e.g.:
pkg/def/- project-wide defaults
- Project structure (mostly) follows Standard Go Project Layout.
- Strict but convenient golangci-lint configuration.
- Easily testable code (thanks to The Clean Architecture).
- Avoids (and resists to) using global objects (to make it possible to embed such microservices into modular monolith).
- CLI subcommands support using cobra.
- Graceful shutdown support.
- Configuration defaults can be overwritten by env vars and flags.
- CORS support, so you can play with API using Swagger Editor tool.
- Example go-swagger authentication and authorization.
- Example DAL (data access layer):
- MySQL 5.6 (strictest SQL mode).
- Example tests, both unit and integration.
- Production logging using structlog.
- Production metrics using Prometheus.
- Docker and docker-compose support.
- Smart test coverage report, with optional support for coveralls.io.
- Linters for Dockerfile and shell scripts.
- CI/CD setup for GitHub Actions and CircleCI.
- After cloning the repo copy
env.shand update for your system as needed.
- It's recommended to add shell alias
alias dc="if test -f env.sh; then source env.sh; fi && docker-compose"and then run
docker-compose- this way you won't have to run
source env.shafter changing it.
To develop this project you'll need only standard tools:
docker build. Provided scripts are for
- Always load
env.shin every terminal used to run any project-related commands (including
env.sh.distchange (e.g. by
git pull) next run of
source env.shwill fail and remind you to manually update
env.shto match current
go generate ./...- do not forget to run after making changes related to auto-generated code
go test ./...- test project (excluding integration tests), fast
./scripts/test- thoroughly test project, slow
./scripts/test-ci-circle- run tests locally like CircleCI will do
./scripts/cover- analyse and show coverage
./scripts/build- build docker image and binaries in
- Then use mentioned above
docker-compose) to run and control the project.
- Access project at host/port(s) defined in
- Then use mentioned above
dc up -d --remove-orphans # (re)start all project's services dc logs -f -t # view logs of all services dc logs -f SERVICENAME # view logs of some service dc ps # status of all services dc restart SERVICENAME dc exec SERVICENAME COMMAND # run command in given container dc stop && dc rm -f # stop the project docker volume rm PROJECT_SERVICENAME # remove some service's data
It's recommended to avoid
docker-compose down - this command will also
remove docker's network for the project, and next
dc up -d will create a
new network… repeat this many enough times and docker will exhaust
available networks, then you'll have to restart docker service or reboot.
$ docker run -i -t --rm ghcr.io/powerman/go-service-example -v address-book version 0894daa 2020-09-13_19:44:26 go1.15.2 $ dc up -d mysql $ docker run -i -t --rm \ -p 8000:8000 \ --net=go-service-example_default \ -e EXAMPLE_APIKEY_ADMIN=secret \ -e EXAMPLE_MYSQL_ADDR_HOST=mysql \ -e EXAMPLE_MYSQL_AUTH_LOGIN=root \ -e EXAMPLE_MYSQL_AUTH_PASS= \ ghcr.io/powerman/go-service-example address-book: inf main: `started` version 0894daa 2020-09-13_19:44:26 address-book: inf openapi: `OpenAPI protocol` version 0.2.0 address-book: inf serve: `serve` b3ecd12369c3:9000 [Prometheus metrics] address-book: inf serve: `serve` 172.19.0.6:8000 [OpenAPI] address-book: inf swagger: `Serving address book at http://172.19.0.6:8000` ^C address-book: inf swagger: `Shutting down... ` address-book: inf swagger: `HTTP server Shutdown: context deadline exceeded` address-book: inf swagger: `Stopped serving address book at http://172.19.0.6:8000` address-book: inf serve: `shutdown` [OpenAPI] address-book: inf serve: `shutdown` [Prometheus metrics] address-book: inf main: `finished` version 0894daa 2020-09-13_19:44:26
Use of the
./scripts/build script is optional (it's main feature is
embedding git version into compiled binary), you can use usual
go get|install|build to get the application instead.
$ ./scripts/build $ ./bin/address-book -h Example microservice with OpenAPI Usage: address-book [flags] address-book [command] Available Commands: help Help about any command serve Starts microservice Flags: -h, --help help for address-book --log.level OneOfString log level [debug|info|warn|err] (default debug) -v, --version version for address-book Use "address-book [command] --help" for more information about a command. $ ./bin/address-book serve -h Starts microservice Usage: address-book serve [flags] Flags: -h, --help help for serve --host NotEmptyString host to serve OpenAPI (default localhost) --metrics.port Port port to serve Prometheus metrics (default 9000) --port Port port to serve OpenAPI (default 8000) --timeout.shutdown Duration must be less than 10s used by 'docker stop' between SIGTERM and SIGKILL (default 9s) --timeout.startup Duration must be less than swarm's deploy.update_config.monitor (default 3s) Global Flags: --log.level OneOfString log level [debug|info|warn|err] (default debug) $ ./bin/address-book -v address-book version v1.0.0 5e45f44 2020-09-03_15:15:53 go1.15.1 $ ./bin/address-book serve address-book: inf main: `started` version v1.0.0 5e45f44 2020-09-03_15:15:53 address-book: inf openapi: `OpenAPI protocol` version 0.2.0 address-book: inf serve: `serve` localhost:9000 [Prometheus metrics] address-book: inf serve: `serve` 127.0.0.1:8000 [OpenAPI] address-book: inf swagger: `Serving address book at http://127.0.0.1:8000` address-book: dbg openapi: 127.0.0.1:36500 POST contacts: `calling AddContact` admin address-book: dbg dal: 127.0.0.1:36500 POST contacts: `contact added` admin address-book: inf openapi: 127.0.0.1:36500 201 POST contacts: `handled` admin address-book: dbg openapi: 127.0.0.1:36502 POST contacts: `calling AddContact` admin address-book: dbg dal: 127.0.0.1:36502 POST contacts: `contact added` admin address-book: inf openapi: 127.0.0.1:36502 201 POST contacts: `handled` admin address-book: inf openapi: 127.0.0.1:36504 200 GET contacts: `handled` admin address-book: inf openapi: 127.0.0.1:36508 200 GET contacts: `handled` user address-book: inf openapi: 127.0.0.1:36510 401 GET contacts: `handled` address-book: inf openapi: 127.0.0.1:36518 403 POST contacts: `handled` ^C address-book: inf swagger: `Shutting down... ` address-book: inf swagger: `HTTP server Shutdown: context deadline exceeded` address-book: inf swagger: `Stopped serving address book at http://127.0.0.1:8000` address-book: inf serve: `shutdown` [OpenAPI] address-book: inf serve: `shutdown` [Prometheus metrics] address-book: inf main: `finished` version v1.0.0 5e45f44 2020-09-03_15:15:53
- Update JSON Schema support cheatsheet to latest go-swagger version.
- Add alternative DAL implementation for Postgresql.
- Add cookie-based auth with CSRF middleware.
- Add an example of adapter for external service in
Package api contains OpenAPI spec and code generated by go-swagger.
|Package api contains OpenAPI spec and code generated by go-swagger.|
Package restapi Address Book # ...
|Package restapi Address Book # ...|
Package cobrax contains helpers to use with github.com/spf13/cobra.
|Package cobrax contains helpers to use with github.com/spf13/cobra.|
Package concurrent provide a helpers to setup, start and shutdown a lot of services in parallel.
|Package concurrent provide a helpers to setup, start and shutdown a lot of services in parallel.|
Package def provides default values for both commands and tests.
|Package def provides default values for both commands and tests.|
Package migrate manage DB migrations.
|Package migrate manage DB migrations.|
Package netx contains experimental helpers related to network.
|Package netx contains experimental helpers related to network.|
Package reflectx provide helpers for reflect.
|Package reflectx provide helpers for reflect.|
Package repo provide helpers for Data Access Layer.
|Package repo provide helpers for Data Access Layer.|
Package serve provides helpers to start and shutdown network services.
|Package serve provides helpers to start and shutdown network services.|
Package apix provide API-related helpers.
|Package apix provide API-related helpers.|
Package app provides business logic.
|Package app provides business logic.|
Package config provides configurations for subcommands.
|Package config provides configurations for subcommands.|
Package dal implements Data Access Layer using in-memory DB.
|Package dal implements Data Access Layer using in-memory DB.|
Package dal implements Data Access Layer using MySQL DB.
|Package dal implements Data Access Layer using MySQL DB.|
Package migrations provides goose migrations.
|Package migrations provides goose migrations.|
Package openapi implements OpenAPI server.
|Package openapi implements OpenAPI server.|