为什么大厂都在使用DDD?

大家好,我是大头,职高毕业,现在大厂资深开发,前上市公司架构师,管理过10人团队!
我将持续分享成体系的知识以及我自身的转码经验、面试经验、架构技术分享、AI技术分享等!
愿景是带领更多人完成破局、打破信息差!我自身知道走到现在是如何艰难,因此让以后的人少走弯路!
无论你是统本CS专业出身、专科出身、还是我和一样职高毕业等。都可以跟着我学习,一起成长!一起涨工资挣钱!

什么是领域驱动设计DDD

领域驱动设计(Domain-Driven Design,简称DDD)是由美国软件专家埃里克・埃文斯(Eric Evans)在2004年提出的软件设计方法论,旨在解决复杂软件系统开发过程中业务逻辑与技术实现之间的矛盾,提升软件系统的可维护性、可扩展性和灵活性。

说人话就是:

  • What: 它是一种设计思想、一种指导原则。
  • When: 设计微服务的时候,或者说,不知道怎么拆分微服务的时候。
  • Why:为什么要用它,上面其实说了,不知道怎么拆分微服务的时候,可以用它来指导你如何拆分微服务。
  • How:这个后面讲。

很多人都说,DDD是用来处理复杂业务逻辑的,那多复杂才算复杂业务呢?

这个问题,其实和微服务什么时候用是一个问题。

所有的技术都不是银弹。都有适合它的使用场景。

拿微服务来说,你一个小公司,就两三个开发,硬要上微服务,拆好几个服务出来,有什么意义吗?

是提升性能了?

是增加开发效率了?

都不是,你会发现拆分完以后,程序反而三高了。

  • 高复杂度:程序变得更加复杂了。
  • 高维护成本:程序的维护成本增加了、当有需求需要修改的时候、开发效率反而降低了。
  • 高运维成本:原来一台机器就满足了,你拆的服务多了,一台机器不够了。要么加机器性能要么加机器数量。

所以,适合很重要。

俗话说的好,见人说人话,见鬼说鬼话。技术也一样。

为什么大厂都开始使用DDD了?

回到我们的问题,为什么大厂都开始使用DDD了?

因为大厂人傻钱多

不是,有的人会说,因为大厂的业务足够复杂

说对了一半。

大厂通过DDD来指导微服务的拆分,解决了复杂的业务逻辑。

这里面有一些点,我们再细细的拆分一下。

为什么要拆分微服务?

再问大家一个问题,为什么要拆分微服务?

这个问题,千人千面。没有标准答案。

但是呢,总的有一些所谓的最佳实践

  1. 当公司成长了,流量增长了,单机很难支撑了。

比如,你是一个做电商的公司,你某天的成交量突然飙升。如何解决?最简单的做法,扩容机器。

如果你是单机系统,你扩容的机器其实相当于扩容了整个系统,但是,你只有某几个接口的流量很大而已,其他的接口白白浪费了机器的成本。

再比如。你家公司的流量不是突然飙升,而是每天都很大,但是呢,仅限于交易模块。和上面的问题是一样的。

  1. 当公司成长了,需求变多了。

很多人开发一个项目,代码写的很乱,大家每次合并代码都会出现一堆冲突

如果你拆分成微服务的话,天然的限制和约束就可以减少冲突,因为粒度变小了。

至于为什么拆分微服务,不再赘述了。

如何拆分微服务?

先给大家看两个例子吧。

拆分方案1

小李在一家电商公司A,A公司目前的代码架构如下:

ddd1-1

现在,A公司说,要开始拆分微服务了。小李负责拆分微服务。

小李一想,这个很简单啊,直接拆呗,一个模块一个服务就行了。交易量大只需要扩容交易服务,很完美啊。

所以,拆分完成以后,架构如下:

ddd1-2

一开始,小李觉得挺好,但是,逐渐发现问题了。

依赖严重,一个购买接口要跨域多个微服务。

既要从用户服务获取用户信息,又要从商品服务获取商品信息,还要从支付服务进行支付,还要从库存服务扣减库存,等等。。。

导致链路很长,接口的响应速度反而降低了。因为网络请求太多了。

写代码的时候又发现问题了,原来呢,只需要写逻辑就行了,现在还要写RPC接口。开发效率也降低了。

最后吧,代码写完了,又发现问题了,这事务怎么处理啊,只能上分布式事务了。开发成本又上去了。

结果就是,小李被领导一顿臭骂。

小李不语,只是默默承受着。。。

拆分方案2

小李觉得诸事不顺,跳槽去了另外一家电商公司B。

B公司也要拆分微服务了,领导见小李有过拆分微服务的经验,就将这个重要的任务交给了小李。

小李:。。。

小李无奈、只好继续重操旧业。

有了上次的失败经验,小李也学聪明了。

