在单体应用的一些DDD实践经验 (2)

可是我们的平台要承担很多功能,比如开放RESTful的API与Webservice(为了兼容老的接口), 同时还要提供授权(使用了基于Oauth2.0协议的三种模式)、数据库初始化、处理请求上下文等等,我就不一一列出来了。

我们希望BC(BoundedContext,后文都会简写为BC)里不需要关注网络层面的东西而只聚焦于应用,所以很多通用的事情都由平台来承担, 而且有时还会有一些交互,比如在验证权限时你得跟用户权限上下文通信。

在这种前提下,我们抽出了一个用于连接平台和这些BC的交互层,我们把它称作——桥接组件(BrigeComponent),它负责联系起平台和这些BC,外加上一些共用的基础设施,我们的架构图变成了这样:

在单体应用的一些DDD实践经验

 

 

这样一来,你可以把每个BC都当作微服务来处理,每一个BC内的分层结构你可以按你的喜欢的来,如果你喜欢标准的三层架构(UI + BLL + DAL),你可以将BC设计那样。

你甚至可以每个BC都采用不同的风格,比如一个采用N层架构,而另一个采用事件驱动架构(EDA)。

这里我们的BC都用了相同的DDD推荐分层架构(这里省去了 表现层, 因为现代应用大多都是前后端分离了的),如下图所示:

 

 

在单体应用的一些DDD实践经验

 

好了,现在整体架构和领域模型都已经确定下来后,我们开始编码了,但很快我们就遇到了阻碍。

 

“结算上下文需要访问用户权限上下文,它需要知道这个用户的机构信息,我可以直接引用吗?”

“帐户上下文这里输出的数据需要通用上下文提供一些有效性校验,我可以直接引用吗?”

“我这里也需要访问通用上下文!”

……

 

好吧,如果我们直接提供引用,会有以下问题:

由于我们采用了程序集分割上下文,所以相互引用是不被允许的。

就算克服了相互引用的问题,最终也会导致引用拓扑图混乱不堪。

强耦合,这会直接影响到以后的拓展性。

在微服务中,为了克服服务间的互相通信问题,目前我了解的有两类解决方案,

一是类似于ESB(企业服务总线)的中心化通信模式,比如大名鼎鼎的SprinCloud。

二是现在微服务界炒得沸沸腾腾的ServiceMesh(服务网格),比如 Linkerd 和 Istio。

 

我们项目选择了前者,使用了类似于ESB中心化通信方式来解决,简单来说,你需要一个通信中介者(Mediator)来负责BC之间的交互,结构图如下:

在单体应用的一些DDD实践经验

 

如果你是 .Net 的开发者,请容许我给你安利一下我们在项目中使用的,自己开发的组件——ServiceAnt,它目前只支持进程内的通信,但不久后会开发分布式的。

详细情况你可以点击上面的连接进去查看,也可以查看我写的  另一篇博客  了解ServiceAnt是做什么的,当然你也可以选择 Mediator 来实现这个通信中间件。

Java的话,由于经验较少,没有发现类似的项目,Mule ESB什么的就跟 NServiceBus 一样是重量级的组件,不适用我们这样的场景。

 

以上就是我们用于实现DDD的基础架构,基于这样的架构我们可以很轻松地将现有应用向微服务拆分。

当然,上面的架构隐藏了很多细节,比如大量的基础设施(Ioc,Aop, Logger, cache等等),

原因之一是因为这些东西的设计都很常见,网上你随便就可以搜到相关设计的文章,

原因之二是因为我不想这些细节影响到了读者的关注点,我希望我们可以聚焦于如何实现DDD而不是系统的其他部分。

 

其他的一些话

 

在推行DDD过程中,总会有一些成员会问我,DDD给我们带来的好处是什么。

我总会不厌其烦地告诉他们,为了降低系统的维护成本和更合理地去解决系统业务的复杂性。

但后来我渐渐发现,实现DDD本身就不是一件容易的事情,它会对项目引入新的复杂性,有时候你会发现你团队花上大量时间去建模之后,在开发过程中却依然需要不断修正模型。

这很容易让整个团队士气变低,并且让开发人员有挫败感,这种时候我经常会怀疑DDD对我们而言是否真的有价值。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspxwy.html