{"id":19167048,"url":"https://github.com/powerman/go-monolith-example","last_synced_at":"2025-03-31T06:07:58.189Z","repository":{"id":37080281,"uuid":"253297941","full_name":"powerman/go-monolith-example","owner":"powerman","description":"Example Go monolith with embedded microservices and The Clean Architecture","archived":false,"fork":false,"pushed_at":"2023-12-10T13:41:10.000Z","size":4944,"stargazers_count":311,"open_issues_count":17,"forks_count":24,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-23T22:12:21.770Z","etag":null,"topics":["architecture","clean-architecture","clean-code","embedded-microservices","go","golang","microservices","monolith"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/powerman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-05T17:57:20.000Z","updated_at":"2025-03-22T12:38:05.000Z","dependencies_parsed_at":"2024-11-09T09:46:00.496Z","dependency_job_id":null,"html_url":"https://github.com/powerman/go-monolith-example","commit_stats":null,"previous_names":[],"tags_count":7,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/powerman%2Fgo-monolith-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/powerman%2Fgo-monolith-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/powerman%2Fgo-monolith-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/powerman%2Fgo-monolith-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/powerman","download_url":"https://codeload.github.com/powerman/go-monolith-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246423731,"owners_count":20774820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["architecture","clean-architecture","clean-code","embedded-microservices","go","golang","microservices","monolith"],"created_at":"2024-11-09T09:35:32.472Z","updated_at":"2025-03-31T06:07:58.162Z","avatar_url":"https://github.com/powerman.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Example Go monolith with embedded microservices and The Clean Architecture\n\n[![PkgGoDev](https://pkg.go.dev/badge/github.com/powerman/go-monolith-example)](https://pkg.go.dev/github.com/powerman/go-monolith-example)\n[![Go Report Card](https://goreportcard.com/badge/github.com/powerman/go-monolith-example)](https://goreportcard.com/report/github.com/powerman/go-monolith-example)\n[![CI/CD](https://github.com/powerman/go-monolith-example/workflows/CI/CD/badge.svg?event=push)](https://github.com/powerman/go-monolith-example/actions?query=workflow%3ACI%2FCD)\n[![CircleCI](https://circleci.com/gh/powerman/go-monolith-example.svg?style=svg)](https://circleci.com/gh/powerman/go-monolith-example)\n[![Coverage Status](https://coveralls.io/repos/github/powerman/go-monolith-example/badge.svg?branch=master)](https://coveralls.io/github/powerman/go-monolith-example?branch=master)\n[![Project Layout](https://img.shields.io/badge/Standard%20Go-Project%20Layout-informational)](https://github.com/golang-standards/project-layout)\n[![Release](https://img.shields.io/github/v/release/powerman/go-monolith-example)](https://github.com/powerman/go-monolith-example/releases/latest)\n\nThis project shows an example of how to implement monolith with embedded\nmicroservices (a.k.a. modular monolith). This way you'll get many upsides\nof monorepo without it complexity and at same time most of upsides of\nmicroservice architecture without some of it complexity.\n\nThe embedded microservices use Uncle Bob's \"Clean Architecture\", check\n[Example Go microservice](https://github.com/powerman/go-service-example)\nfor more details.\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of Contents**\n\n- [Overview](#overview)\n  - [Structure of Go packages](#structure-of-go-packages)\n  - [Features](#features)\n- [Development](#development)\n  - [Requirements](#requirements)\n  - [Setup](#setup)\n    - [HTTPS](#https)\n  - [Usage](#usage)\n    - [Cheatsheet](#cheatsheet)\n- [Run](#run)\n  - [Docker](#docker)\n  - [Source](#source)\n- [TODO](#todo)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Overview\n\n### Structure of Go packages\n\n- `api/*` - definitions of own and 3rd-party (in `api/ext-*`)\n  APIs/protocols and related auto-generated code\n- `cmd/*` - main application(s)\n- `internal/*` - packages shared by embedded microservices, e.g.:\n  - `internal/config` - configuration (default values, env) shared by\n    embedded microservices' subcommands and tests\n  - `internal/dom` - domain types shared by microservices (Entities)\n- `ms/*` - embedded microservices, with structure:\n  - `internal/config` - configuration(s) (default values, env, flags) for\n    microservice's subcommands and tests\n  - `internal/app` - define interfaces (\"ports\") for The Clean\n    Architecture (or \"Ports and Adapters\" architecture) and implements\n    business-logic\n  - `internal/srv/*` - adapters for served APIs/UI\n  - `internal/sub` - adapter for incoming events\n  - `internal/dal` - adapter for data storage\n  - `internal/migrations` - DB migrations (in both SQL and Go)\n  - `internal/svc/*` - adapters for accessing external services\n- `pkg/*` - helper packages, not related to architecture and\n  business-logic (may be later moved to own modules and/or replaced by\n  external dependencies), e.g.:\n  - `pkg/def/` - project-wide defaults\n- `*/old/*` - contains legacy code which shouldn't be modified - this code\n  is supposed to be extracted from `old/` directories (and refactored to\n  follow Clean Architecture) when it'll need any non-trivial modification\n  which require testing\n\n### Features\n\n- [X] Project structure (mostly) follows\n  [Standard Go Project Layout](https://github.com/golang-standards/project-layout).\n- [X] Strict but convenient golangci-lint configuration.\n- [X] Embedded microservices:\n  - [X] Well isolated from each other.\n  - [X] Can be easily extracted from monolith into separate projects.\n  - [X] Share common configuration (both env vars and flags).\n  - [X] Each has own CLI subcommands, DB migrations, ports, metrics, …\n- [X] Easily testable code (thanks to The Clean Architecture).\n- [X] Avoids (and resists to) using global objects (to ensure embedded\n  microservices won't conflict on these global objects).\n- [X] CLI subcommands support using [cobra](https://github.com/spf13/cobra).\n- [X] Graceful shutdown support.\n- [X] Configuration defaults can be overwritten by env vars and flags.\n- [X] Example JSON-RPC 2.0 over HTTP API, with CORS support.\n- [X] Example gRPC API:\n  - [X] External and internal APIs on different host/port.\n  - [X] gRPC services with and without token-based authentication.\n  - [X] API design (mostly) follows\n    [Google API Design Guide](https://cloud.google.com/apis/design) and\n    [Google API Improvement Proposals](https://google.aip.dev/).\n- [X] Example OpenAPI 2.0 using grpc-gateway, with CORS suport:\n  - [X] Access to gRPC using HTTP/1 (except bi-directional streaming).\n  - [X] Generates `swagger.json` from gRPC `.proto` files.\n  - [X] Embedded [Swagger UI](https://swagger.io/tools/swagger-ui/).\n- [X] Example DAL (data access layer):\n  - [X] MySQL 5.7 (strictest SQL mode).\n  - [X] PostgreSQL 11 (secure schema usage pattern).\n- [X] Example tests, both unit and integration.\n- [X] Production logging using [structlog](https://github.com/powerman/structlog).\n- [X] Production metrics using Prometheus.\n- [X] Docker and docker-compose support.\n- [X] Smart test coverage report, with optional support for coveralls.io.\n- [X] Linters for Dockerfile and shell scripts.\n- [X] CI/CD setup for GitHub Actions and CircleCI.\n\n## Development\n\n### Requirements\n\n- Go 1.16\n- [Docker](https://docs.docker.com/install/) 19.03+\n- [Docker Compose](https://docs.docker.com/compose/install/) 1.25+\n\n### Setup\n\n1. After cloning the repo copy `env.sh.dist` to `env.sh`.\n2. Review `env.sh` and update for your system as needed.\n3. It's recommended to add shell alias `alias dc=\"if test -f env.sh; then\n   source env.sh; fi \u0026\u0026 docker-compose\"` and then run `dc` instead of\n   `docker-compose` - this way you won't have to run `source env.sh` after\n   changing it.\n\n#### HTTPS\n\n1. This project requires https:// and will send HSTS and CSP HTTP headers,\n   and also it uses gRPC with authentication which also require TLS certs,\n   so you'll need to create certificate to run it on localhost - follow\n   instructions in [Create local CA to issue localhost HTTPS\n   certificates](https://gist.github.com/powerman/2fc4b1a5aee62dd9491cee7f75ead0b4).\n2. Or you can just use certificates in `configs/insecure-dev-pki`, which\n   was created this way:\n\n```\n$ . ./env.sh   # Sets $EASYRSA_PKI=configs/insecure-dev-pki.\n$ /path/to/easyrsa init-pki\n$ echo Dev CA $(go list -m) | /path/to/easyrsa build-ca nopass\n$ /path/to/easyrsa --days=3650 \"--subject-alt-name=DNS:postgres\" build-server-full postgres nopass\n$ /path/to/easyrsa --days=3650 \"--subject-alt-name=DNS:localhost\" build-server-full ms-auth nopass\n$ /path/to/easyrsa --days=3650 \"--subject-alt-name=IP:127.0.0.1\" build-server-full ms-auth-int nopass\n```\n\n### Usage\n\nTo develop this project you'll need only standard tools: `go generate`,\n`go test`, `go build`, `docker build`. Provided scripts are for\nconvenience only.\n\n- Always load `env.sh` *in every terminal* used to run any project-related\n  commands (including `go test`): `source env.sh`.\n    - When `env.sh.dist` change (e.g. by `git pull`) next run of `source\n    env.sh` will fail and remind you to manually update `env.sh` to match\n    current `env.sh.dist`.\n- `go generate ./...` - do not forget to run after making changes related\n  to auto-generated code\n- `go test ./...` - test project (excluding integration tests), fast\n- `./scripts/test` - thoroughly test project, slow\n- `./scripts/test-ci-circle` - run tests locally like CircleCI will do\n- `./scripts/cover` - analyse and show coverage\n- `./scripts/build` - build docker image and binaries in `bin/`\n  - Then use mentioned above `dc` (or `docker-compose`) to run and control\n    the project.\n    - Access project at host/port(s) defined in `env.sh`.\n\n#### Cheatsheet\n\n```sh\ndc up -d --remove-orphans               # (re)start all project's services\ndc logs -f -t                           # view logs of all services\ndc logs -f SERVICENAME                  # view logs of some service\ndc ps                                   # status of all services\ndc restart SERVICENAME\ndc exec SERVICENAME COMMAND             # run command in given container\ndc stop \u0026\u0026 dc rm -f                     # stop the project\ndocker volume rm PROJECT_SERVICENAME    # remove some service's data\n```\n\nIt's recommended to avoid `docker-compose down` - this command will also\nremove docker's network for the project, and next `dc up -d` will create a\nnew network… repeat this many enough times and docker will exhaust\navailable networks, then you'll have to restart docker service or reboot.\n\n## Run\n\n### Docker\n\n```\n$ docker run -i -t --rm ghcr.io/powerman/go-monolith-example:0.2.0 -v\nmono version v0.2.0 7562a1e 2020-10-22_03:12:04 go1.15.3\n```\n\n### Source\n\nUse of the `./scripts/build` script is optional (it's main feature is\nembedding git version into compiled binary), you can use usual\n`go get|install|build` to get the application instead.\n\n```\n$ ./scripts/build\n$ ./bin/mono -h\nExample monolith with embedded microservices\n\nUsage:\n  mono [flags]\n  mono [command]\n\nAvailable Commands:\n  help        Help about any command\n  ms          Run given embedded microservice's command\n  serve       Starts embedded microservices\n\nFlags:\n  -h, --help                    help for mono\n      --log.level OneOfString   log level [debug|info|warn|err] (default debug)\n  -v, --version                 version for mono\n\nUse \"mono [command] --help\" for more information about a command.\n\n$ ./bin/mono serve -h\nStarts embedded microservices\n\nUsage:\n  mono serve [flags]\n\nFlags:\n      --example.metrics.port Port             port to serve Prometheus metrics (default 17002)\n      --example.mysql.dbname NotEmptyString   MySQL database name (default example)\n      --example.mysql.pass String             MySQL password\n      --example.mysql.user NotEmptyString     MySQL username (default root)\n      --example.port Port                     port to serve (default 17001)\n  -h, --help                                  help for serve\n      --host NotEmptyString                   host to serve (default home)\n      --host-int NotEmptyString               internal host to serve (default home)\n      --mono.port Port                        port to serve monolith introspection (default 17000)\n      --mysql.host NotEmptyString             host to connect to MySQL (default localhost)\n      --mysql.port Port                       port to connect to MySQL (default 33306)\n      --nats.urls NotEmptyString              URLs to connect to NATS (separated by comma) (default nats://localhost:34222)\n      --stan.cluster_id NotEmptyString        STAN cluster ID (default local)\n      --timeout.shutdown Duration             must be less than 10s used by 'docker stop' between SIGTERM and SIGKILL (default 9s)\n      --timeout.startup Duration              must be less than swarm's deploy.update_config.monitor (default 3s)\n\nGlobal Flags:\n      --log.level OneOfString   log level [debug|info|warn|err] (default debug)\n\n$ ./bin/mono -v\nmono version v0.2.0 7562a1e 2020-10-22_03:19:37 go1.15.3\n\n$ ./bin/mono serve\n         mono: inf      main: `started` version v0.2.0 7562a1e 2020-10-22_03:19:37\n         mono: inf     serve: `serve` home:17000 [monolith introspection]\n      example: inf     natsx: `NATS connected` url=nats://localhost:34222\n      example: inf     goose: OK    00001_down_not_supported.sql\n      example: inf     goose: OK    00002_noop.go\n      example: inf     goose: OK    00003_example.sql\n      example: inf     goose: goose: no migrations to run. current version: 3\n      example: inf     natsx: `STAN connected` clusterID=local clientID=example\n      example: inf     serve: `serve` home:17001 [JSON-RPC 2.0]\n      example: inf     serve: `serve` home:17002 [Prometheus metrics]\n      example: inf  jsonrpc2: 192.168.2.1:46344     IncExample: `handled` 1\n      example: inf  jsonrpc2: 192.168.2.1:46352     Example: `handled` 1\n      example: inf  jsonrpc2: 192.168.2.1:46356     Example: `handled` 2\n      example: ERR  jsonrpc2: 192.168.2.1:46364     Example: `failed to handle` err: unauthorized 0\n^C\n      example: inf     serve: `shutdown` [JSON-RPC 2.0]\n      example: inf     serve: `shutdown` [Prometheus metrics]\n         mono: inf     serve: `shutdown` [monolith introspection]\n         mono: inf      main: `finished` version v0.2.0 7562a1e 2020-10-22_03:19:37\n```\n\n## TODO\n\n- [ ] Add security-related headers for HTTPS endpoints (HSTS, CSP, etc.),\n  also move default host from localhost to avoid poisoning it with HSTS.\n- [ ] Embed https://github.com/powerman/go-service-example as an example\n  of embedding microservices from another repo.\n- [ ] Add example of `internal/svc/*` adapters calling some other services.\n- [ ] Add LPC (local procedure call API between embedded microservices),\n  probably using https://github.com/fullstorydev/grpchan.\n- [ ] Add complete CRUD example as per Google API Design Guide (with\n  PATCH/FieldMask), probably with generation of models conversion code using\n  https://github.com/bold-commerce/protoc-gen-struct-transformer.\n- [ ] Add NATS/STAN publish/subscribe example in `internal/sub`\n  (or maybe use JetStream instead of STAN?).\n- [ ] Switch from github.com/lib/pq to github.com/jackc/pgx.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpowerman%2Fgo-monolith-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpowerman%2Fgo-monolith-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpowerman%2Fgo-monolith-example/lists"}