小李接下来复盘了上次的问题:

  1. 微服务粒度不对
  2. 接口链路太长导致速度下降
  3. 分布式事务等导致开发效率下降,且分布式事务也导致响应时间增加。

小李痛定思痛。要解决这几个问题。

终于,小李想到了好主意。

我不按照模块拆分不就完了!!!

我按照业务拆分。

比如,购买接口就放在交易服务里面。

那么他需要用户信息的时候自己取,不调用用户服务了,其他的逻辑也是。

这样确实解决了上面的一些问题,但是,购买还应该放在交易服务里面吗?

当需要增加一个接口的时候,我们如何判断它属于哪个服务呢?

小李又陷入了另外一个问题。

最后的结果,导致微服务里面代码很乱。

使用DDD拆分微服务

上面的两个案例,不知道大家遇到过没有呢?

还有很多其他的错误案例,大抵意思都差不多,就是不知道如何正确的拆分微服务。

DDD就是干这个活的。

DDD是指导我们如何正确拆分微服务的一种方法。
DDD的一些基本概念

DDD里面有很多的概念,很难一下子说清楚,因此,这里简单介绍一下。

  • 实体:使用充血模型实现的实体,既有属性、也有方法。
  • 值对象:只有属性的类。
  • 聚合根:一个特殊的实体,聚合的入口。
  • 聚合:聚合是一个概念、也可以理解成一个模块。聚合内包含了聚合根、实体、值对象。
  • 限界上下文:分割领域的边界、也是分割微服务的边界,通过这个边界明确这个接口属于哪个领域,也就是属于哪个微服务。每个领域有每个领域的上下文。
  • 领域:领域也就是我们的领域模型,也可以是一个微服务。
  • 子领域:一个领域可以分成多个子领域。这个就是粒度的问题了。
  • 领域事件:领域之间通信的方法。通过这个来调用其他的微服务。

还有一些核心领域、支撑领域、通用领域等,都是领域的一种,作用不同而已。

一个领域里面包含了多个子领域,如图所示。

ddd1-3

一个子领域里面包含了多个聚合,每个聚合里又有一个聚合根作为入口,还有若干个实体值对象

ddd1-4

通过DDD我们可以来拆分微服务。

DDD是围绕业务概念来进行领域建模的,后续的接口也是根据业务概念来划分到对应的领域中。从而解决开发过程中,业务演进的问题。

因为当业务改变了,那么领域就改变了,对应的代码也就跟着变就好了。

所以DDD和微服务不一样,不是一种具体的架构,只是一种指导方法。

它可以划分出清晰的业务边界也就是微服务的边界,从而让微服务的拆分更加符合业务。而不是乱拆分。

他也有一些步骤

  • 战略设计:战略设计从业务的视角上看待问题,建立领域边界、划分领域、子领域等等。
  • 战术设计:战术设计从技术的视角上看待问题,将领域转化成微服务,将业务实体转化成代码实体等等。
  • 事件风暴:通过事件风暴,大家一起来想业务,并将业务转化成领域、子领域、领域事件、实体等等。
电子商务DDD示例

首先,需要进行事件风暴,梳理出一些和业务逻辑有关的词汇

以电商举例,用户购买者订单商品库存收货地址商家价格购买行为下单支付取消订单退款发货等等。

接下来进行一些识别

识别什么呢?识别上面的概念,也就是哪些是实体值对象领域事件

说的再简单一些,名词就是实体或者值对象。如果只有属性值的就是值对象。动词就是领域事件。

比如上面的这些里面,哪些是领域事件呢?

  • 下单
  • 支付
  • 取消订单
  • 购买
  • 退款
  • 发货

哪些是实体呢?

  • 用户
  • 购买者
  • 商家
  • 商品

哪些是值对象呢?

  • 价格:价格仅仅是一个属性,价格变化的时候就是整个值对象进行变化。
  • 收货地址:收货地址也仅仅是一个属性或者一些属性,没有方法。
  • 库存:库存也同样。

有些人看到这里就会有疑问了?用户和购买者不是一个东西吗?

这里就需要提到另外一个概念了。限界上下文

这个在上面简单介绍过。这里再说一下。

这个概念有两个意思

  • 边界:用来划分领域的边界,也就是微服务的边界。
  • 上下文:在划分的边界之内,有着上下文。同样的一个东西,在不同领域里面,也就是在不同的上下文环境中,意思是不一样的。

比如笨蛋这个词吧。

在小情侣你侬我侬之间说,哎呀,你个笨蛋。就是打情骂俏的意思。当然不是说你真的笨了。

那换一个环境呢,你在学习的时候,老师跟你说:你真是个笨蛋!。这里就是真的在说你笨了。

所以呢,不同的环境,不同的上下文里面,一个词语的意思是不一样的。

这里也是,都是用户,但是在浏览商品的时候他只是用户。但是在购买的时候,他就是购买者了。

