- Published on
Golang Best Practice Part 1
- Authors
- Name
- Abdelfettah Latrache
- @AbdelVA
Let's explore the Golang Project structure:
First, the project structure is very important in terms of writing clean code and being able to test your code efficiently.
Here is an example of a Golang project structure:
- In the root directory:
- cmd/
- ProgrameName/
- main file
- environment/
- handler/
- services/
- repository/
- model/
- helper/
- utils/
- mocks/
- adapters/
let’s explore this structure more in detail.
in the root directory, we have a folder called cmd stands for command, in this folder we will put our main go function.
environment/
- The environment directory contains constant variables declaration and constant configurations.
handler/
- the Handler directory, which is responsible for handling the CRUDE operation requests that we have in the main such as “GET/POST/PUT/DELETE” requests.
- Should be no logic included in the handler, its responsibility is to service the requests to the dedicated service.
services/
- The Services directory is responsible for handling the logic of the operation.
- Also, the service acts as a liaison between data access objects (DAO) and data Transfer objects (DTO).
- The service files, should not have any DTO init e.g queries to a database.
repository/
- The repository directory is responsible for handling DAO operations such as database queries.
- The repository files shouldn’t handle any logic on it
adapter/
- The adapter directory is responsible for third-party services e.g Kafka service, Email service (Mail hog)
utils/
- The Utils directory contains files and methods that are utils for your services.
- The Utils methods must contain only logic transformation e.g string transformation
helper/
- The Helper directory contains helper functions that help the logic of your services e.g mapping your data, and transforming models.
mocks/
- The mocks directory contains the mocked files and mocked methods that are to be used in the tests
As you already know the key to better understanding is practice, so let’s get our hands dirty
I have created a bootstrap project structure that contains the project structure shown above and also contains a test case for unit tests.
so clone this repository and follow along
git clone [https://github.com/LatVAlY/golang_bootstrap_project.git](https://github.com/LatVAlY/golang_bootstrap_project.git)
As you can see we have the structure explained above with some test cases and services
After cloning the repository, open the main file in your editor and you will find the main function
with an Echo server that has a GET request user endpoint as shown below.
e:= echo.New()
g := e.Group("/v1")
g.GET("/users/:userId", userHandler.User)
I’m not going to deep dive into echo, but I will definitely point out some important code structure factors:
- using versioning /v1/
- This would make your API mentionable and scalable
- Grouping API domains
- Would make clear your business domain and structure
Opening the user service in the service folder you will find the following:
type UserService interface
type UserServiceImpl struct
The interface:
the interface would help you expose your methods to be invoked by another service, it would also make it so easy to mock your service using the interface
The struct:
everything that you use globally in your service and has to be initialized from the upper level, need to be in the struct
By following the project structure above, we are going to dive straight to the unit testing phase
The service folder:
the service folder must contain only the logic for your application, and should not be dependent on any third-party library (e.g database, REST API…)
Data Transfer Object And Data Access Object: the service methods are the liason between the repository (third party libraries and the APIs), as you can see we have connection to the UserRepository interface
u.UserRepository.FindActiveUserWithUserId(userId)
Unit tests for our service methods:
The unit test for the service is quite easy dons’t require so much data in order to test your functions,
Unit tests had to be independent from the global scope, means that we cannot use the liasons and connection to the other third party services and REST APIs, for that it requires mocks.
Mocking :
In order to mock our service dependencies such as repository interfaces we are going to use the mockery library
brew install mockery
Using mockery by targeting the interface we wanted to mock, as the interface of a Golang project holds the methods that wanted to be exposed to the outside
mockery --name=UserRepo
Executing this command would generate the mock file for your dependencies methods in the mock folder.
Now we are going to generate our user service test file and pass our mocked interface into the struct of our service
// Preparing the mocked methods with input and output test data.
func getUserRepoMock() repository.UserRepo {
userRepoMock := new(mocks.UserRepo)
userRepoMock.On("FindActiveUserWithUserId", "LatVAlY").Return(&model.UserDTO{
Id: 1,
FirstName: "Abdel",
LastName: "Latrache",
UserId: "LatVAlY",
Email: "test@test.com",
}, nil)
return userRepoMock
}
// passing the mocked repository to our user service struct
name: "ShouldReturnUserObjectWithGivenUserId",
fields: fields{
UserRepository: getUserRepoMock(),
},
With this we were able to cover the service dependencies and mock them in oder to test our logic in the user service