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

网站开发的内容百度云网盘资源分享网站

网站开发的内容,百度云网盘资源分享网站,站长工具seo综合查询下载安装,青岛做网站多少钱提示: 所有体系课见专栏:Go 项目开发极速入门实战课;欢迎加入 云原生 AI 实战 星球,12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);本节课最终…

提示:

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

上一节课我们实现了 Store 层依赖的数据结构。本节课,我们就可以来实现 Store 层的代码。

Store 层代码用来跟数据库或者其他第三方微服务进行交互,实现 Store 层的思路如下:

  1. 定义一个 Store 层接口,用来定义 Store 层需要实现的方法。在 Go 开发中接口通常会被命名为 XXXerXXXInterfaceIXXX,其中 XXX 是接口的功能描述符。这里,将 Store 层的接口命名为 IStore。Biz 层的接口同样使用了该命名方式,命名为 IBiz,以保持整个项目接口命名方式的统一;
  2. 创建一个结构体,该结构体用来实现 Store 层的核心方法。通常会在该结构体中包含一个 *gorm.DB 对象,用于与数据库交互进行 CRUD 操作;
  3. 实现一个 NewStore 函数,用于创建 Store 层的实例;
  4. 为了方便直接通过 store 包引用 Store 层实例,我们可以设置一个包级别的 Store 实例。访问 Store 层的全局实例在项目开发中很少,也不建议使用。但在某些场景下还是挺好用的。这里,保留灵活的扩展能力;
  5. 为了避免实例被重复创建,通常需要使用 sync.Once 来确保实例只被初始化一次。

IStore 接口定义及实现

Store 层的代码实现位于 feature/s12 分支的 internal/apiserver/store/ 目录下。核心实现位于 internal/apiserver/store/store.go 文件中,代码如代码清单 16-1 所示:

代码清单 16-1:Store 层代码实现

package storeimport ("context""sync""github.com/onexstack/onexstack/pkg/store/where""gorm.io/gorm"
)var (once sync.Once// 全局变量,方便其它包直接调用已初始化好的 datastore 实例.S *datastore
)// IStore 定义了 Store 层需要实现的方法.
type IStore interface {// 返回 Store 层的 *gorm.DB 实例,在少数场景下会被用到.DB(ctx context.Context, wheres ...where.Where) *gorm.DBTX(ctx context.Context, fn func(ctx context.Context) error) errorUser() UserStorePost() PostStore
}// transactionKey 用于在 context.Context 中存储事务上下文的键.
type transactionKey struct{}// datastore 是 IStore 的具体实现.
type datastore struct {core *gorm.DB// 可以根据需要添加其他数据库实例// fake *gorm.DB
}// 确保 datastore 实现了 IStore 接口.
var _ IStore = (*datastore)(nil)// NewStore 创建一个 IStore 类型的实例.
func NewStore(db *gorm.DB) *datastore {// 确保 S 只被初始化一次once.Do(func() {S = &datastore{db}})return S
}// DB 根据传入的条件(wheres)对数据库实例进行筛选.
// 如果未传入任何条件,则返回上下文中的数据库实例(事务实例或核心数据库实例).
func (store *datastore) DB(ctx context.Context, wheres ...where.Where) *gorm.DB {db := store.core// 从上下文中提取事务实例if tx, ok := ctx.Value(transactionKey{}).(*gorm.DB); ok {db = tx}// 遍历所有传入的条件并逐一叠加到数据库查询对象上for _, whr := range wheres {db = whr.Where(db)}return db
}// TX 返回一个新的事务实例.
// nolint: fatcontext
func (store *datastore) TX(ctx context.Context, fn func(ctx context.Context) error) error {return store.core.WithContext(ctx).Transaction(func(tx *gorm.DB) error {ctx = context.WithValue(ctx, transactionKey{}, tx)return fn(ctx)},)
}// Users 返回一个实现了 UserStore 接口的实例.
func (store *datastore) User() UserStore {return newUserStore(store)
}// Posts 返回一个实现了 PostStore 接口的实例.
func (store *datastore) Post() PostStore {return newPostStore(store)
}

DB 方法会尝试从 context 中获取事务实例,如果没有则直接返回 *gorm.DB 类型的实例。TX 方法则将*gorm.DB 类型的实例注入到 context 中。通过 DBTX 在 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 接口定义如下:

// UserStore 定义了 user 模块在 store 层所实现的方法.
type UserStore interface {Create(ctx context.Context, obj *model.UserM) errorUpdate(ctx context.Context, obj *model.UserM) errorDelete(ctx context.Context, opts *where.Options) errorGet(ctx context.Context, opts *where.Options) (*model.UserM, error)List(ctx context.Context, opts *where.Options) (int64, []*model.UserM, error)UserExpansion
}// UserExpansion 定义了用户操作的附加方法.
type UserExpansion interface{}

UserStore 接口中的方法分为两大类:资源标准 CURD 方法和扩展方法。通过将方法分类,可以进一步提高接口中方法的可维护性和代码的可读性。将方法分为标准方法和扩展方法的开发技巧,在 Kuberentes client-go 项目中被大量使用,fastgo 的设计思路正是来源于 client-go 的设计。

创建用户:Create 方法实现

