标题: 16 | 实现简洁架构的 Store 层 [打印本页] 作者: 大号在练葵花宝典 时间: 2025-3-14 10:50 标题: 16 | 实现简洁架构的 Store 层 提示:
所有体系课见专栏:Go 项目开发极速入门实战课;
接待参加 云原生 AI 实战 星球,12+ 高质量体系课、20+ 高质量实战项目助你在 AI 期间创建技术竞争力(聚焦于 Go、云原生、AI Infra);
本节课最终源码位于 fastgo 项目的 feature/s12 分支;
更具体的课程版本见:Go 项目开发中级实战课:25 | 业务实现(2):实现 Store 层代码
上一节课我们实现了 Store 层依赖的数据结构。本节课,我们就可以来实现 Store 层的代码。
Store 层代码用来跟数据库或者其他第三方微服务进行交互,实现 Store 层的思路如下:
界说一个 Store 层接口,用来界说 Store 层需要实现的方法。在 Go 开发中接口通常会被命名为 XXXer、XXXInterface、IXXX,此中 XXX 是接口的功能描述符。这里,将 Store 层的接口命名为 IStore。Biz 层的接口同样使用了该命名方式,命名为 IBiz,以保持整个项目接口命名方式的统一;
创建一个结构体,该结构体用来实现 Store 层的核心方法。通常会在该结构体中包含一个 *gorm.DB 对象,用于与数据库交互进行 CRUD 操作;
实现一个 NewStore 函数,用于创建 Store 层的实例;
为了方便直接通过 store 包引用 Store 层实例,我们可以设置一个包级别的 Store 实例。访问 Store 层的全局实例在项目开发中很少,也不发起使用。但在某些场景下还是挺好用的。这里,保留灵活的扩展能力;
DB 方法会实验从 context 中获取事务实例,假如没有则直接返回 *gorm.DB 范例的实例。TX 方法则将*gorm.DB 范例的实例注入到 context 中。通过 DB 和 TX 在 Biz 层实现事务,在 Store 层执行事务。
IStore 接口中的 User() 方法和 Post() 方法,分别返回 User 资源的 Store 层方法和 Post 资源的 Store 层方法。这种计划方式实在是软件开发中的抽象工厂模式。通过使用差异的方法,创建差异的对象,对象之间相互独立,以此进步代码的可维护性。另外使用这种方法,也能有用进步资源接口的尺度化。尺度化的资源接口更易理解和使用。
代码清单 16-1 中,使用以下代码创建 *datastore 范例的实例:
// NewStore 创建一个 IStore 类型的实例.
func NewStore(db *gorm.DB) *datastore {
// 确保 S 只被初始化一次
once.Do(func() {
S = &datastore{db}
})
return S
}
复制代码
上述代码使用了 sync.Once 来确保实例只被初始化一次。实例创建完成后,将其赋值给包级变量 S,以便通过 store.S.User().Create() 来调用 Store 层接口。在 Go 的最佳实践中,发起尽量减少使用包级变量,由于包级变量的状态通常难以感知,会增长维护的复杂度。然而,这并非绝对规则,可以根据实际需要选择是否设置包级变量来简化开发。
UserStore 接口界说及实现
为了进步代码的可维护性,将 User 资源的 Store 代码单独存放在 internal/apiserver/store/user.go 文件中。UserStore 接口界说如下:
slog.Error("Failed to list users from database", "err", err, "conditions", opts)
err = errorsx.ErrDBRead.WithMessage(err.Error())
}
return
}
复制代码
在 List 方法中,会调用 s.store.DB(ctx, opts) 方法,根据 opts 入参来配置 *gorm.DB 实例的查询条件。查询的记录会按数据库 id 字段降序排列(从大到小的顺序)。将最新的记录放在返回列表的最前面可以进步返回数据的阅读体验,也是大部门平台的默认排序规则。
整个 UserStore 接口中的方法实现较为简洁,仅直接对数据库记录进行增删改查操作,并未封装任何业务逻辑。Post 资源的 Store 层代码实现与 User 资源保持一致,故本课程不再解读 Post 资源的 Store 层实现。
在 Go 项目开发中,不少开发者会在 Store 层封装业务代码,为了实现差异的查询条件,会在 Store 层封装很多查询类方法,比方:ListUser、ListUserByName、ListUserByID 等。这都会使 Store 层代码变得痴肥,难以维护。实在 Store 层只需要对数据库记录进行简朴的增删改查即可。对插入数据或查询数据的处理可以放在业务逻辑层。对查询条件的定制,可以通过提供灵活的查询参数来实现。
where 查询条件实现
为了实现在查询数据库记录时,配置灵活的查询参数,fastgo 项目引用了 onexstack 项目的 where 包,包名为 github.com/onexstack/onexstack/pkg/store/where。整个 OneX 技术体系,都通过 where 包来定制化查询条件。
where 包更多的计划方法、思考及使用方法可以参考 云原生 AI 实战营 中 Go 项目开发中级实战课 的第 25 | 讲 业务实现(2):实现 Store 层代码。