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

15 | 定义简洁架构 Store 层的数据类型

提示:

  • 所有体系课见专栏:Go 项目开发极速入门实战课;
  • 欢迎加入 云原生 AI 实战 星球,12+ 高质量体系课、20+ 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);
  • 本节课最终源码位于 fastgo 项目的 feature/s11 分支;
  • 更详细的课程版本见:Go 项目开发中级实战课:24 | 业务实现(1):实现 Store 层数据结构定义

在完成基础功能开发后,需要进一步开发与业务逻辑相关的代码。相较于基础功能,业务逻辑代码不仅占据了代码仓库的大部分代码量,其复杂性也更高。因此,需要设计一种合理的代码架构,以确保代码的可读性、可维护性和可扩展性。目前,业界较为流行且被广泛认可的代码架构是简洁架构。

本课程第 14 节课详细介绍了 fastgo 项目的简洁架构设计。本节课,将根据第 14 节课的简洁架构设计,实现 fastgo 的业务逻辑。

三层架构开发

在第 14 节课中介绍了 fastgo 三层简洁架构的依赖关系:Handler 层依赖 Biz 层,Biz 层依赖 Store 层,Store 层依赖数据库。依赖关系如下图所示。

为了能够随时测试所开发的代码功能,最优的方式是优先开发依赖较少的组件。否则,需要先 Mock 或开发所依赖的功能(层)。因此,开发顺序应为:先开发 Store 层,接着是 Biz 层,最后是 Handler 层。

本节课及接下来几节课代码改动量较大,其中有很多同类改动。为了提高你的学习效率,本节课不会对代码进行逐行解读,相反主要讲解其中的核心设计和实现。

Store 层数据结构定义

根据依赖关系,需要先开发 Store 层代码。Store 层依赖一些数据类型。如果项目持久存储用的是 MySQL/MariaDB 数据库,这些数据类型其实就是 GORM Model。GORM Model 实际上是数据库表字段到 Go 结构体的映射。可以根据 fastgo 数据库中的表来创建对应的 Model。

fastgo 数据库中包含以下三张表,这些表的结构在项目设计阶段已完成设计和创建:

  • user 表:该表用于存储用户数据;
  • post 表:该表用于存储博客数据。

需要根据表名、表字段及表字段类型创建 Store 层的 Go 结构体,以映射数据库中的对应表。

GORM Model 结构体定义可以手动编写,也可以借助工具自动生成。建议使用工具自动生成,具体步骤如下:

  1. 创建数据库和数据库表;
  2. 根据数据库表生成 Model 文件;
  3. 修改生成的 Go 代码。

根据数据库表生成 Model 文件

在 Go 项目开发中,编写 GORM Model 文件通常有多种方式,例如根据数据库表结构手动编写 GORM Model,或使用读取数据库表结构并自动生成 GORM Model 的工具,例如 GORM 官方提供 gentool。

$ go install gorm.io/gen/tools/gentool@latest
$ gentool -db mysql -dsn 'fastgo:fastgo1234@tcp(1127.0.0.1:3306)/fastgo' -onlyModel -modelPkgName internal/apiserver/model

上述命令会在 internal/apiserver/model/ 目录下生成 user.gen.go 和 post.gen.go GORM Model 文件。这 2 个文件中包含了 GORM Model 结构体定义。

提示:在第 02 节课中,我们安装了数据库,并创建了 fastgo 数据库及表。

添加 GORM 钩子

在生成了 GORM Model 结构体之后,可根据需要给这些结构体添加一些 GORM 钩子。常用的 GORM 钩子见下表所示:

返回头说明
BeforeCreate在执行 INSERT 语句前触发(比如对数据进行校验或补充字段)
AfterCreate在执行 INSERT 语句后触发(比如生成关联值或更新其他表数据)
BeforeFind在执行查询(SELECT)语句前触发(比如动态调整查询条件)
AfterFind在执行查询(SELECT)操作后触发(比如格式化数据或处理查询返回值)
BeforeUpdate在执行 UPDATE 语句前触发(比如校验更新字段的合法性)
AfterUpdate在执行 UPDATE 语句后触发(比如记录操作日志或更新缓存)
BeforeDelete在执行 DELETE 语句前触发(比如检查业务逻辑或软删除处理)
AfterDelete在执行 DELETE 语句后触发(比如记录日志或同步其他系统的数据)
BeforeSave在执行任何保存(INSERT 或 UPDATE)操作之前触发(适用于既需要创建也需要更新的公共逻辑)
AfterSave在执行任何保存(INSERT 或 UPDATE)操作之后触发(适用于既需要创建也需要更新的公共逻辑)

