当前位置: 首页 > news >正文

golang项目三层依赖架构,自底向上;依赖注入trpc\grpc

1. repo 层面

1.1 依赖、业务逻辑部分

  • repo层的再底层依赖是数据库db,因此需要结构体的成员包含一个依赖Dependency,该依赖的内容DBGetter可由于外部传入
  • 将依赖初始化封装函数(上层调用进行底层初始化)
  • 业务逻辑,需要封装好返回的结构体
package accountimport ("context""fmt""github.com/SIN5t/tRPC-go/app/user/entity""github.com/SIN5t/tRPC-go/proto/user"
)
import "trpc.group/trpc-go/trpc-database/mysql"type UserAccountRepository struct {dep Dependency
}type Dependency struct {DBGetter func(context.Context) (mysql.Client, error)
}func (r UserAccountRepository) initUserAccountRepository(dep Dependency) error {r.dep = depreturn nil
}// QueryUserAccountByName 业务逻辑,实现service的interface中的方法
func (r UserAccountRepository) QueryUserAccountByName(ctx context.Context, req user.GetAccountByUserNameRequest) (*entity.Account, error) {dbClient, err := r.dep.DBGetter(ctx)if err != nil {return nil, fmt.Errorf("获取DB失败(%w)", err)}var userAccountItems []userAccountItemquery := fmt.Sprintf("select * from %s where username = ? LIMIT 1", userAccountItem{}.TableName())if err := dbClient.Select(ctx, &userAccountItems, query, req.GetUsername()); err != nil {return nil, fmt.Errorf("查询db失败(%w)", err)}if len(userAccountItems) == 0 {return nil, nil}return userAccountItems[0].ToEntity(), nil
}

1.2 统一repo仓库管理

  • 如果需要每一个数据库都单独进行依赖注入,代码过于冗余,因此这个进行整个微服务统一的依赖管理
  • 可以从1.1中知道,account业务逻辑初始化需要一个mysql client的DBgetter依赖,因此就从这里传进去。
  • 1.1中还封装了account的初始化,这里也只要传递相关依赖,调用初始化方法,就能初始化account,这样大的Repo结构体就包含了account
  • 对外封装好,并暴露了NewRepo方法,只需要传入db连接名,就会生成一个client,并完成更加底层如account等的依赖构建。
  • 最后外部只需要调用NewRepo方法,完成各种底层库表依赖注入,返回的Repo结构体就可以随意调用其包含的==其他表(如account等)==的方法了
