RepositoryDDDRepository

Repository

资源库通常标识一个存储的区域,提供读写功能。通常我们将实体存放在资源库中,之后通过该资源库来获取相同的实体,每一个实体都搭配一个资源库。

如果你修改了某个实体,也需要通过资源库去持久化。当然你也可以通过资源库去删除某一个实体。

cacheesdb
Repositoryclientclient

隐式写时复制

通常我们通过资源库读取一个实体后,再对这个实体进行修改。那么这个修改后的持久化是需要知道实体的哪些属性被修改,然后再对应的去持久化被修改的属性。

changesRepositoryRepository
Repository
//商品实体type Goods struct {    changes map[string]interface{}        //被修改的属性    Name    string//商品名称  Price   int// 价格  Stock   int// 库存}// SetPrice .func (obj *Goods) SetPrice(price int) {  obj.Price = price  obj.changes["price"] = price //写时复制}// SetStock .func (obj *Goods) SetStock(stock int) {  obj.Stock = stock  obj.changes["stock"] = stock //写时复制}//示例func main() {    goodsEntity := GoodsRepository.Get(1)     goodsEntity.SetPrice(1000)    GoodsRepositorySave(goodsEntity) //GoodsRepository 会内部处理商品实体的changes}
工厂和创建
IDIDIDKey
Eric Evans
IDDDDRepositoryselect count(*)
RepositoryRepositoryRepository

实践

https://github.com/8treenet/freedom/tree/master/example/fshop/adapter/repository