fastgo 项目在 internal/apiserver/model/hook.go 文件中,添加了数据库表 userIDpostID 字段的自动生成钩子,用来生成并保存记录的唯一标识符,代码如下所示:

package modelimport ("gorm.io/gorm""github.com/onexstack/fastgo/internal/pkg/rid"
)// AfterCreate 在创建数据库记录之后生成 postID.
func (m *PostM) AfterCreate(tx *gorm.DB) error {m.PostID = rid.PostID.New(uint64(m.ID))return tx.Save(m).Error
}// AfterCreate 在创建数据库记录之后生成 userID.
func (m *UserM) AfterCreate(tx *gorm.DB) error {m.UserID = rid.UserID.New(uint64(m.ID))return tx.Save(m).Error
}

上述代码在创建完数据库表记录之后,会调用 rid 包,基于数据库生成的自增 ID 生成一个形如 user-uvalgf 的英文唯一 ID,并调用 tx.Save() 方法将 ID 更新到表记录中。

Go 项目开发中,需要为每一个条 REST 资源生成唯一标识符(Unique Identifier,UID),以唯一定位该 REST 资源。例如更新资源、删除资源时,需要提供该唯一 ID。

fastgo 项目的 github.com/onexstack/fastgo/internal/pkg/rid 包用来生成唯一 ID。rid(resource id)包核心实现如下:

package ridimport ("github.com/onexstack/onexstack/pkg/id"
)const defaultABC = "abcdefghijklmnopqrstuvwxyz1234567890"type ResourceID stringconst (// UserID 定义用户资源标识符.UserID ResourceID = "user"// PostID 定义博文资源标识符.PostID ResourceID = "post"
)// String 将资源标识符转换为字符串.
func (rid ResourceID) String() string {return string(rid)
}// New 创建带前缀的唯一标识符.
func (rid ResourceID) New(counter uint64) string {// 使用自定义选项生成唯一标识符uniqueStr := id.NewCode(counter,id.WithCodeChars([]rune(defaultABC)),id.WithCodeL(6),id.WithCodeSalt(Salt()),)return rid.String() + "-" + uniqueStr
}

上述代码,定义了一个 ResourceID 数据类型,其 String 方法和 New 方法,分别用来返回资源的字符串标识和资源的唯一 ID,格式为 <资源标识符>-<6 位随机数>。使用带资源前缀的唯一 ID,有利于通过唯一 ID 辨别资源类型,通过前缀避免可能的随机数冲突。

ResourceID 是一个简单的、可扩展的统一表示形式,未来可根据需要添加更多的自定义资源,并复用 ResourceID 的方法,生成新资源的唯一 ID,例如 comment-w6irkgNew(counter uint64) 方法中的 counter 通常为数据库自增 ID,基于数据库自增 ID 生成唯一标识,不仅可以生成短小、易读的唯一 ID,还可以隐藏掉自增 ID 的背后的数据规模。


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

相关文章:

  • CMD批处理一些冷门命令,编写windows脚本常用?
  • 10 | 基于 Gin 实现 HTTP 服务器
  • vue 仿deepseek前端开发一个对话界面
  • 如何搭建一个适配微信小程序,h5,app的uni-app项目
  • Go Ebiten小游戏开发:俄罗斯方块
  • halcon机器人视觉(四)calibrate_hand_eye_stationary_3d_sensor
  • JAVA 基础语法备忘录 -
  • 01 | Go 项目开发极速入门课介绍
  • 如何搭建一个适配微信小程序,h5,app的工程
  • VSCode集成C语言开发环境
  • 要登录的设备ip未知时的处理方法
  • 17 | 实现简洁架构的 Biz 层
  • 【大模型】WPS 接入 DeepSeek-R1详解,打造全能AI办公助手
  • 编程助手学Python--Deepseek对OpenAI的Python库调用GPT-4模型生成对话回复理解
  • Future<V>接口 和 CompletableFuture<T>类 介绍
  • BLDC直流无刷电机转速电流双闭环调速MATLAB仿真
  • 21 | 全面测试项目功能
  • 12 | 给应用添加优雅关停功能
  • 完整项目案例:基于Django的毕业设计选题管理系统(包含源码结构、核心代码及设计文档框架)
  • 【Linux】在VMWare中安装Ubuntu操作系统(2025最新_Ubuntu 24.04.2)#VMware安装Ubuntu实战分享#