[Required subject DI] Implement and understand the mechanism of DI with Go

Introduction

Various concepts such as clean architecture and onion architecture have been proposed, I studied that and was frustrated. Because I didn't know much about DI and addiction in the first place. Yes, I was studying in the wrong order. Well, I'm glad I noticed it.

Therefore, I tried to summarize DI in as much detail as possible. After reading this article, you will definitely be able to understand and implement the benefits of ** DI **

What is DI?

DI is an abbreviation for Dependency Injection

To briefly explain the DI design pattern, Define the object as an interface, and the user should use the interface instead of the implementation object. Implementation objects can be replaced by injecting them into the interface from the outside.

What is a DI container?

DI container is a ** framework ** to help realize DI

Definition of words

As I was learning, there were words I didn't understand and I was very confused. So let's match the definitions of words first.

・ Where is it from the outside? From other objects!

・ What is addiction? A B object that requires an A object In other words, the A object knows the contents of the B object. For the time being, you may think that you are dependent on New

・ You say you don't depend on it, but you depend on it, right? You're using an A object with a B object injected into the A interface! !! Is this dependent? Right? I've always thought. This was the most annoying thing. To be precise, the B object depends on the A interface. So the B object does not depend on the A object. (Sometimes it depends on abstraction) What's good about this is that B objects can be implemented without knowing A objects. Details will be described later.

Being happy by using DI

** "Why do you use DI?" This is the most important. ** ** I don't think it can be used even if I use DI without knowing this. It is important to always be aware of the ** purpose of using the method "for what". Then I will write that you can be happy by using DI.

** ・ Because it does not depend on the external DB, it is resistant to changes. ** ** Reason: Inject the implementation object through the interface, so The logic side that uses the interface can use it without being aware of the implementation object.

** ・ Easy unit testing ** Reason: Same as above, but by mocking the DB (like a fake DB) You can test without changing the logic on the side that uses the interface. Both of the results are problematic even if you switch because you do not know the outside (depend on it).

** ・ Simply makes coding easier. ** ** Reason: Suppose you have classes A and B. A needs B (dependence). In that case, A cannot be implemented without B. However, if A implements using B's interface, it can be implemented without B. Injecting a B object through the interface is the same as A using B.

Look at the code and recognize the difference

So far, we have explained the concept and mechanism of DI. From here, we will look at the actual code and understand it. Even if you don't understand at first, you should be able to understand it if you read it many times! Actually I was.

The code to be created is made up of the following three. ・ Repository layer ・ ・ ・ Layer for connecting to DB ・ Service layer ・ ・ ・ Layer that calls repository ・ Call layer ・ ・ ・ Layer that operates the whole

Dependent code

First, let's take a look at the messy code that has dependencies. (* I think I wrote this code when I started programming.)

repository layer The created DB is given directly to User Repository. (Bad thing: repository layer depends on db. I know mysql)

package repository

type UserRepository struct {
	db sql.DB
}

func NewUserRepository() *UserRepository {
	//DB creation
	db, _ := sql.Open("mysql", "root:@/database")
	//Created DB directly to repository
	return &UserRepository{*db}
}

service layer The repository is new and given directly to the service. (Bad point: service layer depends on repository.)

package service

type UserService struct {
	ur repository.UserRepository
}

func NewUserService() *UserService {
	//Repository creation
	ur := repository.NewUserRepository()
	//Created repository directly to service
	return &UserService{*ur}
}


Call layer If you new service, repository and db will be created.

package main

func main() {

	us := service.NewUserService()
	//Gonyo Gonyo using us
}

I've seen bad code once. You can see that it depends a lot. Reconfirmed just in case, a dependency is a state in which an object knows the contents of an object.

Code that introduced DI

From here, we will look at a very beautiful code that introduces DI. (* When I first see the code that uses DI, it looks very dirty ...)

I forgot one important thing before looking at the code. There are four ways to achieve DI. 1.Constructor Injection 2.Setter Injection 3.Interface Injection 4.Field Injection

This time we will use 1 Constructor Injection. I think this is the best for me.

repository layer In NewUserRepository, db is used as the constructor. Therefore, it does not depend on db. Also, return returns the interface. This also eliminates the dependency between the repository layer and the service layer.

package repository

type UserRepository interface {
	CreateUser(ctx context.Context, user *model.User)
}

