Microservice library user service, user management service. It provides a Restful interface for user management, which mainly realizes the functions of user registration, querying users according to user ID or email, querying books borrowed by users, etc.
Full code:
https://github.com/Justin02180218/micro-kit
Package structure description
-
dao: data access layer
-
dto: data transmission layer
-
models: database table mapping layer
-
service: business logic layer
-
Endpoint: the concept of go kit encapsulates every method provided by the service into an endpoint.
-
Transport: go kit concept, protocol transport layer, support grpc, thrift, http and other protocols are used in combination with gin web framework.
-
user.yaml: configuration file used by the service
-
main.go: Service initiator
code implementation
Database table
First, create the user table in the library database
CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT '', `password` varchar(255) DEFAULT '', `email` varchar(255) DEFAULT '', `created_at` datetime DEFAULT NULL, `updated_at` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
models layer
Create user.go in the models layer and define the User struct corresponding to the table user:
type User struct { ID uint64 `gorm:"primary_key" json:"id" form:"id"` CreatedAt time.Time `form:"created_at" json:"created_at"` UpdatedAt time.Time `form:"updated_at" json:"updated_at"` Username string Password string Email string }
dao layer
Create a user that interacts with the database in the Dao layer_ dao.go
Define UserDao interface and its implementation:
type UserDao interface { SelectByID(id uint64) (*models.User, error) SelectByEmail(email string) (*models.User, error) Save(user *models.User) error } type UserDaoImpl struct{} func NewUserDaoImpl() UserDao { return &UserDaoImpl{} }
-
SelectByID: query user information according to user ID
-
SelectByEail: query user information according to user email
-
Save: save user information
Function implementation of UserDao interface:
func (u *UserDaoImpl) SelectByID(id uint64) (*models.User, error) { user := &models.User{} err := databases.DB.Where("id = ?", id).First(user).Error if err != nil { return nil, err } return user, nil } func (u *UserDaoImpl) SelectByEmail(email string) (*models.User, error) { user := &models.User{} err := databases.DB.Where("email = ?", email).First(user).Error if err != nil { return nil, err } return user, nil } func (u *UserDaoImpl) Save(user *models.User) error { return databases.DB.Create(user).Error }
dto layer
User in dto layer_ Create struct UserInfo and RegisterUser for data transmission in dto.go:
type UserInfo struct { ID uint64 `json:"id"` Username string `json:"username"` Email string `json:"email"` } type RegisterUser struct { Username string Password string Email string }
service layer
Create user in the service layer_ service.go
Define UserService interface and Implementation:
type UserService interface { Register(ctx context.Context, vo *dto.RegisterUser) (*dto.UserInfo, error) FindByID(ctx context.Context, id uint64) (*dto.UserInfo, error) FindByEmail(ctx context.Context, email string) (*dto.UserInfo, error) } type UserServiceImpl struct { userDao dao.UserDao } func NewUserServiceImpl(userDao dao.UserDao) UserService { return &UserServiceImpl{ userDao: userDao, } }
Function implementation of UserService interface:
func (u *UserServiceImpl) Register(ctx context.Context, vo *dto.RegisterUser) (*dto.UserInfo, error) { user, err := u.userDao.SelectByEmail(vo.Email) if user != nil { log.Println("User is already exist!") return &dto.UserInfo{}, ErrUserExisted } if err == gorm.ErrRecordNotFound || err == nil { newUser := &models.User{ Username: vo.Username, Password: vo.Password, Email: vo.Email, } err = u.userDao.Save(newUser) if err != nil { return nil, ErrRegistering } return &dto.UserInfo{ ID: newUser.ID, Username: newUser.Username, Email: newUser.Email, }, nil } return nil, err } func (u *UserServiceImpl) FindByID(ctx context.Context, id uint64) (*dto.UserInfo, error) { user, err := u.userDao.SelectByID(id) if err != nil { return nil, ErrNotFound } return &dto.UserInfo{ ID: user.ID, Username: user.Username, Email: user.Email, }, nil } func (u *UserServiceImpl) FindByEmail(ctx context.Context, email string) (*dto.UserInfo, error) { user, err := u.userDao.SelectByEmail(email) if err != nil { return nil, ErrNotFound } return &dto.UserInfo{ ID: user.ID, Username: user.Username, Email: user.Email, }, nil }
endpoint layer
Create user in the endpoint layer_ endpoint.go,
Define UserEndpoints struct, and each request corresponds to an endpoint.
type UserEndpoints struct { RegisterEndpoint endpoint.Endpoint FindByIDEndpoint endpoint.Endpoint FindByEmailEndpoint endpoint.Endpoint }
Create each endpoint
func MakeRegisterEndpoint(svc service.UserService) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(*dto.RegisterUser) user, err := svc.Register(ctx, req) if err != nil { return nil, err } return user, nil } } func MakeFindByIDEndpoint(svc service.UserService) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { id, _ := strconv.ParseUint(request.(string), 10, 64) user, err := svc.FindByID(ctx, id) if err != nil { return nil, err } return user, nil } } func MakeFindByEmailEndpoint(svc service.UserService) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { email := request.(string) user, err := svc.FindByEmail(ctx, email) if err != nil { return nil, err } return user, nil } }
transport layer
Request forwarding adopts the combination of gin web framework and go kit transport. Therefore, http_ Define a Router in util.go and return to gin.Engine
func NewRouter(mode string) *gin.Engine { gin.SetMode(mode) r := gin.New() r.Use(gin.Recovery()) return r }
In user_ The NewHttpHandler defined in transport.go returns gin.Engine, and the request handler of gin is processed by http.NewServer of go kit.
func NewHttpHandler(ctx context.Context, endpoints *endpoint.UserEndpoints) *gin.Engine { r := utils.NewRouter(ctx.Value("ginMod").(string)) e := r.Group("/api/v1") { e.POST("register", func(c *gin.Context) { kithttp.NewServer( endpoints.RegisterEndpoint, decodeRegisterRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) e.GET("findByID", func(c *gin.Context) { kithttp.NewServer( endpoints.FindByIDEndpoint, decodeFindByIDRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) e.GET("findByEmail", func(c *gin.Context) { kithttp.NewServer( endpoints.FindByEmailEndpoint, decodeFindByEmailRequest, utils.EncodeJsonResponse, ).ServeHTTP(c.Writer, c.Request) }) } return r }
Start service
Complete main.go
var confFile = flag.String("f", "user.yaml", "user config file") func main() { flag.Parse() err := configs.Init(*confFile) if err != nil { panic(err) } err = databases.InitMySql(configs.Conf.MySQLConfig) if err != nil { fmt.Println("load mysql failed") } ctx := context.Background() userDao := dao.NewUserDaoImpl() userService := service.NewUserServiceImpl(userDao) userEndpoints := &endpoint.UserEndpoints{ RegisterEndpoint: endpoint.MakeRegisterEndpoint(userService), FindByIDEndpoint: endpoint.MakeFindByIDEndpoint(userService), FindByEmailEndpoint: endpoint.MakeFindByEmailEndpoint(userService), } ctx = context.WithValue(ctx, "ginMod", configs.Conf.ServerConfig.Mode) r := transport.NewHttpHandler(ctx, userEndpoints) errChan := make(chan error) go func() { errChan <- r.Run(fmt.Sprintf(":%s", strconv.Itoa(configs.Conf.ServerConfig.Port))) }() go func() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errChan <- fmt.Errorf("%s", <-c) }() fmt.Println(<-errChan) }
start-up
Enter the library user service directory and execute go run main.go, as shown:
The service starts successfully. Listen to port 10086
Interface test
postman is used for interface test, and register interface test is carried out here. The results are as follows:
The test is successful, and a user record is successfully inserted into the database
In the next article, we begin to write a Book Management micro service: library book service
Full code:
https://github.com/Justin02180218/micro-kit
More distributed album [Architecture album] series, please pay attention to the official account.