谈谈架构层级的“开闭原则”
有界上下文
还好,确实有其他方案可供选择。我们可以让领域驱动设计(DDD)来帮忙。只要将领域拆分成有界上下文,就可以利用其优势来完成工作。在一个超简化的系统模型中,我们可以定义如下的有界上下文:
租赁协议客户 车辆。
租赁专员:使用系统来办理租赁协议租赁中介:通常情况下客户并不直接租车,而是通过代理人来租车
所有这些实体都出现在租赁协议中,但又自成有界上下文。因此,在最初设定消息格式的时候,我们可以根据正在进行的操作来引入主要的有界上下文。在本例中,初始的消息内容设计应该是下面的样子:
{"Status":"Closed","RentalAgreementID":1234,"CustomerID":8965,"VehicleID":98263,"RentalAgent":24352,"Broker":6723}
有了这样的消息结构,我们总算可以在不打破OCP原则和“迪米特法则”的情况下实现CustomerThanking微服务了。更不用说,我们还可以应用这样的消息结构来应付将来新的业务需要。比如:
计算租赁专员佣金。 提供租赁代理的经济信息。 车辆维护事宜。 诸如此类。
最重要的是,通过这样设计的消息内容,我们打开了一扇门,可以添加大量与这个事件相关的新功能(这些功能我们不可能在一开始就想清楚),而不需要改变现存的代码。
四、事件和消息数据
消息是如何构成的?消息的必要内容是什么?
为了回答这些问题,我们首先需要了解消息类型和目标的差异。首先,消息表征了事件,而事件即事实,代表已经发生的事情。我们在既往的时间定义了事件,这些事件代表已经发生过的事情,我们无法改变既成事实。我们在Topic中存储事件的时候,以事件为Topic命名。为了更好的理解事件,我推荐Jonas Bonér的演讲。
事件的目标又是什么呢?我所知道的两种类型事件的目标是这样的:
表征事实。
建立数据流。
用来表征事实的事件,用于如前文所述的系统之中。其主要的目标就是传达某事已经发生的消息,并提供与这一事情相关的有用的数据。我们尝试不多不少地仅提供所需的信息,比如仅提供与事件相关的有界上下文实体的ID。
用于为系统构建数据流的事件,一般是应用于大数据系统中。在大数据系统中充斥着横跨系统范围内大量的信息,信息在传递过程中会经过多次转换。可以让事件附加我们所能提供的尽量多的信息,以便减少在信息转换所带来的额外的成本。
最小化消息信息
为什么在表征事实的时候,最小化信息量是如此的重要?我们来看一个例子。
假设我们需要为系统添加一个新的功能:一个Recommendations的微服务,来向客户发送一封邮件,并根据客户的个人资料推荐一些优惠信息。简化起见,假定我们只需要客户的年龄信息来进行推荐。为了不产生额外的开销,我们可以选择不读数据库,而是把年龄数据存储在消息中(此处先不考虑OCP原则,我们只是在分析在消息中添加新数据产生的影响)。
{"Status":"Closed","RentalAgreementID":5678,"CustomerID":8965,"VehicleID":98263,"RentalAgent":24352,"Broker":6723,"CustomerAge":27}
系统示意图如下:
看起来我们有了一个良好解耦的系统,这很好。但是,事实真的是这样么?假定我们需要修改推荐算法,把用户驾照的发证信息纳入考虑。很简单,只需要把这个字段加到JSON里即可。但在本例中,我们的微服务并没有实际解耦,因此,每次我们需要一个新的字段的时候,就需要同时修改订阅者和发布者。我们的微服务是紧耦合的,并且是以一种很丑陋的方式,直到我们需要对系统做出变更的时候,我们才会意识到这一点。
我们可能会这样想,是不是可以把每个可能的字段都加到消息数据中,这样问题就解决了,我们再也不需要修改发布者和订阅者了。但系统是随时间进化的,总会有一个时刻,需要在模型中添加新的字段,同时需要修改每一个微服务。因此这条路也行不通。
我们所能采取的最好的方案是,在消息中提供足够的信息,既满足我们最初的设计中所考虑到的用例场景的需要,同时也要让这些信息对我们可能尚未考虑到的新的服务是可用的。比如把主要的有界上下文的实体的ID引入 进来,这些实体要么是我们与事件通信的事实的一个组成部分,要么与之相关。新的微服务需要涵盖很多的实体,当然,这会打破“迪米特法则”,但这是必要的妥协。软件架构的要义即在于此:做出恰当的权衡以达成尽量好的系统。遵循开闭原则的能力是如此的重要而有意义,从某种程度上讲,我们可以为此不惜违背“迪米特法则”。
五、总结
1、事件驱动系统给了我们很好的机会来在架构层级应用开闭原则。我们可以重用已有的代码,并且在未知的方向上实现功能的扩展。
2、然而,需要谨慎的设计事件的内容,同时警惕糟糕的设计可能引入的耦合的可能性。
3、要根据系统的目标来指导架构设计,为某一目标设计某种适用的架构(如,为大数据系统设计的流式数据)很可能对于另一目标来说是糟糕的设计(不适用于表征事实发生的事件驱动系统)。
4、领域驱动设计的有界上下文可以为事件内容的设计提供一些指导。
5、架构是关于决策和权衡,最大化应用开闭原则很可能意味着对“迪米特法则”的最小化遵循,必须谨小慎微地寻找一个平衡点。
本文由公众号EAWorld翻译发表,转载需注明出处。
关于EAWorld:微服务,DevOps,数据治理,移动架构原创技术分享,关注EAWorld
图片新闻
最新活动更多
-
11月8日立即预约>> 筑梦启光 砺行致远 | 新天激光数字化产研基地奠基仪式
-
即日-11.13立即报名>>> 【在线会议】多物理场仿真助跑新能源汽车
-
11月20日火热报名中>>> 2024 智能家居出海论坛
-
11月28日立即报名>>> 2024工程师系列—工业电子技术在线会议
-
11月29日立即预约>> 【上海线下】设计,易如反掌—Creo 11发布巡展
-
11月30日立即试用>> 【有奖试用】爱德克IDEC-九大王牌安全产品
发表评论
请输入评论内容...
请输入评论/评论长度6~500个字
暂无评论
暂无评论