DDD分层架构

DDD(Domain Driven Design,领域驱动设计)

DDD

ddd作为一种软件开方法,可以帮助我们设计高质量的软件模型,在正确实现的情况下,我们通过DDD完成的设计恰恰就是软件的工作方式

ddd是为了解决快速变化,复杂系统的设计问题的

传统的mvc模式,是面向 数据的设计

模型

贫血模型

目前几乎所有的后端系统都是基于贫血模型的.

​
// Controller+VO(View Object) //
public class UserController {
  private UserService userService; //通过构造函数或者IOC框架注入
  
  public UserVo getUserById(Long userId) {
    UserBo userBo = userService.getUserById(userId);
    UserVo userVo = [...convert userBo to userVo...];
    return userVo;
  }
}
 
public class UserVo {//省略其他属性、get/set/construct方法
  private Long id;
  private String name;
  private String cellphone;
}
 
// Service+BO(Business Object) //
public class UserService {
  private UserRepository userRepository; //通过构造函数或者IOC框架注入
  
  public UserBo getUserById(Long userId) {
    UserEntity userEntity = userRepository.getUserById(userId);
    UserBo userBo = [...convert userEntity to userBo...];
    return userBo;
  }
}
 
public class UserBo {//省略其他属性、get/set/construct方法
  private Long id;
  private String name;
  private String cellphone;
}
 
// Repository+Entity //
public class UserRepository {
  public UserEntity getUserById(Long userId) { //... }
}
 
public class UserEntity {//省略其他属性、get/set/construct方法
  private Long id;
  private String name;
  private String cellphone;
}

平时开发web后端项目的傻时候,基本上都是这么组织代码的,其中userEntity和USerRepository组成了数据访问层,UserBo和UserService组成了业务逻辑层,UserVo和UserController这里属于接口层

从代码中我们可以发现,UserBo是一个纯粹的数据结构,只包含数据,不包含任何的业务逻辑,业务逻辑集中在UserSErvice中,我们通过UserSErvice来操作UserBo.换句话说,Service层的数据业务逻辑,被分割为Bo和Service俩个类中,像UserBo这样,只包含数据,不包含逻辑的类,就叫做贫血模型,同理UserEntity,UserVo都是基于贫血模型设计的.这种贫血模型讲数据与操作分离,这种分离直观上就不是在同一个类里,就和一个车只有车的特点,破坏了面向对象封装的的特效,是一种典型的面向过程的编程风格.

充血模型

什么是基于充血模型的DDD开发模式

在贫血模型中,数据和业务逻辑被风格到不同的类中,充血模型正好相反,数据和对应的业务逻辑被封装到同一个类中.因此这种充血模型满足面向对象的封装性,是典型的面向对象风格编程.

什么是驱动领设计

驱动领域设计即DDD,主要是用来知道如何解耦业务逻辑,划分业务模块,定义业务领域模型及其交互.领域驱动设计这个概念在04年被提出,到现在已经有十几年的历史了.不过它被大众熟知,是因为另一个概念的兴起,那就是微服务

我们知道,除了监控、调用链追踪、API 网关等服务治理系统的开发之外,微服务还有另外一个更加重要的工作,那就是针对公司的业务,合理地做微服务拆分。而领域驱动设计恰好就是用来指导划分服务的。所以,微服务加速了领域驱动设计的盛行。

实际上基于充血模型的DDD开发模式实现的代码,也是按照mvc三层架构分层的.Controller层还是负责暴露接口,Repository还是负责数据存取,Service层负责核心业务逻辑.它跟基于贫血模型的传统开发模式的区别主要在Service层.

在贫血模型的传统开发模式中,Service层包含Service类和Bo类俩部分,Bo是贫血模型,只包含数据,不包含具体的业务逻辑.业务逻辑在Serivce类中.在基于充血模型的DDD开发模式中,Service层包含Service类和Domain类俩部分.Domain就相当于贫血模型中的BO,区别是基于充血模型开发的,既包含数据,也包含业务逻辑.总结一下的话就是,基于贫血模型的传统的开发模式,重 Service 轻 BO;基于充血模型的 DDD 开发模式,轻 Service 重 Domain。

为什么传统模式如此受欢迎

  1. 大部分情况下,开发系统业务都比较简单,不需要精心设计充血模型,
  2. 充血模型的设计比贫血模型更有难度
  3. 传统开发模式转型有成本,没有遇到开发痛点情况下大多不愿意转型

转载自(32条消息) 什么是贫血模型?Limonare的博客-CSDN博客贫血模型

mvc保证了最差也差不到哪去(一般都是最差),DDD如果做得差,会做的比MVC还要差,如果做的好,的确可以应对业务的变化

归约

什么是归约模式,用来将业务规则(通常是隐式业务关于则)封装成独立的逻辑单元,从而将隐式业务规则提炼为显示概念,并达到代码复用的目的

什么是隐式业务规则,假如开发了一个网站,目标用户是18岁以上人群,当地政策不允许18岁以下浏览,如何验证用户符合需求呢

public ActionResult Register(UserRegisterInfo user){
    if(user.Age < 18){
        throw new Exception("Too young too simple...");
    }
}

在Register方法中if语句就是一条隐式业务规则

当然这样写也能满足业务规则,但是改天新来一个程序员,没闹明白为啥要加if判断、或者没闹明白为啥是18,,修改了代码,业务完整性就被破坏了。

public ActionResult Register(UserRegisterInfo user){
    var specification = new UserMustBeAdultSpecification();
    if(!specification.IsSatisfiedBy(user)){
        throw new Exception("Too young too simple...");
    }
    
    //todo:注册逻辑
}
​
class UserMustBeAdultSpecification {
    private int adultAge;
    public UserMustBeAdultSpecification(int adultAge = 18){
        this.adultAge = adultAge;
    }
    
    public IsSatisfiedBy(UserRegisterInfo user){
        return user.Age > this.adultAge;
    }
}

我们把if判断提炼成一个显示概念,用来确认用户必须是成人。这样新人来了以后,也不至于揣着明白装糊涂。

为什么要归约

通常业务不会仅仅验证一下年龄那么简单,比如订单提交,需要验证用户账号是否可用,订单的库存是否满足预定量,配送地址是否完整...如果仅仅是通过一连串的if判断,那就真的太不利于维护了,并且if嵌套了多的代码难于理解,不好说明白意图.一次需要隐式转换成显式概念,这也是DDD的要求

比如网站不仅需要注册,还可能有更新用户信息的功能,更新的时候我们任然需要确认用户必须是成人,

Last modification:May 9, 2022
如果觉得我的文章对你有用,请随意赞赏