mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
977 lines
48 KiB
HTML
977 lines
48 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
<link rel="icon" href="/static/favicon.png">
|
||
<title>020 辨别限界上下文的协作关系(上).md.html</title>
|
||
<!-- Spectre.css framework -->
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
<!-- theme css & js -->
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
</head>
|
||
|
||
<body>
|
||
|
||
<div class="book-container">
|
||
<div class="book-sidebar">
|
||
<div class="book-brand">
|
||
<a href="/">
|
||
<img src="/static/favicon.png">
|
||
<span>技术文章摘抄</span>
|
||
</a>
|
||
</div>
|
||
<div class="book-menu uncollapsible">
|
||
<ul class="uncollapsible">
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
</ul>
|
||
|
||
<ul class="uncollapsible">
|
||
<li><a href="../">上一级</a></li>
|
||
</ul>
|
||
|
||
<ul class="uncollapsible">
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/001 「战略篇」访谈 DDD 和微服务是什么关系?.md.html">001 「战略篇」访谈 DDD 和微服务是什么关系?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/002 「战略篇」开篇词:领域驱动设计,重焕青春的设计经典.md.html">002 「战略篇」开篇词:领域驱动设计,重焕青春的设计经典.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/003 领域驱动设计概览.md.html">003 领域驱动设计概览.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/004 深入分析软件的复杂度.md.html">004 深入分析软件的复杂度.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/005 控制软件复杂度的原则.md.html">005 控制软件复杂度的原则.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/006 领域驱动设计对软件复杂度的应对(上).md.html">006 领域驱动设计对软件复杂度的应对(上).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/007 领域驱动设计对软件复杂度的应对(下).md.html">007 领域驱动设计对软件复杂度的应对(下).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/008 软件开发团队的沟通与协作.md.html">008 软件开发团队的沟通与协作.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/009 运用领域场景分析提炼领域知识(上).md.html">009 运用领域场景分析提炼领域知识(上).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/010 运用领域场景分析提炼领域知识(下).md.html">010 运用领域场景分析提炼领域知识(下).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/011 建立统一语言.md.html">011 建立统一语言.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/012 理解限界上下文.md.html">012 理解限界上下文.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/013 限界上下文的控制力(上).md.html">013 限界上下文的控制力(上).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/014 限界上下文的控制力(下).md.html">014 限界上下文的控制力(下).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/015 识别限界上下文(上).md.html">015 识别限界上下文(上).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/016 识别限界上下文(下).md.html">016 识别限界上下文(下).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/017 理解上下文映射.md.html">017 理解上下文映射.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/018 上下文映射的团队协作模式.md.html">018 上下文映射的团队协作模式.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/019 上下文映射的通信集成模式.md.html">019 上下文映射的通信集成模式.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
<a class="current-tab" href="/专栏/领域驱动设计实践(完)/020 辨别限界上下文的协作关系(上).md.html">020 辨别限界上下文的协作关系(上).md.html</a>
|
||
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/021 辨别限界上下文的协作关系(下).md.html">021 辨别限界上下文的协作关系(下).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/022 认识分层架构.md.html">022 认识分层架构.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/023 分层架构的演化.md.html">023 分层架构的演化.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/024 领域驱动架构的演进.md.html">024 领域驱动架构的演进.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/025 案例 层次的职责与协作关系(图文篇).md.html">025 案例 层次的职责与协作关系(图文篇).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/026 限界上下文与架构.md.html">026 限界上下文与架构.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/027 限界上下文对架构的影响.md.html">027 限界上下文对架构的影响.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/028 领域驱动设计的代码模型.md.html">028 领域驱动设计的代码模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/029 代码模型的架构决策.md.html">029 代码模型的架构决策.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/030 实践 先启阶段的需求分析.md.html">030 实践 先启阶段的需求分析.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/031 实践 先启阶段的领域场景分析(上).md.html">031 实践 先启阶段的领域场景分析(上).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/032 实践 先启阶段的领域场景分析(下).md.html">032 实践 先启阶段的领域场景分析(下).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/033 实践 识别限界上下文.md.html">033 实践 识别限界上下文.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/034 实践 确定限界上下文的协作关系.md.html">034 实践 确定限界上下文的协作关系.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/035 实践 EAS 的整体架构.md.html">035 实践 EAS 的整体架构.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/036 「战术篇」访谈:DDD 能帮开发团队提高设计水平吗?.md.html">036 「战术篇」访谈:DDD 能帮开发团队提高设计水平吗?.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/037 「战术篇」开篇词:领域驱动设计的不确定性.md.html">037 「战术篇」开篇词:领域驱动设计的不确定性.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/038 什么是模型.md.html">038 什么是模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/039 数据分析模型.md.html">039 数据分析模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/040 数据设计模型.md.html">040 数据设计模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/041 数据模型与对象模型.md.html">041 数据模型与对象模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/042 数据实现模型.md.html">042 数据实现模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/043 案例 培训管理系统.md.html">043 案例 培训管理系统.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/044 服务资源模型.md.html">044 服务资源模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/045 服务行为模型.md.html">045 服务行为模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/046 服务设计模型.md.html">046 服务设计模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/047 领域模型驱动设计.md.html">047 领域模型驱动设计.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/048 领域实现模型.md.html">048 领域实现模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/049 理解领域模型.md.html">049 理解领域模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/050 领域模型与结构范式.md.html">050 领域模型与结构范式.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/051 领域模型与对象范式(上).md.html">051 领域模型与对象范式(上).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/052 领域模型与对象范式(中).md.html">052 领域模型与对象范式(中).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/053 领域模型与对象范式(下).md.html">053 领域模型与对象范式(下).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/054 领域模型与函数范式.md.html">054 领域模型与函数范式.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/055 领域驱动分层架构与对象模型.md.html">055 领域驱动分层架构与对象模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/056 统一语言与领域分析模型.md.html">056 统一语言与领域分析模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/057 精炼领域分析模型.md.html">057 精炼领域分析模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/058 彩色 UML 与彩色建模.md.html">058 彩色 UML 与彩色建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/059 四色建模法.md.html">059 四色建模法.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/060 案例 订单核心流程的四色建模.md.html">060 案例 订单核心流程的四色建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/061 事件风暴与业务全景探索.md.html">061 事件风暴与业务全景探索.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/062 事件风暴与领域分析建模.md.html">062 事件风暴与领域分析建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/063 案例 订单核心流程的事件风暴.md.html">063 案例 订单核心流程的事件风暴.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/064 表达领域设计模型.md.html">064 表达领域设计模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/065 实体.md.html">065 实体.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/066 值对象.md.html">066 值对象.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/067 对象图与聚合.md.html">067 对象图与聚合.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/068 聚合设计原则.md.html">068 聚合设计原则.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/069 聚合之间的关系.md.html">069 聚合之间的关系.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/070 聚合的设计过程.md.html">070 聚合的设计过程.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/071 案例 培训领域模型的聚合设计.md.html">071 案例 培训领域模型的聚合设计.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/072 领域模型对象的生命周期-工厂.md.html">072 领域模型对象的生命周期-工厂.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/073 领域模型对象的生命周期-资源库.md.html">073 领域模型对象的生命周期-资源库.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/074 领域服务.md.html">074 领域服务.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/075 案例 领域设计模型的价值.md.html">075 案例 领域设计模型的价值.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/076 应用服务.md.html">076 应用服务.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/077 场景的设计驱动力.md.html">077 场景的设计驱动力.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/078 案例 薪资管理系统的场景驱动设计.md.html">078 案例 薪资管理系统的场景驱动设计.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/079 场景驱动设计与 DCI 模式.md.html">079 场景驱动设计与 DCI 模式.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/080 领域事件.md.html">080 领域事件.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/081 发布者—订阅者模式.md.html">081 发布者—订阅者模式.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/082 事件溯源模式.md.html">082 事件溯源模式.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/083 测试优先的领域实现建模.md.html">083 测试优先的领域实现建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/084 深入理解简单设计.md.html">084 深入理解简单设计.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/085 案例 薪资管理系统的测试驱动开发(上).md.html">085 案例 薪资管理系统的测试驱动开发(上).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/086 案例 薪资管理系统的测试驱动开发(下).md.html">086 案例 薪资管理系统的测试驱动开发(下).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/087 对象关系映射(上).md.html">087 对象关系映射(上).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/088 对象关系映射(下).md.html">088 对象关系映射(下).md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/089 领域模型与数据模型.md.html">089 领域模型与数据模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/090 领域驱动设计对持久化的影响.md.html">090 领域驱动设计对持久化的影响.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/091 领域驱动设计体系.md.html">091 领域驱动设计体系.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/092 子领域与限界上下文.md.html">092 子领域与限界上下文.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/093 限界上下文的边界与协作.md.html">093 限界上下文的边界与协作.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/094 限界上下文之间的分布式通信.md.html">094 限界上下文之间的分布式通信.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/095 命令查询职责分离.md.html">095 命令查询职责分离.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/096 分布式柔性事务.md.html">096 分布式柔性事务.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/097 设计概念的统一语言.md.html">097 设计概念的统一语言.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/098 模型对象.md.html">098 模型对象.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/099 领域驱动设计参考过程模型.md.html">099 领域驱动设计参考过程模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/100 领域驱动设计的精髓.md.html">100 领域驱动设计的精髓.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/101 实践 员工上下文的领域建模.md.html">101 实践 员工上下文的领域建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/102 实践 考勤上下文的领域建模.md.html">102 实践 考勤上下文的领域建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/103 实践 项目上下文的领域建模.md.html">103 实践 项目上下文的领域建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/104 实践 培训上下文的业务需求.md.html">104 实践 培训上下文的业务需求.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/105 实践 培训上下文的领域分析建模.md.html">105 实践 培训上下文的领域分析建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/106 实践 培训上下文的领域设计建模.md.html">106 实践 培训上下文的领域设计建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/107 实践 培训上下文的领域实现建模.md.html">107 实践 培训上下文的领域实现建模.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/108 实践 EAS 系统的代码模型.md.html">108 实践 EAS 系统的代码模型.md.html</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/109 后记:如何学习领域驱动设计.md.html">109 后记:如何学习领域驱动设计.md.html</a>
|
||
|
||
</li>
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
<div class="sidebar-toggle-inner"></div>
|
||
</div>
|
||
|
||
<script>
|
||
function add_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.add('show')
|
||
}
|
||
|
||
function remove_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.remove('show')
|
||
}
|
||
|
||
function sidebar_toggle() {
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let content = document.querySelector('.off-canvas-content')
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
sidebar_toggle.classList.remove('extend')
|
||
sidebar.classList.remove('hide')
|
||
content.classList.remove('extend')
|
||
} else { // hide
|
||
sidebar_toggle.classList.add('extend')
|
||
sidebar.classList.add('hide')
|
||
content.classList.add('extend')
|
||
}
|
||
}
|
||
|
||
|
||
function open_sidebar() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.add('show')
|
||
overlay.classList.add('show')
|
||
}
|
||
function hide_canvas() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.remove('show')
|
||
overlay.classList.remove('show')
|
||
}
|
||
|
||
</script>
|
||
|
||
<div class="off-canvas-content">
|
||
<div class="columns">
|
||
<div class="column col-12 col-lg-12">
|
||
<div class="book-navbar">
|
||
<!-- For Responsive Layout -->
|
||
<header class="navbar">
|
||
<section class="navbar-section">
|
||
<a onclick="open_sidebar()">
|
||
<i class="icon icon-menu"></i>
|
||
</a>
|
||
</section>
|
||
</header>
|
||
</div>
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;">
|
||
<div class="book-post">
|
||
<p id="tip" align="center"></p>
|
||
<div><h1>020 辨别限界上下文的协作关系(上)</h1>
|
||
<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>限界上下文 A 对外通过控制器(Controller)为用户界面层暴露 REST 服务,而在内部则调用应用层的应用服务(Application Service),然后再调用领域层的领域模型(Domain Model)。倘若限界上下文 A 需要访问限界上下文 B 的服务,则通过放置在领域层的接口(Interface)去访问,但真正的访问逻辑实现则由基础设施层的客户端(Client)完成,这个客户端就是上下文映射模式的防腐层。客户端访问的其实是限界上下文 B 的控制器,这个控制器处于基础设施层,相当于上下文映射模式的开放主机服务。限界上下文 B 访问限界上下文 C 的方式完全一致,在限界上下文 C 中,则通过资源库(Repository)接口经由持久化(Persistence)组件访问数据库。</p>
|
||
<p>从图中可以看到,当我们在界定限界上下文的协作关系时,需要考虑分层架构设计。通常,我们会将分层架构的应用层、领域层与基础设施层都视为在限界上下文的边界之内。如果限界上下文并未采用“零共享架构”,那么,在考虑协作关系时还需要考虑数据库层是否存在耦合。</p>
|
||
<p>唯独分层架构的用户界面层是一个例外,我们在领域建模时,通常不会考虑用户界面层,它并不属于限界上下文。究其原因,在于用户界面层与领域的观察视角完全不同。用户界面层重点考虑的是用户体验,而非业务的垂直划分,更不会考虑到业务之间的高内聚、松耦合。许多时候,为了用户操作的方便性,减少用户的操作次数,提高用户体验,可能会在一个 UI 页面中聚合许多属于不同限界上下文的业务。我们可以看看亚马逊或京东的页面,例如,在“我的京东”页面下,几乎将整个电商系统中各方面的业务都一网打尽了。这不符合我们对限界上下文的划分原则。事实上,在“前后端分离”的架构中,用户界面层往往会作为后端服务的调用者,当然应该被排除在限界上下文之外了。</p>
|
||
<p>这里存在一个设计决策,即引入开放主机服务与防腐层是否必要?这就需要设计者权衡变化、代码重用、架构简单性的优先级。没有标准答案,而需结合具体的应用场景帮助你做出判断。我自然无法穷尽所有的业务场景,这里给出的无非是其中一种选择罢了。譬如说,倘若限界上下文采用进程内通信,那么下游限界上下文是否还需要通过客户端与控制器去访问,就值得斟酌了。如果需要考虑未来从进程内通信演化为进程间通信,则保留客户端及其接口就是有必要的。</p>
|
||
<p><strong>说明</strong>:以上提到的限界上下文通信边界、领域驱动设计分层架构、零共享架构、代码模型结构以及北向网关、南向网关的知识,都会在后面章节详细阐述。</p>
|
||
<h3>协作即依赖</h3>
|
||
<p>如果限界上下文之间存在协作关系,必然是某种原因导致这种协作关系。从依赖的角度看,这种协作关系是因为一方需要“<strong>知道</strong>”另一方的知识,这种知识包括:</p>
|
||
<ul>
|
||
<li>领域行为:需要判断导致行为之间的耦合原因是什么?如果是上下游关系,要确定<strong>下游是否就是上游服务的真正调用者</strong>。</li>
|
||
<li>领域模型:需要重用别人的领域模型,还是自己重新定义一个模型。</li>
|
||
<li>数据:是否需要限界上下文对应的数据库提供支撑业务行为的操作数据。</li>
|
||
</ul>
|
||
<h4>领域行为产生的依赖</h4>
|
||
<p>所谓领域行为,落到设计层面,其实就是每个领域对象的职责,职责可以由实体(Entity)、值对象(Value Object)来承担,也可以是领域服务(Domain Service)或者资源库(Repository)乃至工厂(Factory)对象来承担。</p>
|
||
<p>对象履行职责的方式有三种,Rebecca Wirfs-Brock 在《对象设计:角色、职责与协作》一书中总结为:</p>
|
||
<ul>
|
||
<li>亲自完成所有的工作。</li>
|
||
<li>请求其他对象帮忙完成部分工作(和其他对象协作)。</li>
|
||
<li>将整个服务请求委托给另外的帮助对象。</li>
|
||
</ul>
|
||
<p><img src="assets/23bf7fe0-ab31-11e8-807c-2dcb8b265ca8" alt="enter image description here" /></p>
|
||
<p>如果我们选择后两种履行职责的形式,就必然牵涉到对象之间的协作。<strong>一个好的设计,职责一定是“分治”的,就是让每个高内聚的对象只承担自己擅长处理的部分,而将自己不擅长的职责转移到别的对象</strong>。《建筑的永恒之道》作者 Christepher Alexander 就建议,<strong>在遇到设计问题时尽量少用集权的机制</strong>。还是在《对象设计:角色、职责与协作》这本书,作者认为:</p>
|
||
<blockquote>
|
||
<p>软件对象通过相互作用和共享责任联系在一起。在对象之间建立简单、一致的通信机制,避免了解决方案的集权性,局部变化的影响不应扩散到整个系统,这是系统的强适应性所要求的。当职责得以划分,组织有序,同时协作遵循可预测性模式,那么复杂的软件系统就更便于管理。</p>
|
||
</blockquote>
|
||
<p>领域驱动设计提出的限界上下文事实上是架构层次的“分权”,通过它的边界让“职责得以划分,组织有序”,限界上下文之间的协作也“遵循可预测性模式”,就可以有效地控制业务复杂度与技术复杂度。因此,在考虑限界上下文的协作关系时,关键要辨别这些分离的职责,弄清楚到底是限界上下文内的对象协作,还是限界上下文之间的对象协作,主要考虑有如下两个方面:</p>
|
||
<ul>
|
||
<li>职责由谁来履行?——这牵涉到领域行为该放置在哪一个限界上下文。</li>
|
||
<li>谁发起对该职责的调用?——倘若发起调用者与职责履行者在不同限界上下文,则意味着二者存在协作关系,且能够帮助我们确定上下游关系。</li>
|
||
</ul>
|
||
<p>以电商系统的订单功能为例。考虑一个业务场景,客户已经选择好要购买的商品,并通过购物车提交订单,这就牵涉到一个领域行为:提交订单。假设客户属于客户上下文,而订单属于订单上下文,现在需要考虑提交订单的职责由谁来履行。</p>
|
||
<p>从电商系统的现实模型看,该领域行为由客户发起,也就是说客户应该具有提交订单的行为,这是否意味着应该将该行为分配给 Customer 聚合根?其实不然,<strong>我们需要注意现实模型与领域模型尤其是对象模型的区别</strong>。在“下订单”这个业务场景中,Customer 是一个参与者,角色为买家。领域建模的一种观点认为:<strong>领域模型是排除参与者在外的客观世界的模型</strong>,作为参与者的 Customer 应该排除在这个模型之外。</p>
|
||
<p>当然,这一观点亦存在争议,例如,四色建模就不这样认为,四色建模建议在时标性对象与作为人的实体对象之间引入角色对象,也就是说,角色对象会作为领域模型的一份子。当然,我们不能直接给角色与模型的参与者划上等号。在 DCI(Data Context Interation)模式中,则需要在一个上下文(Context)中,通过识别角色来思考它们之间的协作关系。譬如在转账业务场景中,银行账户 Account 作为数据对象(Data)参与到转账上下文的协作,此时应抽象出 Source 与 Destination 两个角色对象。</p>
|
||
<p><strong>说明</strong>:在战术设计内容中,我会再深入探讨领域建模、四色建模与 DCI 之间的关系与建模细节。</p>
|
||
<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>下订单场景的实现代码如下所示:</p>
|
||
<pre><code class="language-java">@RestController
|
||
@RequestMapping(value = "/orders/")
|
||
public class OrderController {
|
||
@Autowired
|
||
private OrderAppService service;
|
||
|
||
@RequestMapping(method = RequestMethod.POST)
|
||
public void create(@RequestParam(value = "request", required = true) CreateOrderRequest request) {
|
||
if (request.isInvalid()) {
|
||
throw new BadRequestException("the request of placing order is invalid.");
|
||
}
|
||
Order order = request.toOrder();
|
||
service.placeOrder(order);
|
||
}
|
||
}
|
||
|
||
@Service
|
||
public class OrderAppService {
|
||
@Autowired
|
||
private PlaceOrderService orderService;
|
||
|
||
public void placeOrder(Order order) {
|
||
try {
|
||
placeOrderService.execute(order);
|
||
} catch (InvalidOrderException | Exception ex) {
|
||
throw new ApplicationException(ex.getMessage());
|
||
}
|
||
}
|
||
}
|
||
|
||
</code></pre>
|
||
<p>既然 PlaceOrderService、OrderAppService 与 OrderController 都属于订单上下文,而该行为调用的真正发起者又不是 Customer 领域对象,而是通过用户界面与系统进行交互操作的用户,因此在这个业务场景中,并不存在我们想象的因为客户下订单导致客户上下文对订单上下文在领域行为上的依赖。</p>
|
||
<p>在将调用职责分配给前端时,我们需要时刻保持谨慎,不能将对限界上下文调用的工作全都交给前端,以此来解除后端限界上下文之间的耦合。前端确乎是发起调用的最佳位置,但前提是:<strong>我们不能让前端来承担后端应该封装的业务逻辑</strong>。当一个领域行为成为另一个领域行为“内嵌”的一个执行步骤时,发起的调用者就不再是前端 UI,因为该执行步骤组成了业务逻辑的一部分。例如,在计算订单总价时,需要根据客户的类别确定不同的促销策略,然后根据促销策略计算订单的总价,这里牵涉到四个领域行为:</p>
|
||
<ul>
|
||
<li>计算订单总价</li>
|
||
<li>获得客户类别</li>
|
||
<li>确定促销策略</li>
|
||
<li>计算促销折扣</li>
|
||
</ul>
|
||
<p>后面三个领域行为都是为“计算订单总价”提供功能支撑的,这就是前面所谓的“内嵌”执行步骤。除了订单总价属于订单上下文的行为,获得客户类别属于客户上下文,而促销策略与折扣计算则属于促销上下文。因为产生了领域行为的依赖,它们会作为订单上下文的上游限界上下文。</p>
|
||
<p>这里其实存在设计上的变化,这取决于我们对职责的分层(在前面讲解的领域场景分析中介绍了职责的分层):</p>
|
||
<ul>
|
||
<li>计算订单总价——订单上下文
|
||
<ul>
|
||
<li>获得客户类别——客户上下文</li>
|
||
<li>根据客户类别获得促销策略——促销上下文</li>
|
||
<li>通过促销策略计算促销折扣——促销上下文</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>当采用这种职责分层结构时,客户上下文与促销上下文就是订单上下文的上游。如果我们将获得客户类别视为促销上下文内含的业务逻辑,则职责的分层结构就变为:</p>
|
||
<ul>
|
||
<li>计算订单总价——订单上下文
|
||
<ul>
|
||
<li>获得促销策略——促销上下文
|
||
<ul>
|
||
<li>获得客户类别——客户上下文</li>
|
||
<li>根据客户类别获得促销策略——促销上下文</li>
|
||
</ul>
|
||
</li>
|
||
<li>通过促销策略计算促销折扣——促销上下文</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>这时候,订单上下文的上游为促销上下文,而在促销上下文内部,又需要去调用客户上下文的领域行为。</p>
|
||
<p>我们甚至可以对职责做进一步封装。因为对于计算订单总价而言,其实它并不关心促销折扣究竟是怎样得来的,也就是说,获得促销策略这个职责其实是计算促销折扣的细节,于是职责的分层结构再次变化:</p>
|
||
<ul>
|
||
<li>计算订单总价——订单上下文
|
||
<ul>
|
||
<li>计算促销折扣——促销上下文
|
||
<ul>
|
||
<li>获得促销策略——促销上下文
|
||
<ul>
|
||
<li>获得客户类别——客户上下文</li>
|
||
<li>根据客户类别获得促销策略——促销上下文</li>
|
||
</ul>
|
||
</li>
|
||
<li>通过促销策略计算促销折扣——促销上下文</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>这样的设计既可以减少其他限界上下文与订单上下文的协作,又可以减少彼此协作时需要依赖的领域行为。例如,我们如果希望降低订单上下文与促销上下文之间的耦合,从而避免促销上下文可能发生的变化对订单上下文的影响,就可以引入上下文映射中的防腐层。由于订单上下文只需要知道“计算促销折扣”这一个领域行为职责,防腐层接口的设计就变得更加容易:</p>
|
||
<pre><code class="language-java">package praticeddd.ecommerce.saleordercontext.domain;
|
||
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.stereotype.Service;
|
||
|
||
// DiscountCalculator 是定义在订单上下文的防腐层接口
|
||
import praticeddd.ecommerce.saleordercontext.interfaces.DiscountCalculator;
|
||
|
||
@Service
|
||
public class PriceCalculator {
|
||
@Autowired
|
||
private DiscountCalculator discountCalculator;
|
||
|
||
public Price priceFor(Order order) {
|
||
double discount = discountCalculator.calculate(order);
|
||
return order.totalPrice().multiply(discount);
|
||
}
|
||
}
|
||
|
||
package praticeddd.ecommerce.saleordercontext.interfaces;
|
||
|
||
public interface DiscountCalculator {
|
||
double calculate(Order order);
|
||
}
|
||
|
||
</code></pre>
|
||
<p>显然,不同的职责分层会直接影响到我们对限界上下文协作关系的判断。归根结底,还是彼此之间需要了解的“知识”起着决定作用。我们应尽可能遵循“<strong>最小知识法则</strong>”,在保证职责合理分配的前提下,产生协作的限界上下文越少越好。</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/领域驱动设计实践(完)/019 上下文映射的通信集成模式.md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/领域驱动设计实践(完)/021 辨别限界上下文的协作关系(下).md.html">下一页</a>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
</div>
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997e1f4c0b3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
</body>
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
|
||
function gtag() {
|
||
dataLayer.push(arguments);
|
||
}
|
||
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-NPSEEVD756');
|
||
var path = window.location.pathname
|
||
var cookie = getCookie("lastPath");
|
||
console.log(path)
|
||
if (path.replace("/", "") === "") {
|
||
if (cookie.replace("/", "") !== "") {
|
||
console.log(cookie)
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
}
|
||
} else {
|
||
setCookie("lastPath", path)
|
||
}
|
||
|
||
function setCookie(cname, cvalue) {
|
||
var d = new Date();
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
var expires = "expires=" + d.toGMTString();
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
}
|
||
|
||
function getCookie(cname) {
|
||
var name = cname + "=";
|
||
var ca = document.cookie.split(';');
|
||
for (var i = 0; i < ca.length; i++) {
|
||
var c = ca[i].trim();
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
}
|
||
return "";
|
||
}
|
||
|
||
</script>
|
||
|
||
</html>
|