Skip to content

存储库

参考文档

  1. 规格模式 - cbf4life - 博客园
  2. 领域模型对象的生命周期-资源
  3. 阿里技术专家详解DDD系列 第三讲 - Repository模式
  4. 领域驱动设计(ddd)实践初探
  5. 领域模型 vs 数据模型,应该怎么用?

在六边形架构下存储库(Repository)和聚合(Aggregate)的关系:保证一个聚合根对应一个资源库

资源库的重要作用如下:

  1. 分离了聚合的"领域行为"和"持久化行为" 参考文档
  2. 扮演一个"领域模型"和"数据模型"的映射 参考文档

如果需要查询聚合的内部实体和值对象,可以通过调用聚合对应的资源库进行。

举例说明:要为订单(聚合根)添加订单项

// 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)
}