type userRepository struct {
	db sql.DB
}

func NewUserRepository(db *sql.DB) UserRepository {
	return &userRepository{*db}
}

func (ur *userRepository) CreateUser(ctx context.Context, user *model.User) {
	ur.db.Query("INSERT INTO table name (column name 1),Column name 2,……)")
}

service layer NewUserService has a repository interface in the constructor. Therefore, it does not depend on the repository. Also, return returns the interface. This also eliminates the dependency between the service layer and the layer on the user side.

package service

type UserService interface {
	CreateUser(ctx context.Context, user *model.User)
}

type userService struct {
	ur repository.UserRepository
}

func NewUserService(ur repository.UserRepository) UserService {
	return &userService{ur}
}

func (us *userService) CreateUser(ctx context.Context, user *model.User) {
	us.ur.CreateUser(ctx, user)
}

Layer on the user side ur := repository.NewUserRepository(db) Any db can be accepted as long as it is a db.

us := service.NewUserService(ur) As long as ur (repository) is a repository, any repository can be accepted.

This makes it easy to replace it with another repository or DB at the time of testing. Since they do not know each other, there are no dependencies and changes can be made easily.

(* For convenience of explanation, the creation of db is written in main)

package main

func main() {
	db, err := sql.Open("mysql", "root:@/database")
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	ur := repository.NewUserRepository(db)
	us := service.NewUserService(ur)

	//Gonyo Gonyo using us
}

Summary

I have explained about DI. By actually implementing it or putting it together in an article like this, The inside of my head was very refreshing. Also, even if I think I understand it, I feel that it may not be possible if it is implemented unexpectedly. If you are studying DI, please try to implement it yourself.

References

https://recruit-tech.co.jp/blog/2017/12/11/go_dependency_injection/ https://github.com/akito0107/dicon http://inukirom.hatenablog.com/entry/di-in-go

Recommended Posts

[Required subject DI] Implement and understand the mechanism of DI with Go
Play with the password mechanism of GitHub Webhook and Python
Implement and understand union-find trees in Go
[Linux] I learned LPIC lv1 in 10 days and tried to understand the mechanism of Linux.
[Statistics] Understand the mechanism of Q-Q plot by animation.
Visualize the range of interpolation and extrapolation with python
[Ev3dev] Let's understand the mechanism of LCD (screen) control
Implement the mathematical model "SIR model" of infectious diseases with OpenModelica (example of repeating regulation and deregulation)
Implement the mathematical model "SIR model" of infectious diseases with OpenModelica
See the power of speeding up with NumPy and SciPy
"Deep copy" and "Shallow copy" to understand with the smallest example
Wagtail Recommendations (3) Understand and use the tree structure of pages
Mechanism of pyenv and virtualenv
I compared the speed of Hash with Topaz, Ruby and Python
Understand the arguments required for the HTTP request method of the Requests module
[Statistics] Visualize and understand the Hamiltonian Monte Carlo method with animation.
To improve the reusability and maintainability of workflows created with Luigi
Try to implement and understand the segment tree step by step (python)
I tried to make a mechanism of exclusive control with Go
I investigated the mechanism of flask-login!
Understand the contents of sklearn's pipeline
About the basic type of Go
Using cgo with the go command
Coexistence of Python2 and 3 with CircleCI (1.0)
I replaced the numerical calculation of Python with Rust and compared the speed
Calculate the shortest route of a graph with Dijkstra's algorithm and Python
You can also check the communication of DB and cache with curl
Adjust the bin width crisply and neatly with the histogram of matplotlib and seaborn
The golden combination of embulk and BigQuery shines even more with Digdag
I vectorized the chord of the song with word2vec and visualized it with t-SNE
Find the general terms of the Tribonacci sequence with linear algebra and Python
Read the graph image with OpenCV and get the coordinates of the final point of the graph
The story of making a sound camera with Touch Designer and ReSpeaker
Get the number of articles accessed and likes with Qiita API + Python
Make a DNN-CRF with Chainer and recognize the chord progression of music
Get and estimate the shape of the head using Dlib and OpenCV with python
Implement a model with state and behavior (3) --Example of implementation by decorator
I measured the speed of list comprehension, for and while with python2.7.
Try to separate the background and moving object of the video with OpenCV
Article that can be a human resource who understands and masters the mechanism of API (with Python code)