存储库
参考文档
- 规格模式 - cbf4life - 博客园
- 领域模型对象的生命周期-资源
- 阿里技术专家详解DDD系列 第三讲 - Repository模式
- 领域驱动设计(ddd)实践初探
- 领域模型 vs 数据模型,应该怎么用?
在六边形架构下存储库(Repository)和聚合(Aggregate)的关系:保证一个聚合根对应一个资源库
资源库的重要作用如下:
如果需要查询聚合的内部实体和值对象,可以通过调用聚合对应的资源库进行。
举例说明:要为订单(聚合根)添加订单项
// OrderItem不是聚合,不能为其定义资源库
OrderItemRepository orderItemRepo;
orderItemRepo.add(orderId, orderItem);
OrderRepository orderRepo;
Order order = orderRepo.orderOfId(orderId);
order.addItem(orderItem);
orderRepo.update(order);
// 在这个案例里面Repository的调用方仅能操作聚合根Order对象,而无法直接操作非聚合根的OrderItem实体
变更追踪(写时复制)
接口设计思路
细化接口
介绍
- 介绍:实现查询接口明确化,为每一个接口限定了具体的任务。在功能上对接口进行了限制,在命名上体现了接口的功能。调用者无需再对查询条件进行组合,按照接口定义的条件实施即可实现相应功能。
- 例子:例如:allInProgressOrdersOfCustomer(customerId)限定了这个方法的功能,也限制了具体的条件。
优点
- 接口的功能、命名清晰明确。
- 减轻了调用者的使用负担。
缺点
- 接口功能太具体,导致接口方法膨胀。
- 业务发生变更时,接口的变更会比较复杂,扩展性差。
通用接口
介绍
- 介绍:接口的定义不限制具体查询功能,查询方法没有具体针对的需求,调用者可以据此实现自己任意需求的功能,不会被限制。
- 例子:比如一个查询方法 它只定义Findallmatch(string sql)这个方法,后面相关的具体的sql语言,查询条件等,都由调用者自己去实现(使用查询条件表达式)。
优点
- 可扩展。
- 灵活地运用于各种业务规则。
- 能适应各种的业务变化。
缺点
- 可读性差。
- 增加调用者的负担。
- 封装性差。
规格模式
介绍
- 规格模式用于封装查询条件。相较于直接使用查询条件,规格模式的封装性更好,可以实现按照业务规则定义不同的规格子类,由规格子类实现查询命令。
- 规格模式例子:见代码
- 主要思想: 规格模式有四个接口方法,分别为And、Not、Or和Issatisfy四个方法,其中最后一个方法为判断条件,前三个为组合条件,根据不同的Issatisfy条件结合And、Not、Or三个方法,可以形成自定义的查询条件组合。由此实现对通用查询条件语句的封装。
优点
- 对通用接口的查询命令进行封装,封装性好。
- 能做到对领域规则的扩展
缺点
- 实现不同的组合需要定义不同的实例。
- 可读性差。
结论
- 在资源库查询方法并不足以造成接口膨胀的情况下,可以采用细化接口设计的同时,也定义通用的接口的方式,在满足可读性的同时兼顾可扩展性。
- 在具有大量的涉及各种细节的资源库查询,则建议使用通用接口,再使用规格模式对数据库查询命令进行封装。 阿里技术专家详解DDD系列 第三讲 - Repository模式接口定义规范中提及,应避免“通用”的Repository模式。
规格模式代码
package main
import (
"fmt"
)
// Iuser 是一个用户结构
type Iuser struct {
Name string
Age int
WeaponHeight int
}
// IuserProvide 用于选择最终的用户
type IuserProvide interface {
Finduser(iuserspecific Iuserspecific) []Iuser
}
// Iuserspecific 根据条件区别用户
type Iuserspecific interface {
IsSatisfiedBy(iuser Iuser) bool
// And(iuserspecific Iuserspecific) Iuserspecific
}
// Icommanstatic 提供And方法用以联合不同的条件
type Icommanstatic interface {
IsSatisfiedBy(iuser Iuser) bool
And(iuserspecific Iuserspecific) Icommanstatic
}
// Iuserspecificbyname 实现区别用户这个接口 姓名
type Iuserspecificbyname struct {
Name string
}
// IsSatisfiedBy 实现区别用户这个接口
func (i *Iuserspecificbyname) IsSatisfiedBy(iuser Iuser) bool {
return i.Name == iuser.Name
}
// Iuserspecificbyage 实现区别用户这个接口 年龄
type Iuserspecificbyage struct {
Age int
}
// IsSatisfiedBy 实现区别用户这个接口
func (i *Iuserspecificbyage) IsSatisfiedBy(iuser Iuser) bool {
return i.Age < iuser.Age
}
// Iuserspecificbywh 实现区别用户这个接口 wepon
type Iuserspecificbywh struct {
WeaponHeight int
}
// IsSatisfiedBy 实现Icommanstatic
func (i *Iandshixian) IsSatisfiedBy(iuser Iuser) bool {
var result = true
for _, v := range i.right {
result = v.IsSatisfiedBy(iuser) && result
}
return result && i.left.IsSatisfiedBy(iuser)
}
func main() {
var userlist = []Iuser{
{Name: "zhangfei", Age: 38, WeaponHeight: 15},
{Name: "zhangfei", Age: 17, WeaponHeight: 15},
{Name: "guanyu", Age: 39, WeaponHeight: 20},
{Name: "zhaoyun", Age: 20, WeaponHeight: 18},
{Name: "liubei", Age: 40, WeaponHeight: 10},
{Name: "machao", Age: 30, WeaponHeight: 21},
{Name: "huangzhong", Age: 45, WeaponHeight: 13},
}
var isuserp IuserProvide = &UserProvider{userlist: userlist}
// 姓名、年龄、武器三个筛选实例
var ilogicname = Iuserspecificbyname{Name: "zhangfei"}
var ilogicage = Iuserspecificbyage{Age: 20}
var ilogicwh = Iuserspecificbywh{WeaponHeight: 13}
// Iandshixian 实现了and接口的实例
var iandshixan = Iandshixian{left: &ilogicage}
userlist = isuserp.Finduser(iandshixan.And(&ilogicname).And(&ilogicwh))
fmt.Println(userlist)
}
// IsSatisfiedBy 实现区别用户这个接口
func (i *Iuserspecificbywh) IsSatisfiedBy(iuser Iuser) bool {
return i.WeaponHeight < iuser.WeaponHeight
}
// UserProvider 实现用户筛选接口
type UserProvider struct {
userlist []Iuser
}
// Finduser 实现用户筛选接口
func (u *UserProvider) Finduser(iuserspecific Iuserspecific) []Iuser {
var result []Iuser
for _, v := range u.userlist {
if iuserspecific.IsSatisfiedBy(v) {
result = append(result, v)
}
}
return result
}
// Iandshixian 实现Icommanstatic
type Iandshixian struct {
left Iuserspecific
right []Iuserspecific
}
// And 实现Icommanstatic
func (i Iandshixian) And(iuserspecific Iuserspecific) Icommanstatic {
i.right = append(i.right, iuserspecific)
return &i
}
// IsSatisfiedBy 实现Icommanstatic
func (i *Iandshixian) IsSatisfiedBy(iuser Iuser) bool {
var result = true
for _, v := range i.right {
result = v.IsSatisfiedBy(iuser) && result
}
return result && i.left.IsSatisfiedBy(iuser)
}
func main() {
var userlist = []Iuser{
{Name: "zhangfei", Age: 38, WeaponHeight: 15},
{Name: "zhangfei", Age: 17, WeaponHeight: 15},
{Name: "guanyu", Age: 39, WeaponHeight: 20},
{Name: "zhaoyun", Age: 20, WeaponHeight: 18},
{Name: "liubei", Age: 40, WeaponHeight: 10},
{Name: "machao", Age: 30, WeaponHeight: 21},
{Name: "huangzhong", Age: 45, WeaponHeight: 13},
}
var isuserp IuserProvide = &UserProvider{userlist: userlist}
// 姓名、年龄、武器三个筛选实例
var ilogicname = Iuserspecificbyname{Name: "zhangfei"}
var ilogicage = Iuserspecificbyage{Age: 20}
var ilogicwh = Iuserspecificbywh{WeaponHeight: 13}
// Iandshixian 实现了and接口的实例
var iandshixan = Iandshixian{left: &ilogicage}
userlist = isuserp.Finduser(iandshixan.And(&ilogicname).And(&ilogicwh))
fmt.Println(userlist)
}
// IsSatisfiedBy 实现Icommanstatic
func (i *Iandshixian) IsSatisfiedBy(iuser Iuser) bool {
var result = true
for _, v := range i.right {
result = v.IsSatisfiedBy(iuser) && result
}
return result && i.left.IsSatisfiedBy(iuser)
}
func main() {
var userlist = []Iuser{
{Name: "zhangfei", Age: 38, WeaponHeight: 15},
{Name: "zhangfei", Age: 17, WeaponHeight: 15},
{Name: "guanyu", Age: 39, WeaponHeight: 20},
{Name: "zhaoyun", Age: 20, WeaponHeight: 18},
{Name: "liubei", Age: 40, WeaponHeight: 10},
{Name: "machao", Age: 30, WeaponHeight: 21},
{Name: "huangzhong", Age: 45, WeaponHeight: 13},
}
var isuserp IuserProvide = &UserProvider{userlist: userlist}
// 姓名、年龄、武器三个筛选实例
var ilogicname = Iuserspecificbyname{Name: "zhangfei"}
var ilogicage = Iuserspecificbyage{Age: 20}
var ilogicwh = Iuserspecificbywh{WeaponHeight: 13}
// Iandshixian 实现了and接口的实例
var iandshixan = Iandshixian{left: &ilogicage}
userlist = isuserp.Finduser(iandshixan.And(&ilogicname).And(&ilogicwh))
fmt.Println(userlist)
}