Clean Architecture with GO
Clean Architecture is designed to separate concerns by organizing code into several layers with a very explicit rule which enables us to create a testable and maintainable project. In this article, I’m going to highlight how Clean Architecture works in Go.
You can view the full codebase at:
The target of readers in this post is who:
- knows the basic idea of Clean Architecture
- wants to implement Go with Clean Architecture
So if you are not familiar with it, you can read some recommended articles to catch up.
- The Clean Code Blog
- Clean Architecture: Part 2 — The Clean Architecture
- Better Software Design with Clean Architecture
The darkness of software design
If you’ve been working as a programmer, you’ve probably run into the code that:
- is painful to add new features
- is too difficult to debug
- is hard or impossible to test without dependencies like a web server or database
- get a view and business logic mixed in widely, even can’t be separated
- is hard to understand what its purpose for
- has a lot of jobs only in one function and too long.
You might be even scared of committing a tiny little change because you don’t know how much it affects other functions and there are no unit tests, even if there is, it’s too complicated and mysterious, which means useless. Even if it is structured in MVC or MVVM, there are still issues around the project, of which business logic is leaking into controllers, the domain model is used across the entire project for different purposes and so on.
Advantages in Clean Architecture
Clean Architecture is one of the software designs of organizing the codebase and provides a solution to these issues you’ve ever seen. By introducing Clean Architecture you’ll get your code:
- highly decoupled from any UI, web framework or the database
- focusing more on business logic
- easily testable
- maintainable, visible and easier to understand
In Clean Architecture the codebase should be flexible and portable. It should not be dependent on any specific web framework or database, which means you could switch it to an entirely new platform. For instance, at the beginning of the project you use RDB but somehow even if you are forced to replace it with NoSQL for some reason, you can switch it without changing any business logic.
The only rules you need to know
When you implement Clean Architecture, you might try to follow the circle diagram completely, but actually, the author said:
Only Four Circles? No, the circles are schematic… There’s no rule that says you must always have just these four…
So you don’t need to implement it as it is, just takes it as an example for understanding the architecture. But there are important rules that you need to follow.
1. The dependencies can only point inward
In Clean Architecture the details like a web framework and databases are in the outer layers while important business rules are in the inner circles and have no knowledge of anything outside world. Following Acyclic Dependencies Principle(ADP), the dependencies only point inward in the circle, not point outward and no circulation.
2. Separation of details and abstracts
The details in Clean Architecture are the data, framework, database, and API. Using the details in the core layer means it violates The Dependency Rule. It should always be dependent on an abstract interface not specific knowledge of the details so it will be flexible and maintainable. As seen at the right of the diagram, it shows that Controller and Presenter are dependent on Use Case Input Port and Output Port which is defined as an interface, not specific logic(the details). But how is it possible to work without knowing the details in the outer layer?
We’ve learned a basic of the rule in Clean Architecture. Next, take a look at the layers before implementing them.
Entities is a domain model that has wide enterprise business rules and can be a set of data structures and functions.
ex.) A struct type of user, book and author.
Use cases contain application business rules using a domain model and have Input Port and Output Port.
Input Port is in charge of handling data from the outer layer and defined as abstract.
Output Port is in charge of handling data from Use cases to the outer layer and defined as abstract.
Interface Adapter handles the communication with the inner and the outer layer. It has only concerns about technological logic, not business logic.
Controllers are a set of a specific implementation of Input Port in Use Cases.
ex.) Convert form data before saving it in database
Presenter is a set of a specific implementation of Output Port in Use Cases.
ex.) Convert data from the database before passing to View
Frameworks and Drivers
Frameworks and drivers contain tools like databases, frameworks or API and basically do not have very much code.
ex.) API, database and web framework
Implementation of Clean Architecture in GO
Next, we’ll introduce Clean Architecture to Go and see how this works using a real use case. Let’s suppose that we’ll create a simple API responding to user data.
Now that we have the project structure as below:
│ └── model
│ └── user.go
│ ├── datastore
│ │ └── db.go
│ └── router
│ └── router.go
│ ├── controller
│ │ ├── app_controller.go
│ │ ├── context.go
│ │ └── user_controller.go
│ ├── presenter
│ │ └── user_presenter.go
│ └── repository
│ └── user_repository.go
│ ├── registry.go
│ └── user_registry.go
│ ├── presenter
│ │ └── user_presenter.go
│ ├── repository
│ │ └── user_repository.go
│ └── interactor
│ └── user_interactor.go
Each directory has a role of each layer as following:
There’s no rule that you must always have just these four layers in Clean Architecture so actually, it depends on the teams or the projects.
First off, we create a
user domain model as Entities layer in
Use Cases Layer
In Use cases layer we have three directories,
interactor is in charge of Input Port and
presenter is in charge of Output Port.
interactor has a set of methods of specific application business rules depending on
Let’s create a
findAll interface in
Get as a function of finding all users in
Interface Adapter Layer
In Interface Adapter layer, there are
controllers is in charge of the C of MVC model and handles API requests that come from the outer layer.
repository is a specific implementation of
repository in Use Cases and stores any database handler as Gateway.
GetUsers it calls
usecase and respond to user data.
And we add
That is a set of controllers defined to use in
ResponseUsers handles user data before passing it to view basically but in this case, we add some text to the user name before responding to the client.
FindAll is an actual implementation of the interface in
usecase/repository/user_repository.go that we’ve defined above and it’ll be injected as a method of handling database.
Frameworks and Drivers Layer
registry plays a role in resolving dependencies using constructor injection.
NewRegistry takes a
db instance to pass down to
NewUserController generates a controller with interactor.
NewUserInteractor returns an interactor with a repository and presenter.
NewUserRepository returns repository of interface passed with a database instance which fulfills
Now that we’re ready to boot a server with users endpoint. Let’s add some code to
echo framework for routing and pass it to router and controllers created by
Let’s try to see how it works at
http://localhost:8080/users : (Make sure that there’s some record in your database)
It seems to work fine.
That’s it. We’ve created a very simple API responding to a user data with Clean Architecture. I like the idea of separating the different aspects of the product as highly decoupled layers.
I hope you will find the benefits I highlighted in this article.
You can view the final codebase at: