mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
913 lines
51 KiB
HTML
913 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>080 领域事件.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 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 class="current-tab" 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>080 领域事件</h1>
|
||
<p>在介绍事件风暴时,我们已经分析了事件的本质,并总结了领域事件的四个特征:</p>
|
||
<ul>
|
||
<li>领域事件是过去发生的与业务有关的事实</li>
|
||
<li>领域事件是管理者和运营者重点关心的内容,若缺少该事件,会对管理与运营产生影响</li>
|
||
<li>领域事件具有时间点的特征,所有事件连接起来会形成明显的时间轴</li>
|
||
<li>领域事件会导致目标对象状态的变化</li>
|
||
</ul>
|
||
<p>作为一种领域分析建模方法,事件风暴将事件视为一种建模的手段,将不同的团队角色统一到一个共同的业务场景下,同时又利用了事件的因果关系驱动我们把握业务的整体流程。在这个过程中,领域事件在事件风暴中起到了核心的驱动作用,但<strong>它本身并不一定属于最终获得的领域分析模型的一部分</strong>。这就与我们选择的建模范式有关。</p>
|
||
<h3>事件建模范式</h3>
|
||
<p>即使采用领域模型驱动设计,选择建模范式的不同,也会直接影响到最后获得的领域模型。我在前面介绍过结构范式、对象范式和函数范式与领域模型之间的关系。特别针对对象范式和函数范式,要明确二者之间的本质区别。对象范式重视领域逻辑中的名词概念,并将领域行为封装到对象中;函数范式重视领域逻辑中的领域行为,将其视为类型的转换操作,因而主张将领域行为定义为无副作用(side-effect free)的纯函数(pure function)。</p>
|
||
<p>倘若领域模型驱动设计的整个过程皆以“领域事件”为核心,建模范式就发生了变化,因为<strong>事件改变了我们观察现实世界的方式</strong>。它的关注点不是领域概念,也并非领域行为,而是因为领域行为引起的领域概念状态的变化。领域事件是在针对<strong>状态</strong>建模,在这样的观察视角下,大多数业务流程都可以视为由命令触发的引起状态迁移的状态机。状态的迁移本质上可以认为是形如 <code>State1 => State2</code> 这样的纯函数,故而这种建模方式更贴近于函数范式,或者说是函数范式的一个分支。</p>
|
||
<p>不过,相比函数范式,它也有自己的独到之处。设计的驱动力是事件,建模的核心是事件,以及事件引起的状态迁移。故而我将这一范式称之为“<strong>事件建模范式</strong>”。该范式通过领域事件去观察现实世界,并围绕着“事件”为中心去表达领域对象的状态迁移,进而以事件来驱动领域场景。事件建模范式影响的是建模者观察现实世界的态度,而这种以领域事件为模型核心元素的范式又会影响到整个软件的体系架构、模型设计与代码实现。</p>
|
||
<h3>领域事件扮演的角色</h3>
|
||
<p>当我们将领域事件引入到领域模型中时,我们必须明确它在模型中到底扮演了什么样的角色,同时,还要认识到它的引入会受到建模范式的影响。如果不明确这两点,就贸然在领域驱动设计中引入领域事件,不免会有过分夸大之嫌,好像变得“无事件就无领域驱动设计”似的。</p>
|
||
<p>领域事件在对象范式和事件范式中起到的作用完全不同。我们需要区分不同范式,看一看领域事件在领域模型驱动设计的分析、设计与实现这三个阶段中到底扮演了什么样的角色。</p>
|
||
<h4>事件在对象范式</h4>
|
||
<p>遵循对象范式的领域建模方法强调对领域知识的提炼,主要体现为以名词为特征的领域概念与以动词为特征的领域行为。采用这种方式进行分析建模,组成领域分析模型的全部都是一个一个表达领域概念的类型,而领域行为则被定义为领域概念对象的方法。虽然建模结果如此,但不同的领域分析方法却有着不同的世界观,产生了不同的分析驱动力。一些领域分析专家们另辟蹊径,发现了领域事件与众不同的驱动力。领域事件同样是对现实世界的映射,它体现的不是领域概念,而是领域专家对发生了什么事情的一种关心。领域事件由命令触发,是对命令产生的结果事实建立的模型,代表了一系列状态的迁移和变化。</p>
|
||
<p>与名词动词法和 ICONIX 方法不同,四色建模法与事件风暴认识到了领域事件的重要性。四色建模法强调记录领域事件,从而通过对这些数据的追溯支撑企业的运营与管理。根据领域事件可以帮助我们获得时标型对象,进而获得 PPT 对象、角色对象和描述对象,从而构成反映整个业务流程的领域分析模型。事件风暴更是将领域事件放到了最为关键的位置,通过领域事件可以驱动出决策命令、读模型和聚合,从而获得参与每个领域场景的领域分析模型。显然,领域事件在分析建模方法中扮演了越来越重要的驱动作用。</p>
|
||
<p>即使我们可以围绕着领域事件进行分析建模,但在对象范式下,我们驱动出来的领域分析模型,作为对现实世界的一种映射,仍然是一个名词做主的世界。动词作为名词的依附者,缺乏作为主体的独立资格。领域事件所关注的状态则被领域模型对象隐藏起来,使得我们在最终获得的领域分析模型中无法看到领域事件的身影。因此,在对象范式的领域分析建模阶段,领域事件仅仅是一种建模的驱动力。</p>
|
||
<p>那么在对象范式下,是否需要在领域设计模型中引入领域事件呢?由于对象范式将状态以及状态的变更封装到了实体对象中,领域事件的价值被抵消和约束,只剩下如下价值需要我们取舍:</p>
|
||
<ul>
|
||
<li>追溯状态变更</li>
|
||
<li>通知状态变更</li>
|
||
<li>依赖解耦</li>
|
||
</ul>
|
||
<p>基于对象范式定义的实体无法满足追溯状态变更的需求。实体拥有状态,也提供了修改状态的领域行为,但却无法跟踪整个状态变更的过程。唯一能弥补这一缺陷的方法就是在修改状态的领域行为中添加日志,通过日志记录来追溯状态变更。若要领域设计模型直接支持对状态变更的追溯,就需要引入“事件溯源(Event Sourcing)”模式。</p>
|
||
<p>事件溯源通过事件来持久化聚合,存储的每一个事件都代表了一次状态的变更。事件存储在专门的事件存储(Event Store),以便于事件的查询、投影和快照。这个时候,领域事件才是整个领域设计模型的核心,聚合根的实例化、聚合状态的变更以及对状态的追溯都是通过领域事件来完成的。显然,事件溯源改变了建模范式,不属于对象范式的范畴。</p>
|
||
<p>要通知状态变更,其目的就是希望了解状态变更的原因。在收到状态变更的事件时,参与协作的对象需要依据当前实体的状态变更决定作出怎样的响应。这实际上是对象协作的需求。对象范式的领域设计模型可以通过在应用服务或领域服务中进行业务编排(Orchestration),以“中心控制”的方式管理多个聚合之间的协作。例如,订单聚合需要了解支付聚合的状态变更,一旦支付完成,就需要即刻更新订单自身的状态。这个过程可以由订单领域服务作为协调者调用支付服务,例如:</p>
|
||
<pre><code class="language-java">public class PayingOrderService {
|
||
private PaymentService paymentServiceClient;
|
||
private OrderRepository orderRepository;
|
||
|
||
public void execute(Order order) {
|
||
try {
|
||
paymentServiceClient.pay(order.getBilling());
|
||
order.complete();
|
||
orderRepository.save(order);
|
||
} catch (PaymentException | Exception ex) {
|
||
logger.error(ex.getMessage());
|
||
throw new ApplicationException("Failed to pay order", ex);
|
||
}
|
||
}
|
||
}
|
||
|
||
</code></pre>
|
||
<p>这种业务编排的方式使得领域服务成为了领域场景的业务枢纽和逻辑起点,虽然利用分治原则避免了“上帝服务”的存在,但编排逻辑的方式仍然不可避免地导致领域模型对象之间的协作过于紧耦合,控制协作的成本也较高。作为控制中心的领域服务不是自治的,一旦领域逻辑和规则发生变化,就需要修改领域服务中的协作代码,无法独立演化。例如,在支付完成订单状态变更为“已支付”后,还需要发送短信通知买家时,就需要修改 PayingOrderService 领域服务,通过与 NotificationService 服务的协作实现通知逻辑。</p>
|
||
<p>事件的解耦能力毋庸讳言。如果事件属于同一进程内领域设计模型的一部分,则为观察者模式(Observer)的体现。该模式定义了主体(Subject)对象与观察者(Observer)对象。一个主体对象可以注册多个观察者对象,而观察者对象则定义了一个回调函数,一旦主体对象的状态发生变化,就会通过调用回调函数,将变化的状态通知给所有的观察者。主体和观察者都进行了抽象,如此即可降低二者之间的耦合。C# 语言中的 Event 与 Delegate 相当于是观察者模式的语法糖。观察者模式的设计类图如下所示:</p>
|
||
<p><img src="assets/d1eceab0-e0e8-11e9-baf7-f97ccb573723" alt="79599410.png" /></p>
|
||
<p>观察者模式的意图为“定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。”在基于对象范式的领域设计模型中,只要领域逻辑满足该意图,就可以运用观察者模式,与领域事件的建模思想无关。在 Java 语言的编程实现中,往往是由聚合作为主体,即使运用了观察者模式,也可能看不到领域事件的定义。例如,当订单的状态被更新为 Completed 时,若运用了观察者模式,就会在发生状态变更时通知事先注册的观察者对象 OrderCompletedHandler:</p>
|
||
<pre><code class="language-java">public interface OrderCompletedHandler {
|
||
void handle(Order order);
|
||
}
|
||
|
||
public class Order extends AggregateRoot<Order> {
|
||
private OrderStatus status = OrderStatus.New;
|
||
private List<OrderCompletedHandler> handlers;
|
||
|
||
public void complete() {
|
||
this.status = OrderStatus.Completed;
|
||
for (OrderCompletedHandler handler : handlers) {
|
||
handler.handle(this);
|
||
}
|
||
}
|
||
}
|
||
|
||
</code></pre>
|
||
<p>**说明:**C# 语言提供的 Event 语法更清晰地表达出领域事件的概念,例如可以通过 <code>event OrderCompletedHandler OrderCompleted</code> 定义一个事件。但本质上,它还是观察者模式的体现。</p>
|
||
<p>虽然在运用观察者模式时,可以对主体与观察者建立抽象来降低二者之间的耦合,但却无法做到彻底的解耦。如果系统采取单体架构,如此的弱依赖是可容忍的,例如,邮件通知服务就可以实现 OrderCompletedHandler 接口,Order 聚合并不需要知道这个具体的邮件通知服务。若为分布式的微服务架构,并采取前后端分离,观察者模式做得还不够彻底,因为依赖的抽象类型无法满足跨进程通信的要求。</p>
|
||
<p>引入消息中间件来传递事件消息,可以解决这一问题。通常,事件作为主题(Topic)消息被发布到消息中间件,对该事件感兴趣的消费者会订阅该事件,一旦事件被发布,即可获取该事件消息并做出相应处理。这一设计方式在意图上仍然属于观察者模式,不过我们更习惯于将其命名为发布者-订阅者模式。发布者只需要定义和发布事件,却无需理会事件消息会被谁订阅和消费,反过来,订阅者只知道它需要关心的事件,却不用理会该事件到底由谁发布。除了需要事先约定事件的协议之外,发布者与订阅者之间可以老死不相往来。</p>
|
||
<p>领域实现模型直接受制于领域设计模型。在对象范式下,对领域实现模型产生影响的是对观察者模式与发布者-订阅者模式的运用。尤其是后者,牵涉到消息中间件的技术选型,事件消息的格式定义,跨进程通信协议以及分布式通信的一致性问题。这与采用远程服务调用是截然不同的实现模式。在微服务架构中,远程服务之间的协作模式称之为<strong>编排(Orchestration)</strong>,利用事件进行协作的模式称之为<strong>协同(Choreography)</strong>。编排模式更加简单,能够清晰直观地表达业务流程;协同模式更为复杂,但能降低服务之间的耦合度,提高服务的自治能力,便于对现有服务进行修改。</p>
|
||
<p>整体看来,对象范式下的领域事件在领域模型驱动设计的各个阶段各有其价值,但它并非缺一不可的核心要素。在一个单体架构中,倘若无需追溯实体的状态变更,则领域事件带来的优势几乎可以忽略不计。领域分析模型、领域设计模型以及领域实现模型可以完全保持一致,状态的管理与变更都交给聚合内的实体来完成,这样获得的领域模型才是最简单的,也最符合对象范式的传统建模思想。</p>
|
||
<p>领域事件给对象范式领域模型带来的主要价值体现为对依赖的解耦。分布式架构尤其是微服务架构对领域模型的设计带来了直接的影响,<strong>我们无法用一张对象图反映整个现实世界</strong>。对象图可能会被进程边界隔离为多个独立的小型对象图,它们之间的协作与通信为模型的设计带来了挑战。领域事件的解耦作用在引入消息中间件之后,可以彻底解除发布者与订阅者之间的耦合,事件消息的异步非阻塞通信方式,还能改进系统的响应能力。</p>
|
||
<h3>事件在事件范式</h3>
|
||
<p>事件范式改变了我们对现实世界的观察角度,也改变了我们的设计与实现,它将“领域事件”抬高到了无与伦比的至高地位。</p>
|
||
<p>当我们选择事件范式进行领域模型驱动设计时,事件风暴就成了领域分析建模自然而然的选择,它简直就是为了事件范式而生的。通过事件风暴获得的以领域事件为核心的模型,是对状态建模的真实体现。事件的因果关系可以帮助我们驱动出管理者关心的关键领域事件,进而通过领域事件驱动出决策命令、读模型、策略与聚合。每一个领域场景,都体现为一系列“命令-事件”的响应模式,读模型是命令或事件携带的消息数据,聚合为状态的持有者。</p>
|
||
<p>事件范式针对状态迁移进行建模,领域事件作为聚合状态迁移历史的“留存”,改变了聚合的生命周期管理方式。聚合由状态的持有者与控制者变成了响应命令和发布事件的中转站,聚合的状态也不再发生变更,而是基于“事实”的特征,为每次状态变更记录一条领域事件。聚合不再被持久化,持久化的是一系列沿着时间轴不停记录下来的历史事件。这就是与事件范式设计模型相匹配的<strong>事件溯源模式</strong>。该模式通过对事件的溯源,满足管理和运营的审计需求,通过回溯整个状态变更的过程可以完美地重现聚合的生命旅程。</p>
|
||
<p>事件溯源彻底改变了领域设计模型。要注意事件在对象范式和事件范式设计模型中的角色差异。如前所述,对象范式中的事件实则是观察者模式的体现,而事件范式中的事件则是状态的实例。当聚合发生状态变更时,领域事件被创建,但它并非观察者模式中的主体(Subject)类,事件溯源模式只是将事件视为一条需要持久化以便于追溯的状态数据。</p>
|
||
<p>当然,事件一经发生,总会有对象关心,天然就会引入观察者模式。观察者模式与事件溯源模式都是一种设计模式,但前者体现了设计理念,后者体现为建模理念。观察者模式中的事件扮演了“触发器”的角色,而事件溯源模式中的事件则是一种“事实”。如果事件溯源需要事件同时成为一个触发器,就可以将事件溯源模式与观察者模式结合。例如,事件溯源采用的事件存储无法响应复杂的聚合查询需求。要弥补这一缺陷,可以将事件存储与聚合存储分开。存储的事件是聚合状态发生变更的事实,但同时它又作为触发器通知聚合,由其接收事件消息来更新自身的聚合值,并完成持久化。这种机制其实是 CQRS(Command Query Responsibility Segregation,命令查询职责分离)架构模式的一种实现。</p>
|
||
<p>CQRS 架构模式将领域模型分为了查询模型和命令模型,事件在其中起到了协调的作用。由于命令模型与查询模型往往位于不同的进程,引入的事件其实是通过消息中间件传递的事件消息,此时运用的观察者模式应为发布者-订阅者模式。</p>
|
||
<p>无论是对象范式,还是事件范式,引入发布者-订阅者模式的目的都是为了解耦。命令、查询与事件是服务交互的三种机制。与命令和查询不同,事件无需返回值,它的传递是异步的。事件的发布者只需考虑将事件发布出去,至于事件能否被正确传递,则由消息中间件来负责。一旦事件发布,发布者就无需等待,这种异步非阻塞的通信模式可以大幅度提升系统整体的响应能力。</p>
|
||
<p>在领域实现阶段,领域实现模型直接受到领域设计模型的影响。对象之间的协作方式、领域模型的持久化以及对一致性与不变量的保证都会发生天翻地覆的改变,实现代码的测试战略也将随之发生变化。Scott Millett 认为:“既然概念化的模型都是以事件为中心的,那么代码也需要以事件为中心,以便它能够表述概念化模型。”当然,无论怎么改变,实现模型始终需要遵循一些基本的架构原则,尤其需要分离业务复杂度和技术复杂度。限界上下文的边界对实现模型也将产生直接影响。若边界为跨进程通信,就需要考虑分布式事务的问题。</p>
|
||
<p>分布式事务满足 ACID 的成本太高,因为它需要将参与事务的所有资源锁定,若事务执行周期较长,就会严重影响系统的并发性能。因此,在多个分布式远程服务参与协作的场景下,采用满足最终一致性的柔性事务才是最佳选择。一旦我们选择分布式通信的事件消息来实现领域事件,就产生了正反两方面的结果:</p>
|
||
<ul>
|
||
<li>正面:引入事件可以支持事务的最终一致性</li>
|
||
<li>反面:事件消息带来了分布式事务的挑战</li>
|
||
</ul>
|
||
<p>若要消融这正反两方面的矛盾,就需要将领域事件用到正确的领域场景中,即因为技术实现的约束,需要引入事件来保证服务之间的松散耦合与事务的最终一致性。这一设计决策其实违背了领域驱动设计的精神,因为它是技术实现干预领域模型的一种体现。</p>
|
||
<p>选择事件范式,意味着你改变了整个领域模型驱动设计的过程与实践。决定是否采用事件范式,还将直接影响整个系统的架构与技术选型,对于团队成员的开发技能也提出了不同的要求。Martin Fowler 认为:“架构就是完成之后很难更改的东西。”显然,一旦选择了事件范式,就很难在将来再更换为其他建模范式。因此,<strong>决定是否采用事件范式的决策属于架构决策</strong>,在做出该决定时,需要慎之又慎。</p>
|
||
<p>如果觉得建模范式的选择承载了架构的重量,就需要把握好限界上下文的控制边界。我在《领域驱动设战略设计》中将架构决策分为了系统和限界上下文两个层次。我们可以根据限界上下文的特点选择不同的建模范式,只要把握好限界上下文边界的控制力,就能将建模范式对全局的影响降到最小。</p>
|
||
<p>建模范式与架构模式、设计模式之间存在着一丝若隐若现的关系,它们并非相互对应,却又互相影响和约束。若选择对象范式,通常只可运用观察者模式或发布者—订阅者模式,即将领域事件用在状态通知和依赖解耦上;若选择事件范式,则主要运用事件溯源模式来实现聚合状态变更的记录和追溯的本源,但同时也可运用观察者模式或发布者—订阅者模式来实现状态通知。</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/领域驱动设计实践(完)/079 场景驱动设计与 DCI 模式.md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/领域驱动设计实践(完)/081 发布者—订阅者模式.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":"70997eb62fbc3cfa","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>
|