如何在go中使用依赖注入

lpwwtiir  于 2023-06-19  发布在  Go
关注(0)|答案(3)|浏览(104)

我正在学习依赖注入,并希望在gRPC项目中使用它,但我不确定如何做到这一点。我有这个gRPC服务

service UserService {
    rpc GetUser(GetUserRequest) returns (User);
}

message GetUserRequest {
    oneof prop {
        string id = 1;
        string email = 2;
        string phoneNumber = 3;
    } 
}

message User {
    string firstName = 1;
    string lastName = 2;                                            
    ...
}

然后在main函数中定义GetUser并初始化服务器
main.go

package main

import (
    pb "proto/user/go"
    "context"
    "net"
    "fmt"
)

type Server struct {
    pb.UserServiceServer
}

func (s *Server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
    var user pb.User
    var err error
    switch req.Prop.(type) {
    case *pb.GetUserRequest_Email:
        user, err = GetUserByEmail(req.GetEmail())
    case *pb.GetUserRequest_Id:
        user, err = GetUserById(req.GetId())
    case *pb.GetUserRequest_PhoneNumber:
        user, err = GetUserByPhoneNumber(req.GetPhoneNumber())
    }
    return &user, err
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        fmt.Println("Failed to listen for incoming requests")
    }
    s := grpc.NewServer()
    cs := Server{}
    pb.RegisterUserServiceServer(s, cs)
    err = s.Serve(lis)
    if err != nil {
        fmt.Println("Failed to start server: ", err)
    }
}

GetUserByEmailGetUserByIdGetUserByPhoneNumber位于另一个封装中
user/get_user.go

func GetUserById(id string) (pb.User, error) {
    // call the database to get the user
}

所以我的问题是,为了使测试GetUserByEmailGetUserByIdGetUserByPhoneNumber更容易,我必须依赖注入数据库连接,我不知道最好的方法是什么。我的第一个想法是在Server结构体中添加一个名为db的字段,然后在GetUser中将其传递给函数,使其看起来像这样。

type Server struct {
    pb.UserServiceServer
    db *dynamodb.Client
}

func (s *Server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
    var user pb.User
    var err error
    switch req.Prop.(type) {
    case *pb.GetUserRequest_Email:
        user, err = GetUserByEmail(req.GetEmail(), s.db)
    case *pb.GetUserRequest_Id:
        user, err = GetUserById(req.GetId(), s.db)
    case *pb.GetUserRequest_PhoneNumber:
        user, err = GetUserByPhoneNumber(req.GetPhoneNumber(), s.db)
    }
    return &user, err
}

我觉得使用参数传递数据库不是一个好的做法,但我可能是错的。任何帮助将不胜感激。

whlutmcx

whlutmcx1#

最简单的说,依赖注入只是为独立的东西添加一个参数。它将使用某些东西的代码与创建/销毁某些东西的代码分开。这个模式大致是“创造东西,使用它,处理东西”。
“创建”部分可以是多个步骤。它可以在层中完成(每个层处理一件事的“创建”和“处理”),或者它可以从多个部分创建对象(并依次组合这些部分),但我不认为在所有情况下都有一个“最好”的方法。
一般来说,“dispose”部分是“create”部分的镜像,以保持一致(减去任何预先存在的,或者您想要保留的)。但是传递什么或如何传递的细节取决于您希望如何构建代码。

qlfbtfca

qlfbtfca2#

我目前正在使用Kratos,我认为它完美地展示了依赖注入。您可以探索其layoutinjection以了解详细信息。
简单地说:
1.抽象数据库访问,即接口而不是struct,就像你在pb.RegisterUserServiceServer(s, cs)中所做的那样,你应该有数据库接口来声明所需的方法。

type dbInterface interface{
   GetUser(ctx context.Context, id int64) (*pb.User, error)
   ...
}

1.为接口实现结构

type dbStruct struct {
    // Your real client here
    ...
}

// assert implementation
var _ dbInterface = (*dbStruct)(nil)

// implementations
func  (s *dbStruct) GetUser(ctx context.Context, id int64) (*pb.User, error) {
    ...
}
  1. Wire要注入的 * 提供程序 *。
func NewDB(...) (*dbStruct, error)
func NewServer(...) (*Server, error)
vh0rcniy

vh0rcniy3#

传递数据库连接器的依赖项(通过按链接传递)没有任何问题。但是,我建议您在单独的包中创建一个结构,在其中描述数据库客户端接口,并将必要的方法挂在这个结构上。ex

相关问题