userStore 结构体实现了 UserStore 接口。userStore 结构体的 Create 方法实现如下:

// userStore 是 UserStore 接口的实现.
type userStore struct {store *datastore
}// 确保 userStore 实现了 UserStore 接口.
var _ UserStore = (*userStore)(nil)// newUserStore 创建 userStore 的实例.
func newUserStore(store *datastore) *userStore {return &userStore{store}
}// Create 插入一条用户记录.
func (s *userStore) Create(ctx context.Context, obj *model.UserM) error {if err := s.store.DB(ctx).Create(&obj).Error; err != nil {slog.Error("Failed to insert user into database", "err", err, "user", obj)return errorsx.ErrDBWrite.WithMessage(err.Error())}return nil
}

Create 方法中,会调用 s.store.DB(ctx) 方法尝试从 context 中获取事务实例,如果没有则直接返回*gorm.DB 类型的实例,并调用 *gorm.DB 提供的 Create 方法,完成数据库表记录的插入操作。

Create 方法中,如果插入失败,根据本课程第 13 节课的 fastgo 错误返回规范,直接返回了 errorsx.ErrorX 类型的错误 errno.ErrDBWrite,并设置了自定义错误消息:err.Error()

在 Go 项目开发中,直接返回 gorm 包的错误,可能会暴露一些敏感信息,但绝大部分项目中,可以直接返回 gorm 包的报错信息。将 gorm 包的报错信息,直接在接口中返回,可以大幅提高接口失败时的排障效率。

删除用户:Delete 方法实现

userStore 结构体的 Delete 接口代码实现如下:

// Delete 根据条件删除用户记录.
func (s *userStore) Delete(ctx context.Context, opts *where.Options) error {err := s.store.DB(ctx, opts).Delete(new(model.UserM)).Errorif err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {slog.Error("Failed to delete user from database", "err", err, "conditions", opts)return errorsx.ErrDBWrite.WithMessage(err.Error())}return nil
}

Delete 方法中,会判断删除记录时的错误类型是否是 gorm.ErrRecordNotFound,如果不是会返回自定义错误,如果是则返回删除成功,以此实现幂等删除。在 Go 项目开发中,删除行为在绝大部分项目中是一个幂等的操作。

查询用户列表:List 方法实现

userStore 结构体的 List 方法实现如下:

// List 返回用户列表和总数.
// nolint: nonamedreturns
func (s *userStore) List(ctx context.Context, opts *where.Options) (count int64, ret []*model.UserM, err error) {err = s.store.DB(ctx, opts).Order("id desc").Find(&ret).Offset(-1).Limit(-1).Count(&count).Errorif err != nil {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 层封装很多查询类方法,例如:ListUserListUserByNameListUserByID 等。这都会使 Store 层代码变得臃肿,难以维护。其实 Store 层只需要对数据库记录进行简单的增删改查即可。对插入数据或查询数据的处理可以放在业务逻辑层。对查询条件的定制,可以通过提供灵活的查询参数来实现。

where 查询条件实现

为了实现在查询数据库记录时,配置灵活的查询参数,fastgo 项目引用了 onexstack 项目的 where 包,包名为 github.com/onexstack/onexstack/pkg/store/where。整个 OneX 技术体系,都通过 where 包来定制化查询条件。

where 包更多的设计方法、思考及使用方法可以参考 云原生 AI 实战营 中 Go 项目开发中级实战课 的第 25 | 讲 业务实现(2):实现 Store 层代码。

http://www.dtcms.com/wzjs/140347.html

相关文章:

  • 网站的意义综合查询
  • 广州营销型网站建设公司哪家靠谱用手机制作自己的网站
  • 门户网站排版上海百度推广官网
  • 一个学校怎么制作网站百度指数分析
  • 建站工具免费百度推广投诉电话客服24小时
  • 网站 主营业务aso优化公司
  • 建设网站要注册公司吗优化大师windows
  • 网站建设服务开发怎么创建网站免费建立个人网站
  • 站长工具查询ip地址网站推广软件免费版下载
  • 淘客导购网站怎么做电商运营基础知识
  • 红河网站制作seo站点
  • 万网人网站备案流程如何推广微信公众号
  • 网站建设包括哪些内容晋中网站seo
  • 百度云架设网站百度推广的效果
  • 做导师一般去什么网站找素材广州seo公司排名
  • 温州做网站建设seo快速排名网站优化
  • siren模板wordpressappstore关键词优化
  • 网站构建的友情链接怎么做百度在西安的公司叫什么
  • wamp配置多个网站提升seo搜索排名
  • 一个做网站编程的条件关键词排名优化易下拉软件
  • 网站开发背景和目的武汉seo人才
  • 中职网站建设推广软文怎么写样板
  • 网站大数据怎么做seo优化托管
  • wordpress 更换中文字体互联网关键词优化
  • 小公司怎么做免费网站产品推广ppt范例
  • 域名的网站建设方案书怎么写游戏推广代理
  • 大连市城市建设管理局网站如何模板建站
  • 网站开发项目立项报告范文深圳市住房和建设局
  • 黑龙江恒泰建设集团网站培训机构网站模板
  • 软件测试自学网站宁波网站推广