实体的缓存
keyIdentity
redisSetSource
// freedom.Entitytype Entity interface {  DomainEvent(string, interface{},...map[string]string)  Identity() string  GetWorker() Worker  SetProducer(string)  Marshal() []byte}// infra.EntityCachetype EntityCache interface {  //获取实体  GetEntity(freedom.Entity) error  //删除实体缓存  Delete(result freedom.Entity, async ...bool) error  //设置数据源  SetSource(func(freedom.Entity) error) EntityCache  //设置前缀  SetPrefix(string) EntityCache  //设置缓存时间,默认5分钟  SetExpiration(time.Duration) EntityCache  //设置异步反写缓存。默认关闭,缓存未命中读取数据源后的异步反写缓存  SetAsyncWrite(bool) EntityCache  //设置防击穿,默认开启  SetSingleFlight(bool) EntityCache  //关闭二级缓存. 关闭后只有一级缓存生效  CloseRedis() EntityCache}
以下实现了一个商品的资源库
package repositoryimport (  "time"  "github.com/8treenet/freedom/infra/store"  "github.com/8treenet/freedom/example/fshop/domain/po"  "github.com/8treenet/freedom/example/fshop/domain/entity"  "github.com/8treenet/freedom")func init() {  freedom.Prepare(func(initiator freedom.Initiator) {    initiator.BindRepository(func() *Goods {      return &Goods{}    })  })}// Goods .type Goods struct {  freedom.Repository //资源库必须继承,这样是为了约束 db、redis、http等的访问  Cache store.EntityCache //依赖注入实体缓存组件}// BeginRequestfunc (repo *Goods) BeginRequest(worker freedom.Worker) {  repo.Repository.BeginRequest(worker)  //设置缓存的持久化数据源,旁路缓存模型,如果缓存未有数据,将回调该函数。  repo.Cache.SetSource(func(result freedom.Entity) error {    return findGoods(repo, result)   })  //缓存30秒, 不设置默认5分钟  repo.Cache.SetExpiration(30 * time.Second)  //设置缓存前缀  repo.Cache.SetPrefix("freedom")}// Get 通过id 获取商品实体.func (repo *Goods) Get(id int) (goodsEntity *entity.Goods, e error) {  goodsEntity = &entity.Goods{}  goodsEntity.Id = id  //注入基础Entity 包含运行时和领域事件的producer  repo.InjectBaseEntity(goodsEntity)  //读取缓存, Identity() 会返回 id,缓存会使用它当key  return goodsEntity, repo.Cache.GetEntity(goodsEntity)}// Save 持久化实体.func (repo *Goods) Save(entity *entity.Goods) error {  _, e := saveGoods(repo, entity) //写库,saveGoods是脚手架生成的函数,会做写时复制的处理。  //清空缓存  repo.Cache.Delete(entity)  return e}func (repo *Goods) FindsByPage(page, pageSize int, tag string) (entitys []*entity.Goods, e error) {  build := repo.NewORMDescBuilder("id").NewPageBuilder(page, pageSize) //创建分页器  e = findGoodsList(repo, po.Goods{Tag: tag}, &entitys, build)  if e != nil {    return  }  //注入基础Entity 包含运行时和领域事件的producer  repo.InjectBaseEntitys(entitys)  return}func (repo *Goods) New(name, tag string, price, stock int) (entityGoods *entity.Goods, e error) {  goods := po.Goods{Name: name, Price: price, Stock: stock, Tag: tag, Created: time.Now(), Updated: time.Now()}  _, e = createGoods(repo, &goods)  //写库,createGoods是脚手架生成的函数。  if e != nil {    return  }  entityGoods = &entity.Goods{Goods: goods}  repo.InjectBaseEntity(entityGoods)  return}
领域服务使用仓库
package domainimport (  "github.com/8treenet/freedom/example/fshop/domain/dto"  "github.com/8treenet/freedom/example/fshop/adapter/repository"  "github.com/8treenet/freedom/example/fshop/domain/aggregate"  "github.com/8treenet/freedom/example/fshop/domain/entity"  "github.com/8treenet/freedom/infra/transaction"  "github.com/8treenet/freedom")func init() {  freedom.Prepare(func(initiator freedom.Initiator) {    initiator.BindService(func() *Goods {      return &Goods{}    })    initiator.InjectController(func(ctx freedom.Context) (service *Goods) {      initiator.GetService(ctx, &service)      return    })  })}// Goods 商品领域服务.type Goods struct {  Worker    freedom.Worker   //依赖注入请求运行时对象。  GoodsRepo repository.Goods //依赖注入商品仓库}// New 创建商品func (g *Goods) New(name string, price int) (e error) {    g.Worker.Logger().Info("创建商品")  _, e = g.GoodsRepo.New(name, entity.GoodsNoneTag, price, 100)  return}// Items 分页商品列表func (g *Goods) Items(page, pagesize int, tag string) (items []dto.GoodsItemRes, e error) {  entitys, e := g.GoodsRepo.FindsByPage(page, pagesize, tag)  if e != nil {    return  }  for i := 0; i < len(entitys); i++ {    items = append(items, dto.GoodsItemRes{      Id:    entitys[i].Id,      Name:  entitys[i].Name,      Price: entitys[i].Price,      Stock: entitys[i].Stock,      Tag:   entitys[i].Tag,    })  }  return}// AddStock 增加商品库存func (g *Goods) AddStock(goodsId, num int) (e error) {  entity, e := g.GoodsRepo.Get(goodsId)  if e != nil {    return  }  entity.AddStock(num) //增加库存    entity.DomainEvent("Goods.Stock", entity) //发布增加商品库存的领域事件  return g.GoodsRepo.Save(entity)}

目录

  • golang领域模型-开篇 八叉树,公众号:从菜鸟到大佬Golang领域模型-开篇
  • golang领域模型-六边形架构 八叉树,公众号:从菜鸟到大佬Golang领域模型-六边形架构
  • golang领域模型-实体 八叉树,公众号:从菜鸟到大佬Golang领域模型-实体
  • golang领域模型-资源库
  • golang领域模型-依赖倒置
  • golang领域模型-聚合根
  • golang领域模型-CQRS
  • golang领域模型-领域事件

项目代码 https://github.com/8treenet/freedom/tree/master/example/fshop