mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
1533 lines
51 KiB
HTML
1533 lines
51 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>025 案例 层次的职责与协作关系(图文篇).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 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 class="current-tab" 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>025 案例 层次的职责与协作关系(图文篇)</h1>
|
||
|
||
<p>经历多次演进,我们已经初步得到了符合领域驱动设计思想的分层架构,但这种架构仅仅是一种静态的逻辑划分,在实现一个业务用例时,各层之间是怎么协作的,我们似乎还不得而知。辨别这种动态的协作关系,还是应该从职责的角度入手,即必须清楚地理解分层架构中每个逻辑层的职责。</p>
|
||
|
||
<p>一味的理论讲解可能已经让爱好案例与代码的程序员昏昏欲睡了,何况用纯理论知识来讲解职责与协作,也让我力有未逮。不如通过一个具体案例,来说明层次的职责以及层次之间的协作关系。还是以电商系统的下订单场景为例,在买家提交订单时,除了与订单直接有关的业务之外,还需要执行以下操作。</p>
|
||
|
||
<ul>
|
||
|
||
<li>订单数据的持久化:OrderRepository 提供插入订单功能。它属于支撑订单提交业务的基础功能,但将订单持久化到数据库的实现 OrderMapper 并不属于该业务范畴。</li>
|
||
|
||
<li>发送通知邮件:NotificationService 提供通知服务。它属于支撑通知业务的基础功能,但邮件发送的实现 EmailSender 却不属于该业务范畴。</li>
|
||
|
||
<li>异步发送消息给仓储系统:提交订单成功后需要异步发送消息 OrderConfirmed 给仓储系统,这一通信模式是通过消息队列来完成的。EventBus 发送 OrderConfirmed 事件属于支撑订单提交成功的基础功能,但发送该事件到 RabbitMQ 消息队列的 RabbitEventBus 则不属于该业务范畴。</li>
|
||
|
||
</ul>
|
||
|
||
<p>同时,为了用户界面客户端或第三方服务的分布式调用,需要通过 OrderController 暴露 RESTful 服务。它本身不提供任何业务实现,而是通过将请求委派给应用层的 OrderAppService 来完成订单的提交。</p>
|
||
|
||
<p>下图体现了前述三个流程在各层之间以及系统内外部之间的协作关系。<strong>注意,在这里我将牵涉到的类型放在了同一个限界上下文中,如果牵涉到多个限界上下文之间的协作,实现会略有不同,对应的代码模型也将有所调整</strong>。我会在后续内容中深入探讨限界上下文之间的协作对代码模型的影响。</p>
|
||
|
||
<p><img src="assets/42e90700-c0bd-11e8-afc9-4db08e1ecfa6" alt="enter image description here" /></p>
|
||
|
||
<p>基础设施层的 OrderController 扮演了北向网关的角色,承担了与用户界面层或第三方服务交互的进出口职责。它通过 Spring Boot 来完成对 HTTP 请求的响应、路由和请求/响应消息的序列化与反序列化。它的自有职责仅仅是对请求/响应消息的验证,以及对 OrderAppService 的调用。或许有人会质疑处于后端顶层的控制器为何属于基础设施层?但我认为这样的分配是合理的,因为 Controller 要做的事情与基础设施层所要履行的职责完全匹配,即它提供的是 REST 服务的基础功能。</p>
|
||
|
||
<p>基础设施层南向网关包括 OrderMapper、EmailSender 和 RabbitEventBus,它们对内为具体的某个业务提供支撑功能,对外则需要借助框架或驱动器访问外部资源。与北向网关不同,对它们的调用由属于内层的应用服务 OrderAppService 发起,因此需要为它们建立抽象来解除内层对外层的依赖。前面已经分析,由于 Repository 提供的方法分属领域逻辑,故而将 OrderMapper 所要实现的接口 OrderRepository 放到核心的领域层。至于 EmailSender 与 RabbitEventBus 各自的抽象 NotificationService 与 EventBus 并未代表领域逻辑,为了不污染领域层的纯洁性,放在应用层似乎更为合理。</p>
|
||
|
||
<p>无论是北向网关还是南向网关,它们都要与外部资源进行协作,不管是对内/外协议的适配,还是对内部协作对象的封装,本质上它们只做与业务直接有关的基础功能。真正与业务无关的通用基础功能,是与具体某个软件系统无关的,属于更加基础和通用的框架。例如,OrderController 调用的 Spring Boot APIs,EmailSender 调用的 JavaMail APIs、OrderMapper 调用的 MyBatis APIs 以及 RabbitEventBus 调用的 RabbitMQ APIs,都是这样的通用框架。它们是系统代码边界外的外部框架,通常为第三方的开源框架或商业产品;即使是团队自行研发,也不应该属于当前业务系统的代码模型。</p>
|
||
|
||
<p>我们可以基于这个案例归纳各个层次的职责。</p>
|
||
|
||
<ul>
|
||
|
||
<li>领域层:包含 PlaceOrderService、Order、Notification、OrderConfirmed 与抽象的 OrderRepository,封装了纯粹的业务逻辑,不掺杂任何与业务无关的技术实现。</li>
|
||
|
||
<li>应用层:包含 OrderAppService 以及抽象的 EventBus 与 NotificationService,提供对外体现业务价值的统一接口,同时还包含了基础设施功能的抽象接口。</li>
|
||
|
||
<li>基础设施层:包含 OrderMapper、RabbitEventBus 与 EmailSender,为业务实现提供对应的技术功能支撑,但真正的基础设施访问则委派给系统边界之外的外部框架或驱动器。</li>
|
||
|
||
</ul>
|
||
|
||
<p><strong>注意</strong>:这里定义了两个分属不同层次的服务,二者极容易混淆。PlaceOrderService 是领域服务,定义在领域层中;OrderAppService 是应用服务,定义在应用层中。这二者的区别属于战术设计的层面,我会在之后的战术设计讲解中深入阐释,我的博客《<a href="http://zhangyi.xyz/how-to-identify-application-service/">如何分辨应用服务与领域服务</a>》也有比较详细的介绍。</p>
|
||
|
||
<p>OrderController 的实现代码如下所示:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.infrastucture;
|
||
import org.springframework.web.bind.annotation.RestController;
|
||
|
||
import org.springframework.web.bind.annotation.RequestMapping;
|
||
|
||
import org.springframework.web.bind.annotation.RequestMethod;
|
||
|
||
import org.springframework.web.bind.annotation.RequestParam;
|
||
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import practiceddd.ecommerce.ordercontext.infrastructure.message.CreateOrderRequest;
|
||
|
||
import practiceddd.ecommerce.ordercontext.application.OrderAppService;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.Order;
|
||
@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);
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>应用服务 OrderAppService 的代码如下所示:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.application;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
|
||
import org.springframework.stereotype.Service;
|
||
import practiceddd.ecommerce.ordercontext.domain.PlaceOrderService;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.Order;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.OrderCompleted;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.Notification;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.OrderNotification;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.exceptions.InvalidOrderException;
|
||
@Serivce
|
||
|
||
public class OrderAppService {
|
||
|
||
@Autowired
|
||
|
||
private NotificationService notificationService;
|
||
|
||
@Autowired
|
||
|
||
private EventBus eventBus;
|
||
|
||
@Autowired
|
||
|
||
private PlaceOrderService placeOrderService;
|
||
public void placeOrder(Order order) {
|
||
|
||
try {
|
||
|
||
placeOrderService.execute(order);
|
||
|
||
notificatonService.send(composeNotification(order));
|
||
|
||
eventBus.publish(composeEvent(order));
|
||
|
||
} catch (InvalidOrderException | Exception ex) {
|
||
|
||
throw new ApplicationException(ex.getMessage());
|
||
|
||
}
|
||
|
||
}
|
||
private Notification composeNotification(Order order) {
|
||
|
||
// 组装通知邮件的内容,实现略
|
||
|
||
}
|
||
|
||
private OrderConfirmed composeEvent(Order order) {
|
||
|
||
// 组装订单确认事件的内容,实现略
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>既然 OrderAppService 属于应用层的应用服务,它就不应该包含具体的业务逻辑。倘若我们将发送邮件和异步消息发送视为“横切关注点”,那么在应用服务中调用它们是合乎情理的;然而,通过 Order 组装 Notification 与 OrderConfirmed 的职责,却应该放在领域层,因为基于订单去生成邮件内容以及发布事件包含了业务逻辑与规则。<strong>问题出现!<strong>由于这两个对象是由领域层生成的对象,我们该</strong>如何将领域层生成的对象传递给处于它之上的应用层对象?</strong></p>
|
||
|
||
<p>有三种解决方案可供选择。</p>
|
||
|
||
<p>第一种方案是将组装通知邮件与订单确认事件的职责封装到领域层的相关类中,然后在应用层调用这些类的方法,如此可以减少应用层的领域逻辑:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.domain;
|
||
|
||
import org.springframework.stereotype.Service;
|
||
@Service
|
||
|
||
public class NotificationComposer {
|
||
|
||
public Notification compose(Order order) {
|
||
|
||
// 实现略
|
||
|
||
}
|
||
|
||
}
|
||
package practiceddd.ecommerce.ordercontext.domain;
|
||
|
||
import org.springframework.stereotype.Service;
|
||
@Service
|
||
|
||
public class OrderConfirmedComposer {
|
||
|
||
public OrderConfirmed compose(Order order) {
|
||
|
||
// 实现略
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>则应用服务就可以简化为:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.application;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
|
||
import org.springframework.stereotype.Service;
|
||
import practiceddd.ecommerce.ordercontext.domain.PlaceOrderService;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.Order;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.OrderConfirmed;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.Notification;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.exceptions.InvalidOrderException;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.NotificationComposer;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.OrderConfirmedComposer;
|
||
@Service
|
||
|
||
public class OrderAppService {
|
||
|
||
@Autowired
|
||
|
||
private NotificationService notificationService;
|
||
|
||
@Autowired
|
||
|
||
private EventBus eventBus;
|
||
|
||
@Autowired
|
||
|
||
private PlaceOrderService placeOrderService;
|
||
|
||
@Autowired
|
||
|
||
private NotificationComposer notificationComposer;
|
||
|
||
@Autowired
|
||
|
||
private OrderConfirmedComposer orderConfirmedComposer;
|
||
public void placeOrder(Order order) {
|
||
|
||
try {
|
||
|
||
placeOrderService.execute(order);
|
||
|
||
notificatonService.send(notificationComposer.compose(order));
|
||
|
||
eventBus.publish(orderConfirmedComposer.compose(order));
|
||
|
||
} catch (InvalidOrderException | Exception ex) {
|
||
|
||
throw new ApplicationException(ex.getMessage());
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>采用这种方案的代码结构如下所示:</p>
|
||
|
||
<pre><code>ordercontext.infrastructure
|
||
|
||
- OrderController
|
||
|
||
- OrderMapper
|
||
|
||
- EmailSender
|
||
|
||
- RabbitEventBus
|
||
|
||
ordercontext.application
|
||
|
||
- OrderAppService
|
||
|
||
- NotificationService
|
||
|
||
- EventBus
|
||
|
||
ordercontext.domain
|
||
|
||
- OrderRepository
|
||
|
||
- PlaceOrderService
|
||
|
||
- Order
|
||
|
||
- Notification
|
||
|
||
- OrderConfirmed
|
||
|
||
- NotificationComposer
|
||
|
||
- OrderConfirmedComposer
|
||
</code></pre>
|
||
|
||
<p>第二种方案则将“上层对下层的调用”改为“下层对上层的通知”,即前面讲解层之间协作时所谓“自底向上”的通信问题,这就需要在领域层为订单业务定义 OrderEventPublisher 接口。当满足某个条件时,通过它在领域层发布事件,这个事件即所谓“领域事件(Domain Event)”。如果我们将建模的视角切换到以“事件”为中心,则意味着领域服务在下订单完成后,需要分别发布 NotificationComposed 与 OrderConfirmed 事件,并由应用层的 OrderEventHandler 作为各自事件的订阅者。这里的前提是:发送邮件与异步发送通知属于应用逻辑的一部分。</p>
|
||
|
||
<p>我们需要先在领域层定义发布者接口:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.domain;
|
||
public interface OrderEventPublisher {
|
||
|
||
void publish(NotificationComposed event);
|
||
|
||
void publish(OrderConfirmed event);
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>实现 OrderEventPublisher 接口的类放在应用层:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.application;
|
||
import practiceddd.ecommerce.ordercontext.domain.OrderEventPublisher;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.NotificationComposed;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.Notification;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.OrderConfirmed;
|
||
public class OrderEventHandler implements OrderEventPublisher {
|
||
|
||
private NotificationService notificationService;
|
||
|
||
private EventBus eventBus;
|
||
public OrderEventHandler(NotificationService notificationService, EventBus eventBus) {
|
||
|
||
this.notificationService = notificationService;
|
||
|
||
this.eventBus = eventBus;
|
||
|
||
}
|
||
public void publish(NotificationComposed event) {
|
||
|
||
notificationService.send(event.notification());
|
||
|
||
}
|
||
public void publish(OrderConfirmed event) {
|
||
|
||
eventBus.publish(event);
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>应用层的应用服务则修改为:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.application;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
|
||
import org.springframework.stereotype.Service;
|
||
import practiceddd.ecommerce.ordercontext.domain.PlaceOrderService;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.Order;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.exceptions.InvalidOrderException;
|
||
@Service
|
||
|
||
public class OrderAppService {
|
||
|
||
@Autowired
|
||
|
||
private PlaceOrderService placeOrderService;
|
||
|
||
@Autowired
|
||
|
||
private NotificationService notificationService;
|
||
|
||
@Autowired
|
||
|
||
private EventBus eventBus;
|
||
public void placeOrder(Order order) {
|
||
|
||
try {
|
||
|
||
placeOrderService.register(new OrderEventHandler(notificationService, eventBus));
|
||
|
||
placeOrderService.execute(order);
|
||
|
||
} catch (InvalidOrderException ex) {
|
||
|
||
throw new ApplicationException(ex.getMessage());
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>领域服务修改为:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.domain;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
|
||
import org.springframework.stereotype.Service;
|
||
import practiceddd.ecommerce.ordercontext.domain.exceptions.InvalidOrderException;
|
||
@Service
|
||
|
||
public class PlaceOrderService {
|
||
|
||
@Autowired
|
||
|
||
private OrderRepository orderRepository;
|
||
|
||
@Autowired
|
||
|
||
private NotificationComposer notificationComposer;
|
||
private OrderEventPublisher publisher;
|
||
public void register(OrderEventPublisher publisher) {
|
||
|
||
this.publisher = publisher;
|
||
|
||
}
|
||
public void execute(Order order) {
|
||
|
||
if (!order.isValid()) {
|
||
|
||
throw new InvalidOrderException(String.format("The order with id %s is invalid.", order.id()));
|
||
|
||
}
|
||
|
||
orderRepository.save(order);
|
||
|
||
fireNotificationComposedEvent(order);
|
||
|
||
fireOrderConfirmedEvent(order);
|
||
|
||
}
|
||
private void fireNotificationComposedEvent(Order order) {
|
||
|
||
Notification notification = notificationComposer.compose(order);
|
||
|
||
publisher.publish(new NotificationComposed(notification));
|
||
|
||
}
|
||
|
||
private void fireOrderConfirmedEvent(Order order) {
|
||
|
||
publisher.publish(new OrderConfirmed(order));
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>倘若采用这种方案,则代码结构如下所示:</p>
|
||
|
||
<pre><code>ordercontext.infrastructure
|
||
|
||
- OrderController
|
||
|
||
- OrderMapper
|
||
|
||
- EmailSender
|
||
|
||
- RabbitEventBus
|
||
|
||
ordercontext.application
|
||
|
||
- OrderAppService
|
||
|
||
- NotificationService
|
||
|
||
- EventBus
|
||
|
||
- OrderEventHandler
|
||
|
||
ordercontext.domain
|
||
|
||
- OrderRepository
|
||
|
||
- PlaceOrderService
|
||
|
||
- NotificationComposer
|
||
|
||
- OrderEventPublisher
|
||
|
||
- Order
|
||
|
||
- OrderConfirmed
|
||
|
||
- NotificationComposed
|
||
</code></pre>
|
||
|
||
<p>第三种方案需要重新分配 NotificationService 与 EventBus,将这两个抽象接口放到单独的一个名为 interfaces 的包中,这个 interfaces 包既不属于应用层,又不属于领域层。在后面讲解代码模型时,我会解释这样设计的原因,详细内容请移步阅读后面的章节。</p>
|
||
|
||
<p>通过这样的职责分配后,业务逻辑发生了转移,发送邮件与异步发送通知的调用不再放到应用服务 OrderAppService 中,而是封装到了 PlaceOrderService 领域服务。这时,应用服务 OrderAppService 的实现也变得更加简单。看起来,修改后的设计似乎更符合领域驱动分层架构对应用层的定义,即“应用层是很薄的一层,不包含业务逻辑”。这里的前提是:<strong>发送邮件与异步发送通知属于业务逻辑的一部分。</strong></p>
|
||
|
||
<p>应用服务的定义如下所示:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.application;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
|
||
import org.springframework.stereotype.Service;
|
||
import practiceddd.ecommerce.ordercontext.domain.PlaceOrderService;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.Order;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.exceptions.InvalidOrderException;
|
||
@Service
|
||
|
||
public class OrderAppService {
|
||
|
||
@Autowired
|
||
|
||
private PlaceOrderService placeOrderService;
|
||
public void placeOrder(Order order) {
|
||
|
||
try {
|
||
|
||
placeOrderService.execute(order);
|
||
|
||
} catch (InvalidOrderException | Exception ex) {
|
||
|
||
throw new ApplicationException(ex.getMessage());
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>不过,领域服务就变得不太纯粹了:</p>
|
||
|
||
<pre><code class="language-java">package practiceddd.ecommerce.ordercontext.domain;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
|
||
import org.springframework.stereotype.Service;
|
||
import practiceddd.ecommerce.ordercontext.interfaces.NotificationService;
|
||
|
||
import practiceddd.ecommerce.ordercontext.interfaces.EventBus;
|
||
|
||
import practiceddd.ecommerce.ordercontext.domain.exceptions.InvalidOrderException;
|
||
@Service
|
||
|
||
public class PlaceOrderService {
|
||
|
||
@Autowired
|
||
|
||
private NotificationService notificationService;
|
||
|
||
@Autowired
|
||
|
||
private EventBus eventBus;
|
||
|
||
@Autowired
|
||
|
||
private OrderRepository orderRepository;
|
||
|
||
@Autowired
|
||
|
||
private NotificationComposer notificationComposer;
|
||
public void execute(Order order) {
|
||
|
||
if (!order.isValid()) {
|
||
|
||
throw new InvalidOrderException(String.format("The order with id %s is invalid.", order.id()));
|
||
|
||
}
|
||
|
||
orderRepository.save(order);
|
||
|
||
notificatonService.send(notificationComposer.compose(order));
|
||
|
||
eventBus.publish(new OrderConfirmed(order));
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>代码结构如下所示:</p>
|
||
|
||
<pre><code>ordercontext.infrastructure
|
||
|
||
- OrderController
|
||
|
||
- OrderMapper
|
||
|
||
- EmailSender
|
||
|
||
- RabbitEventBus
|
||
|
||
ordercontext.application
|
||
|
||
- OrderAppService
|
||
|
||
ordercontext.interfaces
|
||
|
||
- NotificationService
|
||
|
||
- EventBus
|
||
|
||
ordercontext.domain
|
||
|
||
- OrderRepository
|
||
|
||
- PlaceOrderService
|
||
|
||
- Order
|
||
|
||
- OrderConfirmed
|
||
|
||
- Notification
|
||
|
||
- NotificationComposer
|
||
</code></pre>
|
||
|
||
<p>这三个方案该如何选择?根本的出发点在于你对业务逻辑和应用逻辑的认知,进而是你对领域服务与应用服务的认知,这些内容,就留待战术设计部分来讨论。由于并不存在绝对完美的正确答案,因此我的建议是在满足功能需求与松散耦合的前提下,请尽量选择<strong>更简单</strong>的方案。</p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/024 领域驱动架构的演进.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/026 限界上下文与架构.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":"70997e2b6e4a3cfa","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>
|
||
|