这样,他就在两个领域里面存在了。在用户领域里面是用户实体,在交易领域里面是购买者实体。

接下来可以进行聚合操作,也就是将意思相近、内容相近的放到一起,放到一个聚合里面。再根据聚合的内容划分出领域、子领域等。

比如

  • 用户聚合:里面就包含了用户实体。
  • 商品聚合:里面包含了商品实体、价格值对象、库存值对象。
  • 订单聚合:里面包含了订单实体、购买者实体、收货地址值对象。

。。。

每个聚合里面还要有一个聚合根作为聚合的入口。

接下来将聚合划分到领域中就可以了。

比如

  • 用户领域:包含了用户聚合
  • 交易领域:包含了商品聚合和订单聚合。也可以划分成两个子领域,一个子领域包含一个聚合。

。。。

还有一些领域事件。作为通信。

比如

  • 下单领域事件:用户聚合发起事件 -》 订单聚合接受事件。完成下单操作。

接下来我们需要把上面梳理出来的内容映射到代码上。

比如,我们将上面的内容放到代码里面。这里面有两种方式,聚合是DDD中的最小单元,所以可以把一个聚合部署为一个微服务。

当然了,也可以一个领域作为一个微服务,具体的情况,根据自身业务和流量这些具体考虑就可以了。

我们这里以领域做为微服务来示例:

  • 用户微服务
  • 交易微服务

先看用户微服务的文件夹吧,因为分层放在了下面介绍,所以这里的代码我们仅展示领域层的代码。

  • userAgg: userAgg文件夹,代表了用户聚合。
    • core: 聚合的核心代码,包含了聚合根、实体、值对象。一个文件夹。
      • userAggRoot.java: 用户聚合根实体,是一个java文件,一个class类。
    • event: 聚合的一些领域事件代码。
      • buyEvent.java: 下单的领域事件。

这个实体采用的是充血模型

简单看一下代码:

public class userAggRoot {
    private Integer userId; //userId是这个实体中的一个值对象。
    private String name; //name也是一个值对象。

    public UserAggRoot getUserInfo(Integer userId) {
        // 获取用户实体的信息
    }

    public void updateUserName(String name) {
        // 更新用户名称
    }

    //getter和setter....
}

领域事件的代码,其实简单来说,因为跨越微服务了,所以可以直接通过消息队列来进行事件通信。

public class buyEvent {

    public void buy() {
        // 组装事件信息并且发送事件
    }
}

再看一下交易微服务:

  • shopAgg: 代表了商品聚合,是一个文件夹
    • core:
      • shopAggRoot.java: 商品聚合根实体。
      • priceVO.java: 价格值对象的类,一个class。VO代表的是Value Object的意思。
      • inventoryVO.java: 库存值对象的类,一个class。
  • orderAgg: 代表了订单聚合,是一个文件夹
    • core:
      • orderAggRoot.java: 订单聚合根实体。
      • buyerEntity.java: 购买者实体,一个java的class。
      • addressVO.java: 收货地址值对象。
    • event:
      • orderEvent: 订单事件,发送订单事件可以给物流微服务,进行物流发货。还有积分服务进行积分处理等等。。。

代码类似:

public class shopAggRoot {
    private Integer shopId; // 商品id值对象
    private String shopName; //商品名称值对象
    private PriceVO price; //价格值对象
    private InventoryVO inventory; //库存值对象

    public void subInventory() {
        // 扣减库存
        // 1. 创建新的库存值对象

        // 2. 替换inventory属性
        // 。。。。
    }
}

看一些值对象:

public class priceVO{
    private BigDecimal originalPrice; //原价格
    private BigDecimal currentPrice; //当前价格,可能是优惠后的价格等等
}

public class inventoryVO{
    private Long inventory; //库存
}
DDD的分层架构

DDD的分层和传统的MVC分层不太一样。

传统的后端服务一般是这样的层次

  • controller:入口层
  • service:业务逻辑层
  • dao:数据层

DDD则是分成了下面四层

  • 用户接口层:也就是传统的入口层
  • 应用层:应用层调用领域层的聚合来完成操作,进行服务编排。可以调用多个聚合来共同完成操作。但是应用层和传统的业务逻辑层不同,它不负责完成业务逻辑,仅仅是服务编排,具体的业务逻辑由领域层实现。
  • 领域层:领域层是核心,包含了聚合、实体、值对象和聚合根。每个实体都是充血模型,包含了逻辑操作。
  • 基础层:基础层提供了基础服务,比如缓存、队列、数据库等等。

总结

我们简单的走了一遍DDD进行领域拆分。也让大家明白了为什么大厂会采用DDD。

因为大厂的微服务架构很完善了,他们的微服务复杂,使用DDD来指导微服务的拆分是一种有效的方法。

可以让代码和业务契合,当业务改变的时候代码随之改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值