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.

What is Clean Architecture?

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.

Use Dependency inversion principle:

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.

Different layers of clean architecture:

Generally, while describing, 4 layers of clean architecture are shown:

  1. Entities:
    This is the base model of the domain where the business rules of the enterprise are kept.
  2. Use cases:
    These are the specific applications of business rules using entities and include an Input port and an output port which are both interfaces.
  3. Interface Adapter:
    This layer manages the communication between the inner and outer layers. The main point of this layer is only technological logic but not core business logic.
  4. Frameworks and Drivers:
    As the name indicates, this layer contains all the frameworks and tools that help everything function.

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

  presenter

    employee_presenter.go

  repository

    employee_repository.go

  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.

registry.go

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 &registry{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

import (

“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.