mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
1160 lines
51 KiB
HTML
1160 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>074 领域服务.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 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 class="current-tab" 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>074 领域服务</h1>
|
||
|
||
<h3>聚合的三个问题</h3>
|
||
|
||
<p>按照面向对象设计原则,需要将“<strong>数据与行为封装在一起</strong>”,避免将领域模型对象设计为贫血对象。如此一来,聚合内的实体与值对象承担了与其数据相关的领域行为逻辑。聚合满足了领域概念的完整性、独立性与不变量,体现了聚合才是对象粒度之上自治单元的最佳选择。一个设计良好的聚合同样需要做到最小完备、自我履行、稳定空间和独立进化。</p>
|
||
|
||
<p>在领域驱动设计中,自治对象参与了聚合内部的协作,而聚合作为多个实体与值对象的整体,则是参与业务场景的自治单元。倘若将聚合拥有的数据称为“已知数据”,若一个业务场景的领域行为只需操作这些已知数据,就应定义为聚合内相关类的领域方法,倘若该领域行为还需要和别的聚合协作,该方法就被分配给聚合根实体。有时候,聚合的已知数据并不足以支持整个业务场景的领域需求,为了保证聚合的自治性,会将不足的部分通过聚合方法的参数传入。通过参数传入的外部数据皆可视为聚合的“未知数据”。</p>
|
||
|
||
<p><strong>为了彰显聚合边界的防御作用,聚合并不会直接与别的聚合协作,那么这些未知数据从何而来?</strong></p>
|
||
|
||
<p>聚合封装了多个实体和值对象,聚合根是访问聚合的唯一入口。若要遵循迪米特法则,聚合调用者应该只需知道暴露在外的聚合根实体,当业务需求需要调用聚合内实体或值对象的方法时,聚合当隐去其细节,由根实体包装这些方法,在方法内部的实现中,将外部的请求委派给内部的相应类。封装的领域行为被固化在聚合之中,成为了聚合的附庸。这是将对象作为一等公民的面向对象设计思想的体现。</p>
|
||
|
||
<p><strong>在业务系统中,总会有一些领域行为游离在聚合之外,它们要么不需要聚合自身携带的已知数据,要么存在与聚合迥然不同的变化方向,这些领域行为应该附庸在哪个对象之上呢?</strong></p>
|
||
|
||
<p>作为自治单元的聚合是领域层的主力军,在企业软件系统中,它封装了系统最为核心的业务功能,在整洁架构思想中,又被视为系统最为稳定的领域模型。从分离变与不变的设计思想来看,聚合必然不能与访问外部资源的技术实现混合在一处;即使领域模型因业务需求而变,但它与外部资源的变化方向定然不同,故而亦当完全分离。无论外部资源是数据库、消息队列还是网络通信,都可以通过抽象的南向网关(包括资源库),作为领域逻辑与技术实现的分水岭。</p>
|
||
|
||
<p><strong>不管聚合做到了多大程度的自治,总需要与抽象的南向网关协作,如此才能实现完整的业务场景,这样的协作行为如果不能分配给聚合,又该分配给什么对象呢?</strong></p>
|
||
|
||
<p>以上三个问题汇成一个答案,曰:<strong>领域服务</strong>。</p>
|
||
|
||
<h3>什么是领域服务</h3>
|
||
|
||
<p>“服务”这个词语在软件领域中实在过于泛滥,而它的宽泛性使得我们甚至无法给出一个准确的定义。服务的特征却显而易见:</p>
|
||
|
||
<ul>
|
||
|
||
<li>首先,服务并不是某一个具体的事物(thing)</li>
|
||
|
||
<li>其次,服务体现的是一种行为(behavior)</li>
|
||
|
||
</ul>
|
||
|
||
<p>故而,领域服务(Domain Service)代表了在名词世界(面向对象)中对动词的封装(接口),它封装了领域行为。前提在于,这一领域行为在实体或值对象中找不到容身之处。换言之,当我们针对领域行为建模时,需要优先考虑使用值对象和实体来封装领域行为,只有确定无法寻觅到合适的对象来承担时,才将该行为建模为领域服务的方法。</p>
|
||
|
||
<p>虽然领域服务是领域设计建模的最末选择,但“服务”这个词语实在太过宽泛了,在表达业务逻辑时,只要服务的修饰语合适,就有充足的理由将相关的领域逻辑分配给它。如此就会导致领域服务的泛滥,成为一个无所不包的“上帝”服务。长此以往,所有的业务逻辑都会放到这个服务中,使得领域层的设计重新走回“贫血模型”的老路。</p>
|
||
|
||
<p>如果阅读供应链的开源项目<a href="https://ofbiz.apache.org/">OFBiz</a>,这种贫血模型加上帝服务的形式就处处可见。例如,该项目定义了 ShipmentService 服务,该服务类包含了一千多行代码,服务定义的公开方法(坦白说,这些方法的命名很好地体现了领域特征)包括:</p>
|
||
|
||
<ul>
|
||
|
||
<li>createShipmentEstimate()</li>
|
||
|
||
<li>removeShipmentEstimate()</li>
|
||
|
||
<li>calcShipmentCostEstimate()</li>
|
||
|
||
<li>fillShipmentStagingTables()</li>
|
||
|
||
<li>updateShipmentsFromStaging()</li>
|
||
|
||
<li>clearShipmentStagingInfo()</li>
|
||
|
||
<li>updatePurchaseShipmentFromReceipt()</li>
|
||
|
||
<li>duplicateShipmentRouteSegment()</li>
|
||
|
||
<li>quickScheduleShipmentRouteSegment()</li>
|
||
|
||
<li>getShipmentPackageValueFromOrders()</li>
|
||
|
||
<li>sendShipmentCompleteNotification()</li>
|
||
|
||
<li>getShipmentGatewayConfigFromShipment()</li>
|
||
|
||
</ul>
|
||
|
||
<p>这就是典型的“上帝”服务,庞大和臃肿,严重违背了单一职责原则。表面是服务对象,其实每个服务方法都是一个事务脚本,缺乏内聚职责的封装,也缺乏对领域模型概念的呈现,成为一种彻头彻尾的过程式实现。若要从领域建模的角度分析,仅需从该服务诸多方法的命名,也可察觉领域概念的端倪。例如:</p>
|
||
|
||
<ul>
|
||
|
||
<li>与运输费用估算有关:ShipmentCostEstimate</li>
|
||
|
||
<li>与分段运输有关:ShipmentStaging</li>
|
||
|
||
<li>与运输路径有关:ShipmentRouteSegment</li>
|
||
|
||
<li>与运输包有关:ShipmentPackage</li>
|
||
|
||
<li>与运输收据有关:ShipmentReceipt</li>
|
||
|
||
</ul>
|
||
|
||
<p>通过这些领域行为甄别出来的领域概念,完全可以定义为相关的实体或值对象,由其承担一部分与其数据有关的领域行为。为何会出现这样的实现呢?我想问题还是在于服务概念的过宽过泛。定义的服务 ShipmentService,其言外之意,凡是与运输(Shipment)有关的业务,或多或少都会与该服务扯上关系。软件的设计人员与开发人员往往存在一种惰性,不愿意锱铢必较探究职责分配的合理性,一旦认为该业务与运输有关,就自然而然考虑分配给 ShipmentService,然后再借助过程式思维模式,按照结构顺序编写代码,就形成了我们看到的事务脚本。</p>
|
||
|
||
<p>随着业务的逐渐增加,这种看似理所当然的职责分配就会让整个服务陷入庞大臃肿的泥沼之中。显然,如果在设计与开发时对职责的分配不加约束,所谓的“职责分治”不过是一句空话罢了。为了避免这种现象,在对领域进行建模时,考虑设计要素的顺序应该为:</p>
|
||
|
||
<pre><code>值对象(Value Object)→ 实体(Entity)→ 领域服务(Domain Service)
|
||
</code></pre>
|
||
|
||
<p>为了避免开发人员把领域服务当做一个“筐”,什么逻辑都往里面装,除了需要提高团队成员面向对象的设计能力,强调领域建模的设计顺序之外,还有一个方法,就是对领域服务加以约束。可惜的是,没有任何语言可以施加领域驱动设计要素的约束。Mat Wall 与 Nik Silver 在 Guardian.co.uk 网站推进领域驱动设计时的实践值得我们借鉴。他们在文章《<a href="http://www.infoq.com/cn/articles/ddd-evolving-architecture">演进架构中的领域驱动设计</a>》中建议:</p>
|
||
|
||
<blockquote>
|
||
|
||
<p>为了对付这一行为,我们对应用中的所有服务进行了代码评审,并进行重构,将逻辑移到适当的领域对象中。我们还制定了一个新的规则:**任何服务对象在其名称中必须包含一个动词。**这一简单的规则阻止了开发人员去创建类似于 ArticleService 的类。取而代之,我们创建 ArticlePublishingService 和 ArticleDeletionService 这样的类。推动这一简单的命名规范的确帮助我们将领域逻辑移到了正确的地方,但我们仍要求对服务进行定期的代码评审,以确保我们在正轨上,以及对领域的建模接近于实际的业务观点。</p>
|
||
|
||
</blockquote>
|
||
|
||
<p>通过限制服务命名来规范领域模型的设计,看似荒唐,其实真如天外飞仙,颇有创见,因为它实则体现了领域服务的行为本质。这个行为是无状态的,相当于一个纯函数。发布文章是一个领域行为,对应于 publishArticle() 函数;删除文章是一个领域行为,对应于 deleteArticle() 函数。只是在 Java 中,无法直接定义这样的函数,不得已才定义类或接口作为函数“附身”的类型罢了。</p>
|
||
|
||
<p>命名约束的实践可能会导致太多细粒度的领域服务产生,但在领域层,这样的细粒度设计值得提倡,它能促进类的单一职责,保证类的重用和应对变化的能力。由于每个服务的粒度非常细,就不可能产生包罗万象的“上帝”服务。服务的定义是有设计成本的。在创建一个新的领域服务时,命名约束为让我们暂时停下来,想一想,要分配给这个新服务的领域逻辑是否有更好的去处?</p>
|
||
|
||
<h3>领域服务的应用场景</h3>
|
||
|
||
<p>领域服务不只限于对领域行为的建模,在领域设计模型中,它与聚合、资源库等设计要素拥有对等的地位。领域服务的应用场景是有设计诉求的,恰好可以呼应前面提及的三个问题。</p>
|
||
|
||
<p>**第一个问题:**为了彰显聚合边界的防御作用,聚合并不会直接与别的聚合协作,那么这些未知数据从何而来?</p>
|
||
|
||
<p>多数时候,一个自治的聚合无法完成一个完整的业务场景,需要共同协作才能完成。然而,聚合的设计原则却要求聚合之间只能通过聚合的身份标识进行协作。这就意味着在聚合之上,需要引入一个设计对象来封装这种聚合之间的协作行为。这就是领域服务承担的职责:</p>
|
||
|
||
<p><img src="assets/494e1350-dd4b-11e9-aaec-b5744b419935" alt="49820588.png" /></p>
|
||
|
||
<p>然则领域服务又是从何处获得聚合对象的呢?一个可能是领域服务的调用者传递给它。领域服务的调用者可以是另外一个领域服务,但在多数情况下应为应用服务。应用服务的调用者又为远程服务,最终为发起服务请求的前端或第三方服务。我在《领域驱动分层架构与对象模型》一节中将客户端发送的请求消息分为查询消息与命令消息。对于聚合而言,客户端的请求消息最终会到达领域服务,据不同的操作类型转换为不同的参数,与管理聚合生命周期的对象进行协作:</p>
|
||
|
||
<ul>
|
||
|
||
<li>查询操作:请求消息为查询条件,由资源库根据查询条件获得聚合对象</li>
|
||
|
||
<li>创建操作:命令消息作为输入参数,由工厂负责创建聚合对象,然后由资源库执行新增操作</li>
|
||
|
||
<li>更新操作:命令消息中必含有聚合的身份标识,由资源库根据身份标识获得聚合对象,再根据命令消息的新值由聚合对象在内存中更新其状态,最终由资源库执行更新操作</li>
|
||
|
||
<li>删除操作:命令消息包含查询条件,由资源库根据查询条件执行删除操作</li>
|
||
|
||
</ul>
|
||
|
||
<p>不管是什么操作,与领域服务协作的聚合对象都不是与生俱来的,也不是通过外部调用者传递而来,而是通过工厂或资源库创建或获取而来。因此,领域服务在协调多个聚合之间的协作时,还需要与工厂或资源库协作。</p>
|
||
|
||
<p>例如,针对“验证订单有效性”这一验证行为,需要验证订单自身属性的完备性,包括验证订单是否提供了配送地址、联系人信息,还要确保订单聚合的不变量,如保证订单包含了有效的订单项。这些信息皆属于 Order 聚合边界内各个类的属性,基于聚合的自治原则,将由 Order 聚合自身来承担验证功能。在验证订单有效性时,还需要验证下订单的顾客是否为有效顾客。顾客是另一个聚合 Customer 的根实体,这就牵涉到两个聚合之间的协作。故而需要引入领域服务 ValidatingOrderService。该领域服务封装了“验证订单”这一领域行为,需要传入被验证的 Order 聚合对象。由于聚合之间的协作只能通过身份标识进行,Order 聚合没有引用 Customer 聚合,而是持有顾客的身份标识 CustomerId。要获得 Customer 聚合,就需要通过该聚合的资源库:</p>
|
||
|
||
<pre><code class="language-java">public class ValidatingOrderService {
|
||
|
||
private CustomerRepository customerRepo;
|
||
public boolean isValid(Order order) {
|
||
|
||
try {
|
||
|
||
order.validate();
|
||
Optional<Customer> optCustomer = customerRepo.customerOf(order.getCustomerId());
|
||
|
||
return optCustomer.isPresent();
|
||
|
||
} catch (InvalidOrderExceptiion ex) {
|
||
|
||
log.info(ex.getMessage());
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p><strong>第二个问题</strong>:在业务系统中,总会有一些领域行为游离在聚合之外,它们要么不需要聚合自身携带的已知数据,要么存在与聚合迥然不同的变化方向,这些领域行为应该附庸在哪个对象之上呢?</p>
|
||
|
||
<p>设计时,我们首先需要遵循“数据与行为封装在一起”的设计原则。有时候,行为的变化方向却与拥有数据的类并非一致,这时就应分离变与不变,将这一变化的领域行为从它所属的聚合中剥离出来,形成领域服务。由于领域行为存在变化,为了满足扩展要求,还应在领域服务基础上建立抽象。许多行为型设计模式,如策略模式(Strategy Pattern)、命令模式(Command Pattern)、访问者模式(Visitor Pattern)都采用了分离并抽象行为的设计。如果这些行为与领域逻辑有关,则抽象的策略接口、命令接口、访问者接口都可以视为领域服务。</p>
|
||
|
||
<p>例如,保险系统常常需要客户填写一系列问卷调查,以了解客户的具体情况,从而确定符合客户需求的保单策略。调查问卷 Questionaire 是一个聚合根实体,其内部是由多个处于不同层级的值对象组成的树形结构:</p>
|
||
|
||
<pre><code>Section ->
|
||
|
||
SubSection ->
|
||
|
||
QuestionGroup->
|
||
|
||
Question->
|
||
|
||
PrimitiveQuestionField
|
||
</code></pre>
|
||
|
||
<p>业务需求要求将一个完整的调查问卷导出为多种形式的文件,这就需要提供转换行为,将一个聚合的值转换为多种不同格式的内容,例如 CSV 格式、JSON 格式与 XML 格式。转换行为操作的数据为 Questionaire 聚合所拥有,若按照数据与行为应封装在一起的原则,该行为代表的职责就应该由聚合自身来履行。然而,这个转换行为却存在多种变化,不同的内容格式代表了不同的实现。正确的做法就是将转换行为从 Questionaire 聚合中分开,并建立一个抽象的接口 QuestionaireTransformer:</p>
|
||
|
||
<p><img src="assets/81332850-dd4b-11e9-aaec-b5744b419935" alt="76979171.png" /></p>
|
||
|
||
<p><strong>第三个问题</strong>:不管聚合做到了多大程度的自治,总需要与抽象的南向网关协作,如此才能实现完整的业务场景,这样的协作行为如果不能分配给聚合,又该分配给什么对象呢?</p>
|
||
|
||
<p>领域逻辑要做到纯粹地不依赖任何外部资源,在真实的企业业务系统中,几乎不可能。我们只能建立不同粒度的领域模型对象,保证较小粒度的领域模型对象能够做到领域逻辑的纯粹性,在领域驱动设计中,这个粒度就是聚合。一旦领域行为突破了聚合粒度,就很有可能牵涉到与外部资源的协作。在领域层,可以将所有的外部资源都视为一个抽象的网关(Gateway),其中,资源库是一种特殊的针对数据库的网关。</p>
|
||
|
||
<p>领域服务在协调多个聚合的协作时,由于聚合协作关系的限制,必须引入资源库参与协作。这一点在解释第一个问题时,已经提及。不仅限于此,即使领域行为仅仅操作一个聚合,只要它还需要与外部资源交互,那么这一职责就应该交由领域服务来承担。这实际上遵循了领域驱动设计中的一个原则:<strong>应该尽量避免在聚合中使用资源库</strong>。</p>
|
||
|
||
<p>资源库是用来管理聚合的生命周期的,如果在聚合内部使用资源库,就意味着资源库在“重建”聚合根对象时,还需要将该聚合根对象依赖的资源库对象提供给它。遵循整洁架构思想,需要抽象资源库与依赖注入相结合才能避免内部领域层依赖外部资源层。当资源库实现通过 ORM 框架在数据库中获得聚合根对象时,依赖注入框架无法做到将资源库自身设值给聚合根。倘若聚合内部还使用了其他资源库,就更无法满足正常构建聚合对象的需求了。因此,在聚合中使用资源库,颇有几分像是蛋生鸡还是鸡生蛋的循环问题。</p>
|
||
|
||
<p>由于领域服务的生命周期并不需要资源库来管理,因此将调用资源库的职责转移到领域服务,该问题就能迎刃而解了。</p>
|
||
|
||
<p>以物流系统的合同管理功能为例。在创建合同时,需要用户为合同提供一个自编码。在用户输入自编码时,除了要验证该自编码是否满足编码规则之外,还要检测它在已有合同中是否已经存在。根据<strong>信息专家模式</strong>,拥有信息(自编码数据)的对象就是操作该信息的专家,如此一来,验证自编码的行为就应该分配给合同 Contract 聚合内的值对象 ContractNumber。但是,检测自编码是否已经存在,又需要访问外部的数据库,这又是聚合对象自身力有未逮的。故而,自编码的整体验证功能将交由领域服务,在其内部,又进行了职责的分解,形成多个对象角色之间的协作:</p>
|
||
|
||
<pre><code class="language-java">public class CustomizedNumberValidator {
|
||
|
||
private ContractRepository contractRepo;
|
||
public boolean isValid(CustomizedNumber number) {
|
||
|
||
try {
|
||
|
||
number.validate();
|
||
|
||
return !contractRepo.isDuplicatedNumber(number.value());
|
||
|
||
} catch (InvalidCustomizedNumberException ex) {
|
||
|
||
log.info(ex.getMessage());
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
</code></pre>
|
||
|
||
<p>因为要访问外部资源,所以应该将该领域行为分配给领域服务,这可能导致细粒度的领域服务。然而,细粒度的领域服务有利于业务功能的重用,也能够更好地应对需求的变化。例如,创建合同和更新合同都会使用验证自编码合法性的功能,当自编码验证规则发生变化时,通过单独分离出来的 CustomizedNumberValidator 服务也可以更好地控制变化。</p>
|
||
|
||
<p>显然,在进行领域模型设计时,需要正确地甄别不同设计要素在设计模型中扮演的角色。正如《领域模型驱动设计》给出的角色构造型所示:</p>
|
||
|
||
<p><img src="assets/9869d870-dd4b-11e9-9cc8-a572519b0723" alt="43487355.png" /></p>
|
||
|
||
<p>聚合内的实体与值对象负责处理与自身信息相关的领域行为,工厂和资源库负责管理聚合的生命周期,网关负责封装对外部资源的访问,而领域服务则封装了上述对象角色之间的协作,并被定义为类或接口,对外体现了一种领域行为。因此,领域服务应满足如下三个特征的任何一个:</p>
|
||
|
||
<ul>
|
||
|
||
<li>领域行为与状态无关</li>
|
||
|
||
<li>领域行为需要多个聚合参与协作,目的是使用聚合内的实体和值对象编排业务逻辑</li>
|
||
|
||
<li>领域行为需要与访问包括数据库在内的外部资源协作</li>
|
||
|
||
</ul>
|
||
|
||
<p>领域服务并非灵丹妙药,切忌将所有的领域逻辑都往领域服务塞,这也是为何要求领域服务的名称必须包含一个动词的原因。<strong>和谐的协作机制</strong>是好的面向对象设计,当领域服务对外承担了业务场景的领域行为时,要注意将不同的职责分配给不同的对象角色,尤其应遵循“信息专家模式”将数据与行为封装在一起,放到持有数据的聚合内对象中,再以行为的方式进行协作,保证职责分配的合理均衡。</p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/073 领域模型对象的生命周期-资源库.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/领域驱动设计实践(完)/075 案例 领域设计模型的价值.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":"70997ea57a483cfa","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>
|
||
|