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:
@@ -421,7 +421,7 @@ function hide_canvas() {
|
||||
<p>在思考限界上下文之间的协作关系时,首先我们需要确定是否存在关系,然后再确定是何种关系,最后再基于变化导致的影响来确定是否需要引入防腐层、开放主机服务等模式。倘若发现协作关系有不合理之处,则需要反思之前我们识别出来的限界上下文是否合理。</p>
|
||||
<h3>限界上下文通信边界对协作的影响</h3>
|
||||
<p>确定限界上下文之间的关系不能想当然,需得全面考虑参与到两个限界上下文协作的业务场景,然后在场景中识别二者之间产生依赖的原因,确定依赖的方向,进而确定集成点,需要注意的是,<strong>限界上下文的通信边界对于界定协作关系至为关键</strong>。限界上下文的通信边界分为<strong>进程内边界</strong>与<strong>进程间边界</strong>,这种通信边界会直接影响到我们对上下文映射模式的选择。例如,采用进程间边界,就需得考虑跨进程访问的成本,如序列化与反序列化、网络开销等。由于跨进程调用的限制,彼此之间的访问协议也不尽相同,同时还需要控制上游限界上下文可能引入的变化,一个典型的协作方式是同时引入开放主机服务(OHS)与防腐层(ACL),如下图所示:</p>
|
||||
<p><img src="assets/0b7033d0-ab31-11e8-807c-2dcb8b265ca8" alt="enter image description here" /></p>
|
||||
<p><img src="assets/0b7033d0-ab31-11e8-807c-2dcb8b265ca8" alt="png" /></p>
|
||||
<p>限界上下文 A 对外通过控制器(Controller)为用户界面层暴露 REST 服务,而在内部则调用应用层的应用服务(Application Service),然后再调用领域层的领域模型(Domain Model)。倘若限界上下文 A 需要访问限界上下文 B 的服务,则通过放置在领域层的接口(Interface)去访问,但真正的访问逻辑实现则由基础设施层的客户端(Client)完成,这个客户端就是上下文映射模式的防腐层。客户端访问的其实是限界上下文 B 的控制器,这个控制器处于基础设施层,相当于上下文映射模式的开放主机服务。限界上下文 B 访问限界上下文 C 的方式完全一致,在限界上下文 C 中,则通过资源库(Repository)接口经由持久化(Persistence)组件访问数据库。</p>
|
||||
<p>从图中可以看到,当我们在界定限界上下文的协作关系时,需要考虑分层架构设计。通常,我们会将分层架构的应用层、领域层与基础设施层都视为在限界上下文的边界之内。如果限界上下文并未采用“零共享架构”,那么,在考虑协作关系时还需要考虑数据库层是否存在耦合。</p>
|
||||
<p>唯独分层架构的用户界面层是一个例外,我们在领域建模时,通常不会考虑用户界面层,它并不属于限界上下文。究其原因,在于用户界面层与领域的观察视角完全不同。用户界面层重点考虑的是用户体验,而非业务的垂直划分,更不会考虑到业务之间的高内聚、松耦合。许多时候,为了用户操作的方便性,减少用户的操作次数,提高用户体验,可能会在一个 UI 页面中聚合许多属于不同限界上下文的业务。我们可以看看亚马逊或京东的页面,例如,在“我的京东”页面下,几乎将整个电商系统中各方面的业务都一网打尽了。这不符合我们对限界上下文的划分原则。事实上,在“前后端分离”的架构中,用户界面层往往会作为后端服务的调用者,当然应该被排除在限界上下文之外了。</p>
|
||||
@@ -442,7 +442,7 @@ function hide_canvas() {
|
||||
<li>请求其他对象帮忙完成部分工作(和其他对象协作)。</li>
|
||||
<li>将整个服务请求委托给另外的帮助对象。</li>
|
||||
</ul>
|
||||
<p><img src="assets/23bf7fe0-ab31-11e8-807c-2dcb8b265ca8" alt="enter image description here" /></p>
|
||||
<p><img src="assets/23bf7fe0-ab31-11e8-807c-2dcb8b265ca8" alt="png" /></p>
|
||||
<p>如果我们选择后两种履行职责的形式,就必然牵涉到对象之间的协作。<strong>一个好的设计,职责一定是“分治”的,就是让每个高内聚的对象只承担自己擅长处理的部分,而将自己不擅长的职责转移到别的对象</strong>。《建筑的永恒之道》作者 Christepher Alexander 就建议,<strong>在遇到设计问题时尽量少用集权的机制</strong>。还是在《对象设计:角色、职责与协作》这本书,作者认为:</p>
|
||||
<blockquote>
|
||||
<p>软件对象通过相互作用和共享责任联系在一起。在对象之间建立简单、一致的通信机制,避免了解决方案的集权性,局部变化的影响不应扩散到整个系统,这是系统的强适应性所要求的。当职责得以划分,组织有序,同时协作遵循可预测性模式,那么复杂的软件系统就更便于管理。</p>
|
||||
@@ -459,7 +459,7 @@ function hide_canvas() {
|
||||
<p>领域模型的确定总是会引起争论,毕竟每个人观察领域模型的角度不同,对设计的看法也不相同。领域模型最终要落实到代码实现,交给实践去检验设计的合理性,<strong>不要在领域建模过程中过多纠缠建模的细节,选择一个恰好合理的模型即可</strong>。从建模到设计,再从设计到编码开发,其实是一个迭代的过程,倘若在实现时确实发现模型存在瑕疵,再回过头来修改即可,孜孜以求领域模型的完美,纯属浪费时间,在建模过程中,最重要的是守住最根本的设计原则。在合理运用设计原则之前,要紧的是明确:<strong>我们究竟要解决什么问题?</strong></p>
|
||||
<p>这里的问题不是如何确定领域模型,而是要确定提交订单这个行为究竟应该分配给谁?首先,这牵涉到<strong>对象的职责分配问题</strong>。从语义相关性剖析,这个领域行为虽然由客户发起,但操作的信息(知识)主体其实是订单,这就意味着它们应该分配给订单上下文。这种分配实际上也符合面向对象设计原则的“信息专家模式”,即“信息的持有者即为操作该行为的专家”;其次,<strong>从分层架构的角度</strong>看,这里所谓的“由客户发起调用”,仅仅代表客户通过用户界面层发起对后端服务的请求,换言之,并不是由属于客户上下文的 Customer 领域对象发起调用。</p>
|
||||
<p>后面我们会讲到,如果遵循整洁架构的思想,领域层应该处于限界上下文的核心。为了保证业务用例的完整性,并避免暴露太多领域协作的细节,领域驱动设计引入了应用层,它包裹了整个领域层;然而,应用层并不会直接与作为调用者的前端进行通信,通常的方式是引入 RESTful 服务,这个 RESTful 服务等同于上下文映射中的开放主机服务(OHS),又相当于是 MVC 模式中的控制器(Controller),属于基础设施层的组件。针对下订单这个场景,客户通过用户界面层的 OrderController 发起调用。OrderController 收到请求后,在处理了请求消息的验证与转换工作后,又将职责转交给了 OrderAppService,然后通过它访问领域层中的领域服务 PlaceOrderService,如下图所示:</p>
|
||||
<p><img src="assets/36b3cb60-ab31-11e8-807c-2dcb8b265ca8" alt="enter image description here" /></p>
|
||||
<p><img src="assets/36b3cb60-ab31-11e8-807c-2dcb8b265ca8" alt="png" /></p>
|
||||
<p>下订单场景的实现代码如下所示:</p>
|
||||
<pre><code class="language-java">@RestController
|
||||
@RequestMapping(value = "/orders/")
|
||||
|
||||
Reference in New Issue
Block a user