Golang 领域模型 - 资源库
前言: 作为领域模型中最重要的环节之一的Repository,其通过对外暴露接口屏蔽了内部的复杂性,又有其隐式写时复制的巧妙代码设计,完美的将DDD中的Repository的概念与代码相结合!
Repository
资源库通常标识一个存储的区域,提供读写功能。通常我们将实体存放在资源库中,之后通过该资源库来获取相同的实体,每一个实体都搭配一个资源库。
如果你修改了某个实体,也需要通过资源库去持久化。当然你也可以通过资源库去删除某一个实体。
资源库对外部是屏蔽了存储细节的,资源库内部去处理 cache、es、db。
操作流程
Repository解除了client的巨大负担,使client只需与一个简单的、易于理解的接口进行对话,并根据模型向这个接口提出它的请求。要实现所有这些功能需要大量复杂的技术基础设施,但接口却很简单,而且在概念层次上与领域模型紧密联系在一起。
隐式写时复制
通常我们通过资源库读取一个实体后,再对这个实体进行修改。那么这个修改后的持久化是需要知道实体的哪些属性被修改,然后再对应的去持久化被修改的属性。
注意商品实体的changes,商品被修改某个属性,对应的Repository就持久化相应的修改。 这么写有什么好处呢?如果不这么做,那只能在service里调用orm指定更新列,但是这样做的话,Repository的价值就完全被舍弃了!
可以说写时复制是Repository和领域模型的桥梁!
工厂和创建
创建商品实体需要唯一ID和已知的属性名称等,可以使用实体工厂去生成唯一ID和创建,在交给资源库去持久化,这也是<<实现领域驱动设计>>的作者推荐的方式,但这种方式更适合文档型数据库,唯一ID是Key和实体序列化是值。
“底层技术可能会限制我们的建模选择。例如,关系数据库可能对复合对象结构的深度有实际的限制"(领域驱动设计:软件核心复杂性应对之道 Eric Evans)
但我们更多的使用的是关系型数据库,这样资源库就需要创建的行为。实体的唯一ID就是聚簇主键。一个实体或许是多张表组成,毕竟我们还要考虑垂直分表。我认为DDD的范式和关系型数据库范式,后者更重要。有时候我们还要为Repository 实现一些统计select count(*)的功能。
根据所使用的持久化技术和基础设施不同,Repository的实现也将有很大的变化。理想的实现是向客户隐藏所有内部工作细节(尽管不向客户的开发人员隐藏这些细节),这样不管数据是存储在对象数据库中,还是存储在关系数据库中,或是简单地保持在内存中,客户代码都相同。Repository将会委托相应的基础设施服务来完成工作。将存储、检索和查询机制封装起来是Repository实现的最基本的特性。
实践
https://github.com/8treenet/freedom/tree/master/example/fshop/adapter/repository
实体的缓存
这个是缓存组件的接口,可以读写实体,实体的key 使用必须实现的Identity 方法。 - 一级缓存是基于请求的,首先会从一级缓存查找实体,生命周期是一个请求的开始和结束。 - 二级缓存是基于redis。 - 组件已经做了幂等的防击穿处理。 - SetSource设置持久化的回调函数,当一、二级缓存未命中,会读取回调函数,并反写一、二级缓存。
以下实现了一个商品的资源库
领域服务使用仓库
目录
golang领域模型-开篇
golang领域模型-六边形架构
golang领域模型-实体
golang领域模型-资源库
golang领域模型-依赖倒置
golang领域模型-聚合根
golang领域模型-CQRS
golang领域模型-领域事件
项目代码 https://github.com/8treenet/freedom/tree/master/example/fshop
PS:加我微信:stargaze111,拉你加入DDD交流群,一起切磋DDD与代码的艺术!
版权声明: 本文为 InfoQ 作者【奔奔奔跑】的原创文章。
原文链接:【http://xie.infoq.cn/article/38ef3c3abef16bd5e1b24536c】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论 (3 条评论)