Domain-Driven Design在微服务中的使用

一般来说在我们在讨论如何设置微服务的boundaries的时候,会围绕domain来进行考虑。因此Domain-Driven Design(DDD)在这个过程中就可以提供很不错的帮助,本文就来聊聊我们在微服务中是如何使用DDD的。DDD其实包含了很多内容,我们这里主要关注两个重要的思想:聚合(Aggregate)以及界限上下文(Bounded context)。

聚合(Aggregate)

DDD中的聚合有时候也让人confuse,不是很明确究竟是什么意思。一个比较简单的理解就是我们可以认为它是一个真实domain概念的展现,就是说它通常有一个生命周期,我们可以通过一个状态机来实现它。

从聚合的定义不难看出,我们还是希望能把一个聚合状态的变化放到一个微服务中进行管理的,当然一个微服务可能会管理多个不同的聚合。外部的服务如果想要改变聚合的状态,一种方法就是直接调用这个微服务来进行改变,或者让管理这个聚合的微服务subscribe到某个event,然后想要改变的外部服务发送event。这里最核心的一点就是管理这个聚合的微服务有说不的权利,比如说外部服务想要把状态改变到一个不合法的状态,管理聚合的微服务可以拒绝。

不同的聚合之间可以有各种各样的关系,他们可以放到不同的微服务上,也可以放到同一个微服务中,如下图所示:

这里Order,Customer,Wishlist都是一个聚合,而Order和Customer是相关的,同时Customer又有自己的Wishlist。假如我们把它们放到一个微服务中,好处就是我们可以使用例如关系型数据库中的foreign key来进行存储。假如我们把它们放到不同的微服务中,我们就需要思考怎样来处理它们之间的关系了。我们可以考虑使用一个ID来表示一个aggregate,比如存储一个CustomerID列来对应相应的Customer aggregate,也可以通过这个ID来从Customer微服务中得到对应的详细信息。当然也有人建议使用customer/id这样的类REST形式来进行存储。这些方法都各有其利弊,我们以后可以详细进行讨论。

界限上下文(Bounded context)

界限上下文表示了一个更大的组织界限,我们从一个例子来了解它:

还是一个淘宝的例子,客户下单一个商品,需要有订单相关的内容,然后还涉及库存的check和修改,以及后期的发货,甚至还有退货相关的内容。当然还包括客户下单的时候的支付,以及支付时选择的发货方式等等。

这里我们把Warehouse,Finance都认为是一个界限上下文,它一个方面要把一些实现的细节隐藏了,比如说货物是放在货架的哪一排这样的信息除了warehouse相关的人员有兴趣之外,对外面的服务应当进行隐藏。当然它另一方面也还需要确定哪些聚合是需要暴露给外面服务的。

我们下面来具体看看:如下图所示,finance可能需要知道Warehouse的一些内容,比如某些商品有多少个,这样可以进行一些资产评估之类的事情;但也有一些Warehouse的内容是没有必要让Finance知道的,比如某个商品在哪个货架上等。

假如Finance这里只需要知道某个stock item还有多少的库存就可以了,那么如下图所示,我们只要把Warehouse中的关于total count相关的内容share出来即可,而诸如这个商品在哪个货架上这种信息则无需share出来。

聚合和界限上下文在微服务中的使用

聚合和界限上下文其实都是某种程度上的单元组合,我们可以使用它们来作为服务的边界。一般来说,我们会先尽量让一个微服务包含整个界限上下文,然后再看是否有必要分成更小的多个微服务,但是最低的限度就是我们还是要保证每个聚合是在一个微服务中的——一个微服务可以有多个聚合,但是我们不希望一个聚合被多个微服务管理。

开始的时候,我们可能会把界限思考得比较细,比如说warehouse可以包含仓储,货物收发,订单处理多个部分,那么是否需要把它们都分成一个个微服务呢?其实不然,我们开始的时候可以先把它们都放到一起,但是内部实现的时候可以分开,但是这个分开在外部是不感知的。如下图所示:

内部分开的Inventory和Shipping其实外部的Finance微服务其实是不知道的,这个从另外一个方面来说就是单个聚合的实现内部可以单独进行处理。

总结

本文简单介绍了DDD中的聚合和界限上下文的概念,我们可以使用这些概念来决定微服务的划分。

You may also like...

Leave a Reply

Your email address will not be published.