Go × Clean Architecture implementation pattern

Click here for Japanese version

What is Clean Architecture?

It is an architecture pattern that dissociates interest by realizing:

  • Make domain logic independent
  • Make framework independent
  • Make UI independent
  • Make any external agency Independent
  • Make domain logic easier to test

Since details are described in various articles, this article focuses on implementation patterns.

Sample App

This is an API which only performs user registration by POSTing to / users.
It’s based on manuelkiessling/go-cleanarchitecture and using gorm as an ORM library.

App Architecture

The Clean Architecture Uncle Bob

Generally, it can be divided into any layers, but you should divide according to “Common Closure Principle (CCP)”.
I divided it into four layers according to original text this time.

Directory structure

Each directory has the role of the following layers, respectively.

Thorough dependency

I will explain an important rule before entering the implementation explanation.
As mentioned earlier, dependencies must be kept in a single direction from the outside to the inside.
However, any program has inputs and outputs, and scenes where the results processed inside are output to the outside frequently appear.
In other words, the scene that you want to depend on from the inside to the outside always appears.
Solving the contradiction using the dependency inversion principle will be the key to keeping the clean architecture clean.

What is dependency inversion principle?

Briefly, the principle is that inside should not depend on the outside, but should depend on abstraction.
It’s hard to understand, so let’s actually look at the code.

This is the second layer from inside, the implementation of the app business rules layer.
This is trying to save the user data in the DB outside.
In this scene many people tend to do accessing the outer layers. But you should avoid it since if you access directly, inside depends on outside.
Therefore, we use the dependency inversion principle. Go realizes this by defining the interface.

Define the repository interface in the same layer so that it depends on this interface. And concrete type can be kept dependency from inside to outside by passing from the outside.
This is the dependency inversion principle. You’d better see here for better understanding.

Implementation example of each layer

I think that the implementation of the inner two layers will change considerably depending on the project, so here we will focus on the outer two layers.

routing

I use gin for WAF this time, but it’s easy to replace because this layer uses it as a tool.
Also, concrete type such as DB connection and logger etc are passed to inside in this layer. You could realize dependency inversion principle by doing this.

ORM

ORMapper is defined in Adapter layer.
In this case, I am devoted to solving the impedance mismatch by converting the DB optimized type to the type optimized for domain logic.
Although the adapter layer depends on abstraction, the interface defined in this layer depends to some extent on the outer library.

In principle,

‘Abstract’ should not depend on implementation details

Although this layer is the purpose of conversion, I think that it is natural to know both sides to some extent.
Please let me know if you know a better implementation pattern here.

Conclusions

As you can see, it’s just too big to make api just to register user.
You can separate concerns by abstraction, but would it be worth that much effort to you? You need to think carefully.
I think it’s often that there is no effect if the application size is small since the code becomes complicated basically when it abstracts.

It would be greatly appreciated if you could point out if there is something wrong.