- Notifications
You must be signed in to change notification settings - Fork626
Clean Architecture template for Golang services
License
evrone/go-clean-template
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Clean Architecture template for Golang services
The purpose of the template is to show:
- how to organize a project and prevent it from turning into spaghetti code
- where to store business logic so that it remains independent, clean, and extensible
- how not to lose control when a microservice grows
Using the principles of Robert Martin (aka Uncle Bob).
Go-clean-template is created &supported byEvrone.
This template implements three types of servers:
- AMQP RPC (based on RabbitMQ astransportandRequest-Reply pattern)
- MQ RPC (based on NATS astransportandRequest-Reply pattern)
- gRPC (gRPC framework based on protobuf)
- REST API (Fiber framework)
# Postgres, RabbitMQ, NATSmake compose-up# Run app with migrationsmake run
# DB, app + migrations, integration testsmake compose-up-integration-testmake compose-up-all
Check services:
- AMQP RPC:
- URL:
amqp://guest:guest@127.0.0.1:5672/ - Client Exchange:
rpc_client - Server Exchange:
rpc_server
- URL:
- NATS RPC:
- URL:
nats://guest:guest@127.0.0.1:4222/ - Server Exchange:
rpc_server
- URL:
- REST API:
- gRPC:
- URL:
tcp://grpc.lvh.me:8081|tcp://127.0.0.1:8081 - v1/translation.history.proto
- URL:
- PostgreSQL:
postgres://user:myAwEsOm3pa55@w0rd@127.0.0.1:5432/db
- RabbitMQ:
- http://rabbitmq.lvh.me |http://127.0.0.1:15672
- Credentials:
guest/guest
- NATS monitoring:
- http://nats.lvh.me |http://127.0.0.1:8222/
- Credentials:
guest/guest
Configuration and logger initialization. Then the main function "continues" ininternal/app/app.go.
The twelve-factor app stores config in environment variables (often shortened toenv vars orenv). Env vars are easyto change between deploys without changing any code; unlike config files, there is little chance of them being checkedinto the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java SystemProperties, they are a language- and OS-agnostic standard.
Config:config.go
Example:.env.example
docker-compose.yml usesenv variables to configure services.
Swagger documentation. Auto-generated byswag library.You don't need to correct anything by yourself.
Protobuf files. They are used to generate Go code for gRPC services.The proto files are also used to generate documentation for gRPC services.You don't need to correct anything by yourself.
Integration tests.They are launched as a separate container, next to the application container.
There is always oneRun function in theapp.go file, which "continues" themain function.
This is where all the main objects are created.Dependency injection occurs through the "New ..." constructors (see Dependency Injection).This technique allows us to layer the application using theDependency Injection principle.This makes the business logic independent from other layers.
Next, we start the server and wait for signals inselect for graceful completion.Ifapp.go starts to grow, you can split it into multiple files.
For a large number of injections,wire can be used.
Themigrate.go file is used for database auto migrations.It is included if an argument with themigrate tag is specified.For example:
go run -tags migrate ./cmd/app
Server handler layer (MVC controllers). The template shows 3 servers:
- AMQP RPC (based on RabbitMQ as transport)
- gRPC (gRPC framework based on protobuf)
- REST API (Fiber framework)
Server routers are written in the same style:
- Handlers are grouped by area of application (by a common basis)
- For each group, its own router structure is created, the methods of which process paths
- The structure of the business logic is injected into the router structure, which will be called by the handlers
Simple RPC versioning.For v2, we will need to add theamqp_rpc/v2 folder with the same content.And in the fileinternal/controller/amqp_rpc/router.go add the line:
routes:=make(map[string]server.CallHandler){v1.NewTranslationRoutes(routes,t,l)}{v2.NewTranslationRoutes(routes,t,l)}
Simple gRPC versioning.For v2, we will need to add thegrpc/v2 folder with the same content.Also add thev2 folder to the proto files indocs/proto.And in the fileinternal/controller/grpc/router.go add the line:
{v1.NewTranslationRoutes(app,t,l)}{v2.NewTranslationRoutes(app,t,l)}reflection.Register(app)Simple REST versioning.For v2, we will need to add thehttp/v2 folder with the same content.And in the fileinternal/controller/http/router.go add the line:
apiV1Group:=app.Group("/v1"){v1.NewTranslationRoutes(apiV1Group,t,l)}apiV2Group:=app.Group("/v2"){v2.NewTranslationRoutes(apiV2Group,t,l)}
Instead ofFiber, you can use any other http framework.
Inrouter.go and above the handler methods, there are comments for generating swagger documentationusingswag.
Entities of business logic (models) can be used in any layer.There can also be methods, for example, for validation.
Business logic.
- Methods are grouped by area of application (on a common basis)
- Each group has its own structure
- One file - one structure
Repositories, webapi, rpc, and other business logic structures are injected into business logic structures(seeDependency Injection).
A repository is an abstract storage (database) that business logic works with.
It is an abstract web API that business logic works with.For example, it could be another microservice that business logic accesses via the REST API.The package name changes depending on the purpose.
RabbitMQ RPC pattern:
- There is no routing inside RabbitMQ
- Exchange fanout is used, to which 1 exclusive queue is bound, this is the most productive config
- Reconnect on the loss of connection
In order to remove the dependence of business logic on external packages, dependency injection is used.
For example, through the New constructor, we inject the dependency into the structure of the business logic.This makes the business logic independent (and portable).We can override the implementation of the interface without making changes to theusecase package.
package usecaseimport (// Nothing!)typeRepositoryinterface {Get()}typeUseCasestruct {repoRepository}funcNew(rRepository)*UseCase {return&UseCase{repo:r,}}func (uc*UseCase)Do() {uc.repo.Get()}
It will also allow us to do auto-generation of mocks (for example withmockery) andeasily write unit tests.
We are not tied to specific implementations in order to always be able to change one component to another.If the new component implements the interface, nothing needs to be changed in the business logic.
Programmers realize the optimal architecture for an application after most of the code has been written.
A good architecture allows decisions to be delayed to as late as possible.
Dependency Inversion (the same one from SOLID) is the principle of dependency injection.The direction of dependencies goes from the outer layer to the inner layer.Due to this, business logic and entities remain independent from other parts of the system.
So, the application is divided into 2 layers, internal and external:
- Business logic (Go standard library).
- Tools (databases, servers, message brokers, any other packages and frameworks).
The inner layer with business logic should be clean. It should:
- Not have package imports from the outer layer.
- Use only the capabilities of the standard library.
- Make calls to the outer layer through the interface (!).
The business logic doesn't know anything about Postgres or a specific web API.Business logic has an interface for working with anabstract database orabstract web API.
The outer layer has other limitations:
- All components of this layer are unaware of each other's existence. How to call another from one tool? Not directly,only through the inner layer of business logic.
- All calls to the inner layer are made through the interface (!).
- Data is transferred in a format that is convenient for business logic (
internal/entity).
For example, you need to access the database from HTTP (controller).Both HTTP and database are in the outer layer, which means they know nothing about each other.The communication between them is carried out throughusecase (business logic):
HTTP > usecase usecase > repository (Postgres) usecase < repository (Postgres) HTTP < usecaseThe symbols > and < show the intersection of layer boundaries through Interfaces.The same is shown in the picture:
Or more complex business logic:
HTTP > usecase usecase > repository usecase < repository usecase > webapi usecase < webapi usecase > RPC usecase < RPC usecase > repository usecase < repository HTTP < usecase- Entities are structures that business logic operates on.They are located in the
internal/entityfolder.In MVC terms, entities are models. - Use Cases is business logic located in
internal/usecase.
The layer with which business logic directly interacts is usually called theinfrastructure layer.These can be repositoriesinternal/usecase/repo, external webapiinternal/usecase/webapi, any pkg, and othermicroservices.In the template, theinfrastructure packages are located insideinternal/usecase.
You can choose how to call the entry points as you wish. The options are:
- controller (in our case)
- delivery
- transport
- gateways
- entrypoints
- primary
- input
The classic versionofClean Architecture was designed forbuilding large monolithic applications and has 4 layers.
In the original version, the outer layer is divided into two more, which also have an inversion of dependenciesto each other (directed inward) and communicate through interfaces.
The inner layer is also divided into two (with separation of interfaces), in the case of complex logic.
Complex tools can be divided into additional layers.However, you should add layers only if really necessary.
In addition to Clean architecture,Onion architecture andHexagonal (Ports and adapters) are similar to it.Both are based on the principle of Dependency Inversion.Ports and adapters are very close toClean Architecture, the differences are mainly in terminology.
About
Clean Architecture template for Golang services
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.


