Alistair Cockburn 提出了一种具有对称特征的架构风格。在这种架构中,不同的客户通过平等的方式与系统交互。比如HTTP客户,MQ客户,它们平等对系统提供输入。Redis和DB也平等的提供输出。每个客户都拥有自己的适配器,去理解输入,比如gin、iris、echo就是http的适配器。那么内部是业务系统(领域模型),外部就是输入和输出的适配器。重心放在内部业务逻辑上,隔离输入和输出。如果非要用分层来理解,那么六边形分为内层和外层。
Alistair Cockburn提出的六边形是有Application和Domain的,但现在微服务体系下Application已经没有存在的必要了,一个微服务就是一个Application。落地的六边形图如下。
那么六边形和DDD的结合是如何应对上述困惑的。所有数据处理全部由repository 适配成实体,逻辑都是领域服务、聚合、实体的行为。分多少层和平层、跨层调用本身也不存在。
对应六边形的内部,主要放领域服务service的代码。子目录分为aggregate聚合根目录、entity实体目录。
对应六边形的外部,主要是输入和输出的适配器。controller子目录负责 http的api输入,repository子目录负责实体的读写。dto子目录是controller或repository的外部输入输出对象。po子目录是数据库的持久化对象,这些对象是生成的。
package controller import ( domain "github.com/8treenet/freedom/example/fshop/domain" ) type Cart struct { Worker freedom.Worker CartSev *domain.Cart //购物车领域服务 } // GetItems 获取购物车商品列表, GET: /cart/items route. func (c *Cart) GetItems() freedom.Result { userId, err := c.Worker.IrisContext().URLParamInt("userId") if err != nil { return &infra.JSONResponse{Error: err} } //适配http的输入参数userId后调用领域模型目录的入口领域服务 dto, err := c.CartSev.Items(userId) if err != nil { return &infra.JSONResponse{Error: err} } return &infra.JSONResponse{Object: dto} }
package domain import ( //引用仓库 "github.com/8treenet/freedom/example/fshop/adapter/repository" "github.com/8treenet/freedom/example/fshop/domain/aggregate" ) // Cart 购物车领域服务. type Cart struct { CartRepo repository.CartRepo //购物车仓库,这里是依赖注入的 } // Items 购物车全部商品项 func (c *Cart) Items(userId int) (items dto.CartItemRes, e error) { // 使用 c.CartRepo读取购物车数据 return } // DeleteAll 清空购物车 func (c *Cart) DeleteAll(userId int) (e error) { return c.CartRepo.DeleteAll(userId) }
欢迎关注我们的微信公众号,每天学习Go知识