项目目录结构
什么是DDD?
DDD 是 Domain-Driven Design 的缩写。 其主要的思想是,我们在设计软件时,先从业务出发,理解真实的业务含义,将业务中的一些概念吸收到软件建模中来,避免造出“大而无用”软件。也避免软件设计没有内在联系,否则一团散沙,无法继续演进。
为什么要使用DDD?
过去,最常用的架构是三层架构,最典型的是MVC。三层架构的问题就是控制层依赖业务层,业务层又依赖数据层。特别是在数据层为贫血模型下,会让业务层的代码高速膨胀。在维护过程中,改了一点需求就会引发意想不到的bug,修复bug又会引起其他的bug。
贫血模型和充血模型
贫血模型
所谓贫血模型,是指Model 中,仅包含状态(属性),不包含行为(方法),采用这种设计时,需要分离出DB层,专门用于数据库操作。
充血模型
Model 中既包括状态,又包括行为,是最符合面向对象的设计方式。
业务层设计
我们在写业务代码的时候,最终要的往往在业务层,控制层就是最一些参数校验处理,数据层就是持久化数据。业务逻辑集中在业务层,在三层就够中业务层往往依赖数据层,吧orm的model对象拿到业务层使用,这是出bug概率最大的问题。那我们能不能做出设计,在业务层,既不依赖控制层也不依赖数据层呢?
我们使用反向依赖的方式,让数据层依赖业务层。
比如我们要查询一些用户的订单信息
1type User struct {
2 Id int
3 Name string
4 Password string
5 Age uint8
6}
7
8type Order struct {
9 Id int
10 Good string
11 PayTime time.Time
12}
13
14type UserRepo interface {
15 GetUsers(ctx context.Context,name string) ([]*User,error)
16}
17
18type OrderRepo interface {
19 GetOrders(ctx context.Context,userId int) ([]*Order,error)
20}
21
22type UserUseCase struct {
23 ur UserRepo
24 or OrderRepo
25}
26
27func NewUserUseCase(ur UserRepo,or OrderRepo) *UserUseCase {
28 return &UserUseCase{
29 ur:ur,
30 or:or,
31 }
32}
33
34func (uuc *UserUseCase) GetOrders(ctx context.Context,name string) ([]*Order,error) {
35 users,err := uuc.ur.GetUsers(ctx,name)
36 if err != nil || len(users) == 0 {
37 return fmt.Errorf("xxx%w",err)
38 }
39 userId := users[0].Id
40 return uuc.or.GetOrders(ctx,userId)
41}
在上面的例子中,在GetOrders
方法中调用接口Repos中的方法,把业务代码写完之后,进行单元测试我们只要mock实现各个repo的接口就好。然后再持久层(数据层)实现这些repo方法,在方法中把PO转成DO。在控制层使用NewUserUseCase创建UseCase。进行调用吧DO转化成DTO返回。
当然在业务复杂时,usecase的方法要简单,吧逻辑都封装到domain service中。
DTO DO和PO
DTO: Data Transfer Object的缩写。用于表示一个数据传输对象。DTO 通常用于不同服务或服务不同分层之间的数据传输。
BO: Business Object 的缩写,用于表示一个业务对象。
PO: Persistant Object缩写。用于数据库中一条记录映射成struct。