In Golang Posted May 3rd, 2021
There are many software architectures that prescribe certain ways of developing software. Some emerging architecture styles include Hexagonal Architecture, Screaming Architecture, DCI, Onion Architecture and others.
A common point in all these architectures is that of separation of concerns. That is, they divide different functionalities into different layers and let each layer handle their own job. Based on all these architectures, Robert Martin a.k.a “Uncle Bob” has synthesized an architecture known as the “clean architecture”.
In this article, let us first understand what Clean architecture is and then we will discuss implementing clean architecture in Go.
Clean architecture is a layered software architecture which follows certain principles other than that of separation of concerns. They are:
Dependency rule: This rule states that dependencies must only point inward(when the entire set of layers are seen as a circle). The outer layers consist of details like frameworks, databases while the inner layers must contain core business logic.
The modules that are deeper in the layers must not know anything about outward layers.
This principle has its roots in the principle above, but this is more of an implementation guidance. When trying to ensure that dependencies only point inwards, you might run into some dead ends.
Dependency inversion principle helps in solving them. There are two rules in this principle:
Modules at a higher level must not depend on lower level modules. Instead, both should depend on interfaces Abstractions must be separated from details. The concrete implementations must make use of interfaces.
Generally, while describing, 4 layers of clean architecture are shown:
However, this is only a scheme and as such you may have as many layers as you want depending on the needs of the project.
Let us now see the different basic layers of Clean architecture:
How clean architecture looks in Golang?
Consider we are building a basic API for accessing employee information. This is what the directories look like:
domain
model
employee.g
infrastructure
datastorage
database.go
interface
controller
application_controller.go
context.go
employee_controller.go
presenter
employee_presenter.go
‘ repository
employee_repository.go
main.go
registry
registry.go
employee_registry.go
usecases
repository
interactor
employee_interactor.go
Here, each directory corresponds to a particular layer in the Clean Architecture, namely:
Entities corresponds to domain
Interface Adapters corresponds to interface
Use Case correspond to use cases
We will now describe how the code exists generally in all the layers:
1. Entities:
Here, we create and store the basic employee model.
employee.go
package model
import “time”
type Employee struct {
ID uint `gorm:”primary_key” json:”id”` Name string `json:”name”` Age string `json:”age”` Salary uint `json:”salary”` CreatedAt time.Time `json:”created_at”` UpdatedAt time.Time `json:”updated_at”` DeletedAt time.Time `json:”deleted_at”`
}
func (Employee) TableName() string { return “employees” }
2. Use Cases:
This layer contains three directories, namely: interactor, presenter and repository. The input port is the interactor, the output port is the presenter. The Interface also contains a certain set of methods with the application’s business rules.
3. Interface Adapter Layer:
This layer too contains three directores: controllers, presenters and repositories. In the whole MVC Architecture, (Model-View-Controller), controllers directory corresponds to the controllers part.
The repository part here is the concrete implementation of the repository that is defined prior in use cases.
4. Frameworks and Drivers Layer: Here, we define the essential database and routing logic.
Registry: Registry helps in resolving dependencies by making use of constructor injection.
package registry
import (
“github.com/jinzhu/gorm” //controller
)
type registry struct {
db *gorm.DB
type Registry interface {
NewAppController() controller.AppController
func NewRegistry(db *gorm.DB) Registry {
return ®istry{db}
func (r *registry) NewAppController() controller.AppController {
return r.NewUserController()
Upon that, we also need to write employee_registry.go creating all the necessary employeeControllers and interactors.
After all the parts are complete, we can code our server and the entrypoint.
main.go:
package main
“fmt” “log”
//All the needed imports… )
func main() {
config.ReadConfig()
db := datastore.NewDB() db.LogMode(true) defer db.Close()
r := registry.NewRegistry(db)
e := echo.New() e = router.NewRouter(e, r.NewAppController())
fmt.Println(“Server listen at http://localhost” + “:” + config.C.Server.Address) if err := e.Start(“:” + config.C.Server.Address); err != nil { log.Fatalln(err) }
Now, if you go to localhost with the specified port and enter the route of employees, you can get the output!
Final Words:
You have seen a glimpse of what Clean architecture entails and how its implementation could look in Golang development. Working this way might be hard at first, but when you have to change some aspects of your application or add new features, this proves handy and you will thank yourself for following good architecture patterns for your application.
Δ
Do you have a Project we can assist you with?
Use our Project Planner
[recaptcha]