mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-11-16 22:23:45 +08:00
fix img
This commit is contained in:
@@ -536,7 +536,7 @@ function hide_canvas() {
|
||||
</blockquote>
|
||||
<p>这种一起成功或一起失败的“同生共死”关系代表的固然是良好的合作,却也说明二者可能存在强耦合关系,甚至是糟糕的<strong>双向依赖</strong>。对于限界上下文的边界而言,即使是逻辑边界,出现双向依赖也是不可饶恕的错误。倘若我们视限界上下文为微服务,则这种“确保这些功能在同一个发布中完成”的要求,无疑抵消了许多微服务带来的好处,负面影响不言而喻。</p>
|
||||
<p>在我过去参与的一个面向教育行业的 SaaS 系统中,我们划分了 ReportEngine、EntityEngine 与 DataEngine 以及 ReportDesigner 等限界上下文。当绘制出上下文映射图时,我们发现这多个限界上下文之间出现了双向依赖与循环依赖,如下图所示:</p>
|
||||
<p><img src="assets/4d13b140-ab36-11e8-bd17-d5905bbd0c49" alt="enter image description here" /></p>
|
||||
<p><img src="assets/4d13b140-ab36-11e8-bd17-d5905bbd0c49" alt="png" /></p>
|
||||
<p><strong>说明:</strong> 虽然在领域驱动设计中,我们应该以标准的模式来表示限界上下文之间的关系,例如标注 U 和 D 代表上游和下游,标注 Partnership 说明二者为合作关系。但在上图我却采用了依赖方式来说明,目的是可以更清晰地体现双向依赖和循环依赖的特征。</p>
|
||||
<p>ReportEngine 与 EntityEngine 之间存在双向依赖,二者又与 DataEngine 之间产生了循环依赖。这种依赖导致三个限界上下文“貌离神合”,边界控制不够彻底,使得它们并不能真正的分开。倘若这三个限界上下文被构建为三个 JAR 包,这种依赖会导致它们在编译时谁也离不开谁。如果是微服务,则任何一个服务出现故障,其他服务都不可用。</p>
|
||||
<p>我个人认为限界上下文的“合作关系”其实是一种“反模式”,罪魁祸首是因为职责分配的不当,是一种设计层面的“特性依恋(Feature envy)”坏味道。解决的办法通常有三种:</p>
|
||||
@@ -546,7 +546,7 @@ function hide_canvas() {
|
||||
<li>识别产生双向依赖或循环依赖的原因,然后将它们从各个限界上下文中剥离出来,并为其建立单独的限界上下文,这就是所谓的“共享内核(Shared Kernel)”。</li>
|
||||
</ul>
|
||||
<p>分析前面的例子,之所以 ReportEngine、EntityEngine 与 DataEngine 之间存在不正确的循环依赖,原因是我们错误地将<strong>元数据功能</strong>放到了 ReportEngine 限界上下文中。EntityEngine 与DataEngine 之所以依赖 ReportEngine,并不是需要调用属于 ReportEngine 本身职责的功能,而是需要访问元数据。事实上,我们还发现 ReportDesigner 也是因为需要访问元数据,才会依赖 ReportEngine。此时,拆分出单独的元数据限界上下文才是最佳选择:</p>
|
||||
<p><img src="assets/5d46e0f0-ab36-11e8-bdd5-d99c499845ac" alt="enter image description here" /></p>
|
||||
<p><img src="assets/5d46e0f0-ab36-11e8-bdd5-d99c499845ac" alt="png" /></p>
|
||||
<p>新引入的 Metadata 成为了其余限界上下文的上游,却解除了 DataEngine 对 ReportEngine 的依赖,同样解除了 EntityEngine 以及 ReportDesigner 对 ReportEngine 的依赖。多余引入的 Metadata 上下文就是我们之前在识别上下文时未曾发现的,现在通过上下文映射,帮助我们甄别了这一错误,及时调整了系统的限界上下文。</p>
|
||||
<h3>共享内核(Shared Kernel)</h3>
|
||||
<p>前面提取“元数据限界上下文”的模式,就是“共享内核”的体现。从设计层面看,共享内核是解除不必要依赖实现重用的重要手段。当我们发现了属于共享内核的限界上下文后,需要确定它的团队归属。注意,共享内核仍然属于领域的一部分,它不是横切关注点,也不是公共的基础设施。分离出来的共享内核属于上游团队的职责,因而需要处理好它与下游团队的协作。</p>
|
||||
@@ -580,7 +580,7 @@ function hide_canvas() {
|
||||
<h3>分离方式(Separate Ways)</h3>
|
||||
<p>分离方式的合作模式就是指两个限界上下文之间没有哪怕一丁点儿的丝毫关系。这种“无关系”仍然是一种关系,而且是一种最好的关系。这意味着我们无需考虑它们之间的集成与依赖,它们可以独立变化而互相不产生影响,还有什么比这更美好的呢?</p>
|
||||
<p>在典型的电商网站中,支付上下文与商品上下文之间就没有任何关系,二者是“分离方式”的体现。虽然从业务角度理解,客户购买商品,确乎是为商品进行支付,但在商品上下文中,我们关心的是商品的价格(另一种可能是将价格作为一个独立的上下文),在支付上下文,关注的却是每笔交易的金额。商品价格影响的是订单上下文,支付上下文会作为订单上下文的上游,被订单上下文调用,但这种调用传递的是每条订单的总金额,支付上下文并不关心每笔订单究竟包含了哪些商品。唯一让支付上下文与商品上下文之间可能存在关联的因素,是二者的领域模型中都需要 Money 值对象。我们可以在这两个限界上下文中重复定义 Money 值对象。如果 Money 值对象其实还牵涉到复杂的货币转换以及高精度的运算逻辑,我宁可将类似 Money 这样的对象剥离到单独的上下文中,例如单独拎出来一个货币上下文。此时的货币上下文其实是支付上下文与商品上下文的共享内核:</p>
|
||||
<p><img src="assets/7b519d10-ab36-11e8-8793-2b2eb0e28f96" alt="enter image description here" /></p>
|
||||
<p><img src="assets/7b519d10-ab36-11e8-8793-2b2eb0e28f96" alt="png" /></p>
|
||||
<p>“分离方式”的映射模式看起来容易识别,然而一旦系统的领域知识变得越来越复杂,导致多个限界上下文之间存在错综复杂的关系时,要识别两个限界上下文之间压根没有一点关系,就需要敏锐的“视力”了。这种没有关系的关系似乎无足轻重,其实不然,它对改进设计质量以及团队组织都有较大帮助。两个毫无交流与协作关系的团队看似冷漠无情,然而,正是这种“无情”才能促进它们独立发展,彼此不受影响。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user