package repoimport ("context""fmt""github.com/SIN5t/tRPC-go/app/user/repo/account""trpc.group/trpc-go/trpc-database/mysql"
)type Repo struct {account.UserAccountRepository
}
type Dependency struct {UserAccountDBClientName string
}// NewRepo 新建 user 服务所需的 repo 依赖全集
func NewRepo(d Dependency) (*Repo,error) {repo := &Repo{}// 初始化用户仓库accountDep := account.Dependency{DBGetter: func(ctx context.Context) (mysql.Client, error) {return mysql.NewUnsafeClient(d.UserAccountDBClientName),nil},}if err := repo.InitUserAccountRepository(accountDep); err != nil{return nil,fmt.Errorf("初始化用户仓库失败(%w)",err)}return repo,nil}

2. main

为什么先说main再说service呢?
因为上面刚刚说完,提供了一个NewRepo的封装,返回一个Repo结构体,这个结构体 表示user 服务所需的 repo 依赖全集可以拿到user的所有服务方法,因此这里直接跳到调用NewRepo的地方先分析逻辑

package mainimport ("github.com/Andrew-M-C/trpc-go-demo/app/user/repo""github.com/Andrew-M-C/trpc-go-demo/app/user/service""trpc.group/trpc-go/trpc-go""trpc.group/trpc-go/trpc-go/log"
)func main() {s := trpc.NewServer()r, err := initializeRepo()if err != nil {log.Fatalf("初始化 repo 失败: %v", err)}if err := service.RegisterUserService(s, r); err != nil {log.Fatalf("注册用户服务失败: %v", err)}if err := s.Serve(); err != nil {log.Fatalf("启动服务失败: %v", err)}
}func initializeRepo() (*repo.Repo, error) {dep := repo.Dependency{UserAccountDBClientName: "db.mysql.userAccount",}r, err := repo.NewRepo(dep)if err != nil {return nil, err}return r, nil
}
  • 代码其实很简单,就是把Repo结构体New出来,用于服务注册

问题:为什么使用Repo结构体进行服务注册?
还是那个答案:Repo结构体,这个结构体 表示user 服务所需的 repo 依赖全集可以拿到user的所有服务方法,因此只需要注册一次即可完成所有user底层管理。

service

问题又来了:proto中定义的service服务主体,GetAccountByUsername()方法是生成的接口需要实现的方法,但之前repo层实现的方法是QueryAccountByUsername()方法,想要将Repo结构体注册给服务,一定需要实现接口的方法,这下没实现,怎么办?

答:这正是service层的作用,实际上,我们必须实现proto中定义的接口,因此也很简单:在service层中搞一个结构体:userImpl,实现接口中的方法GetAccountByUsername即可!

追问:之前的Repo呢?

答:GetAccountByUsername的实现肯定也是要靠底层Repo的,因此service层的依赖就是Repo! 正好Repo还包含所有user服务的方法!

  • 如何优雅实现上面的依赖呢?如下使用interface:Repo{}实现了所有repo层的方法,实际上Repo就是下面这个Dependency的一种implemence,无需显示说明,并且可以传入
  • 后续这个interface再有其他方法也是Repo底层的,只要一个Repo结构体传进来就可以搞定所有,符合golang中的多态特点!
// Dependency 表示用户服务初始化依赖
type Dependency interface {// QueryAccountByUsername 通过用户名查询帐户信息, 如果帐户不存在则返回 (nil, nil)QueryAccountByUsername(ctx context.Context, username string) (*entity.Account, error)// 后续还有很多其他方法...
}type userImpl struct {dep Dependency
}
  • 最后,在service的业务逻辑中,GetAccountByUsername方法调用底层的QueryAccountByUsername即可。
    完整代码如下:
package serviceimport ("context""github.com/Andrew-M-C/trpc-go-demo/app/user/entity""github.com/Andrew-M-C/trpc-go-demo/proto/user""trpc.group/trpc-go/trpc-go/log""trpc.group/trpc-go/trpc-go/server"
)// RegisterUserService 注册用户服务
func RegisterUserService(s server.Service, d Dependency) error {impl := &userImpl{dep: d}user.RegisterUserService(s, impl)return nil
}// Dependency 表示用户服务初始化依赖
type Dependency interface {// QueryAccountByUsername 通过用户名查询帐户信息, 如果帐户不存在则返回 (nil, nil)QueryAccountByUsername(ctx context.Context, username string) (*entity.Account, error)
}type userImpl struct {dep Dependency
}// GetAccountByUserName 根据用户名获取帐户信息
func (impl *userImpl) GetAccountByUserName(ctx context.Context, req *user.GetAccountByUserNameRequest,
) (rsp *user.GetAccountByUserNameResponse, _ error) {rsp = &user.GetAccountByUserNameResponse{}u, err := impl.dep.QueryAccountByUsername(ctx, req.Username)if err != nil {log.ErrorContextf(ctx, "查询 username '%s' 失败: %v", req.Username, err)rsp.ErrCode = -1 // TODO: 采用规范的错误码定义rsp.ErrMsg = err.Error()return}if u == nil {log.InfoContextf(ctx, "username '%s' 不存在", req.Username)rsp.ErrCode = 404rsp.ErrMsg = "用户不存在"return}rsp.UserId = u.IDrsp.Username = u.Usernamersp.PasswordHash = u.PasswordHashrsp.ErrMsg = "success"return
}

时间:上午3.55,睡不着码点字emmmm


http://www.mrgr.cn/news/71969.html

相关文章:

  • Spring学习笔记_37——@RequestMapping衍生注解
  • 解决msvcr100.dll丢失的方法,5个实测可靠的解决方法
  • Sam Altman:年底将有重磅更新,但不是GPT-5!
  • 深圳华为展厅:30寸OLED透明屏中控桌引领科技新风尚
  • Wxml2Canvas小程序将dom转为图片,bug总结
  • RHCE-第四章:ssh远程连接服务器
  • 51c视觉~合集6
  • 【含文档】基于ssm+jsp的在线网课管理系统(含源码+数据库+lw)
  • 音视频入门基础:MPEG2-TS专题(3)——TS Header简介
  • 解剖C++模板(2) —— 模板匹配规则及特化
  • 面向对象试题答案
  • 【Python爬虫实战】轻量级爬虫利器:DrissionPage之SessionPage与WebPage模块详解
  • 斯坦福泡茶机器人DexCap源码解析:涵盖收集数据、处理数据、模型训练三大阶段
  • MATLAB基础应用精讲-【数模应用】Google Caffeine算法
  • Linux设置socks代理
  • Mapwindow5代码BUG记录1
  • AI与育儿领域的融合——探索未来的可能性
  • 计算机毕业设计Python+大模型斗鱼直播可视化 直播预测 直播爬虫 直播数据分析 直播大数据 大数据毕业设计 机器学习 深度学习
  • mpeg ps媒体流文件解析工具
  • 羲和数据集收集器1.4
  • PyQt入门指南五十五 持续集成与部署
  • Java-sec-code-SSRF攻击
  • Day 63 || 拓扑排序、dijkstra
  • 最新版【H5商城直接部署】
  • npm list -g --depth=0(用来列出全局安装的所有 npm 软件包而不显示它们的依赖项)
  • Javascript高级—DOM树的深度遍历和广度遍历