领域驱动设计(Domain-Driven Design DDD)——战略设计1

一、概述

        随着系统的增长,它会越来越复杂,当我们无法通过分析对象来理解系统的时候,就需要掌握一些操纵和理解大模型的技术了。

        最负雄心的企业欲实现一个涵盖所有业务、紧密集成的系统。因大型公司的业务模型巨大且复杂,很难把它作为一个整体来理解且难以管理。按传统做法,我们可以将其拆为较小的部分后组合。但问题在于,如何保证实现这种模块化的同时,不失去集成所具备的好处;从而使系统的不同部分能够进行互操作,以便协调各种业务操作。如果设计一个把所有概念都涵盖进来的单一领域模型,它将会非常笨拙,而且将会出现大量难以察觉的重复和矛盾。而如果用临时拼凑的接口把一组小的、各自不同的子系统集成到一起,又不具备解决企业级问题的能力,并且在每个集成点上都有可能出现不一致。通过采用系统的、不断演变的设计策略,就可以避免这两种极端问题。

        即使这种规模的系统中采用领域驱动设计方法,也不要脱离实现去开发模型。每个决策都必须对系统开发产生直接的影响,否则它就是无关的决策。战略设计原则必须指导设计决策,以便减少各个部分之间的互相依赖,在使设计意图更为清晰的同时而不失去关键的互操作性和协同性。战略设计原则必须把模型的重点放在捕获系统的概念核心,也就是系统的“远景”上。而且在完成这些目标的同时又不能为项目带来麻烦。为了帮助实现这些目标,需探索三个主题:上下文、精炼和大型结构。

        1、上下文是最不易引起注意的原则,但实际上它却是最根本的。无论大小,成功的模型必须在逻辑上一致,不能有互相矛盾或重叠的定义。有时,企业系统会集成各种不同来源的子系统,或包含诸多完全不同的应用程序,以至于无法从同一个角度来看待领域。要把这些不同部分中隐含的模型统一起来可能是要求过高了。通过为每个模型显式地定义一个Bounded Context,然后在必要的情况下定义它与其他上下文的关联,建模人员就可以避免模型变得缠杂不清。

        2、精炼可以减少混乱,并且把注意力集中到正确的地方。人们通常在领域的一些次要问题上花费太多的精力。整体领域模型需要突出系统中最有价值和最特殊的那些方面,而且在构造领域模型时应该尽可能把注意力集中在这些部分上。虽然一些支持组件也很关键,但绝不能把它们和领域核心一视同仁。把注意力集中到正确的地方不仅有助于把精力投入到关键部分上,而且还可以使系统不会偏离预期方向。战略精炼可以使大的模型保持清晰。有了更清晰的视图后,Core Domain的设计就会发挥更大的作用。

        3、大型结构是用来描述整个系统的。在非常复杂的模型中,人们可能会“只见树木,不见森林”。精炼确实有帮助,它使人们能够把注意力集中到核心元素上,并把其他元素表示为支持作用,但如果不贯彻某个主旨来应用一些系统级的设计元素和模式的话,关系仍可能非常混乱。有几种组织大型结构的方法,可以用它们作为基础结构,去构建我们需要的大型结构。如Responsibility Layer(职责层) 、Evolving Order等。

        这3种原则各有各的用处,但结合起来使用将发挥更大的力量。大型结构和精炼能够帮助我们理解各个部分之间的复杂关系,同时保持整体视图的清晰。Bounded Context(上下文)能够使我们在不同的部分中进行工作,而不会破坏模型或是无意间导致模型的分裂。把这些概念加进团队的Ubiquitous Language中,可以帮助开发人员找出大型模型的自己的解决方案。

二、保持模型的完整性

        模型最基本的要求是它应该操持一致,术语总是具有相同的意义,并且不包含互相矛盾的规则:虽然我们很少明确地考虑这些要求。模型内部的一致性又叫统一(Unification),这种情况下,每个术语都不会有模棱两可的意义,也不会有规则冲突。

        但大型系统开发并非如此理想。在整个企业系统中保持这种水平的统一是一件得不偿失的事情。在系统的各个不同部分中开发多个模型是很有必要的,但我们必须慎重地选择系统的哪些部分可以分开,以及它们之间是什么关系。我们需要是一些方法来保持模型关键部分的高度统一。这需要通过有意识的设计决策和建立特定过程才能实现。大型系统领域模型的完全统一即不可行,也不划算。

        有时人们会反对这一点,多个模型看上去似乎不够雅致。甚至对多个模型的抵触会导致“极富雄心”的尝试——将一个大型项目的所有软件统一到单一模型中。如果想这样做一定要考虑以下风险。

        (1)一次尝试对遗留系统做过多的替换。
        (2)大项目可能会陷入困难,因为协调的开销太大,超出了这些项目的能力范围。
        (3)具有特殊需要的应用程序可能不得不使用无法充分满足需要的模型,而只能将这些无法满足的行为入到其他地方。
        (4)另一方面,试图用一个模型来满足所有人的需求可能会导致模型中包含过于复杂的选择,因而很难使用。

        此外,除了技术的因素外,权力上的划分和管理级别的不同也可能要求把模型分开。而且不同模型的出现也可能是团队组织和开发过程导致的结果。因此,即使完全的集成没有来自技术方面的阻力,项目也可能面临多个模型。

        既然无维护一个涵盖整个企业的统一模型,那就不要再受到这种思路的限制。通过预先决定什么应该统一,并实际认识到什么不能统一,我们就能够创建一个清晰的、共同的视图。确定了这些之后,就可以开始着手开始工作,以保证那些需要统一的部分保持一致,不需要统一的部分不会引起混乱或破坏模型。

        我们需要用一种方式来标记出不同模型之间的边界和关系。我们需要有意识地选择一种策略,并一致地遵守它。

        为识别、沟通和选择模型边界及关系,我们使用了一些模式,Bounded Context(限界上下文)定义了每个模型的应用范围,而Context Map(上下文图)则给出了项目上下文以及它们之间关系的总体视图。这些降低模糊性的技术能够使项目更好地进行,但还不够。一旦确立了Context的边界之后,仍需要持续集成这种过程,它能够使模型保持统一。

        其后,在这个稳定的基础之上,我们就可以开始实施那些在界定和关联Context方面更有效的策略——从通过共享内核(Shared Kernel)来紧密上下文,到那些各行其道(Separate Ways)地进行松散耦合的模型。

        模型完整性模式的导航图 

1、模式:Bounded Context

        大型项目上会有多个模型共存,在很多情况下这没什么问题。不同的模式应用于不同的上下文中。但也会有引起含糊和混乱的情况。如:两个团队为同一个新系统开发不同的功能,那么他们使用的是同一个模型吗?他们的意图是至少共享其所做的一部分工作,但却没有界限告诉他们共享什么、没共享什么。而且他们也没有一个过程来维护共享模型,或快速检测模型是否有分歧。他们只是在系统行为不可预测时才意识到他们之间产生了分歧。

        即使在同一个团队中,也可能会出现多个模型。团队的沟通可能会不畅,导致对模型的理解产生难以捉摸的冲突。原先的代码往往反映的是早先的模型概念,而这些概念与当前模型有着微妙的差别。

        每个人都知道两个系统的数据格式是不同的,因此需要进行数据转换,但这只是问题的表面。问题的根本在于两个系统所使用的模型不同。当这种新差异不是来自外部系统,而是发生在同一个系统中时,它将更难发现。然而,所有大型团队项目都会发生这咱情况。

        任何大项目都会存在多个模型。而当基于不同模型的代码被组合到一起后,软件就会出现Bug、变得不可靠和难以理解。团队成员之间的沟通变得混乱。人们往往弄不清楚一个模型不应该在哪个上下文中使用。

        模型混乱的问题最终会在代码不能正常运行时暴露出来,但问题的根源却在于团队的组织方式和成员的交流方法。因此,为了澄清模型的上下文,我们既要注意项目,也要注意它的最终产品(代码、数据库模式等)。

        一个模型只在一个上下文中使用。这个上下文可以是代码的一个特定部分,也可以是某个特定团队的工作。如果模型是在一次头脑风暴会议中得到的,那么这个模型的上下文可能仅限于那次讨论。就拿本书来说,示例中所使用的模型的上下文就是那个示例所在的小节以及任何相关的后续讨论。模型上下文是为了保证该模型中的术语具有特定意义而必须要应用的一组条件。

        为了解决多个模型的问题,我们需要明确地定义模型的范围——模型的范围是软件系统中一个有界的部分,这部分只应用一个模型,并尽可能保持统一。团队组织必须一致遵守这个定义。

        因此:

        明确地定义模型所应用的上下文。根据团队的组织、软件系统的各个部分的用法以及物理表现(代码和数据库模式等)来设置模型的边界。在这些边界中严格保持模型的一致性,而不要受到边界之外问题的干扰和混淆。

        Bounded Context明确地限定了模型的应用范围,以便让团队成员对什么应该保持一致以及上下文之间如何关联有一个明确和共同的理解。在Context中,要保证模型在逻辑上统一,而不用考虑它是不是适用于边界之外的情况。在其他Context中,会使用其他模型,这些模型具有不同的术语、概念、规则和Ubiquitous Language的技术行话。通过划定明确的边界,可以使模型保持纯粹,因而在它所适用的Context中更有效。同时,也避免了将注意力切换到其他Context时引起的混淆。跨边界的集成必然需要进行一些转换,但我们可以清楚地分析这些转换。

        Bounded Context不是Module,有时这两个概念易引起混淆,但它们是具有不同动机的不同模式。确实,当两组对象组成两个不同模型时,人们几乎总是把它们放在不同的Module中。但人们也会在同一模型中用Module来组织元素,它们不一定要表达划分Context的意图。

识别Bounded Context中的不一致

        很多征兆都可能表明模型中出现了差异。最明显的是已编码的接口不匹配。对于更微妙的情况,一些意外行为也可能是一种信号。采用了自动测试的Continuous Integration可以帮助捕捉到这类问题。但语言上的混乱往往是一种早期的警告信号。

        将不同模型的元素组合到一起可能会引发两类问题:重复的概念和假同源。重复的概念是指两个模型元素(以及伴随的实现)实际上表示同一个概念。每当这个概念的信息发生变化时,都必须更新两个地方。每次由于新知识导致一个对象被修改时,必须重新分析和修改另一个对象。如果不进行实际的重新分析,结果就会出现同一概念的两个版本,它们遵守不同的规则,甚至有不同的数据。更严重的是,团队成员必须学习做同一件事情 的两种方法,以及保持这两种方法同步的各种方式。

        假同源可能稍微少见一点,但它潜在的危害更大。它要是指使用相同术语(或已实现的对象)的两个人认为他们是在谈论同一件事情,但实际上并不是这样。

        当发现这些问题时,团队必须要做出相应的决定。可能需要将模型重新整合为一体,并加强用来预防模型分裂的过程。分裂也有可能是由分组造成的,一些小组出于合理的原因,需要以一些不同的方式来开发模型,而且你可能也决定让他们独立开发。

三、参考文档

DOMAIN-DRIVERN DESIGN
TACKLING COMPLEXITY IN THE HEART OF SOFTWARE

领域驱动设计
软件核心复杂性应对之道

【美】Eric Evans 著   赵俐 盛海艳 刘霞 等 译

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/716419.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot236基于springboot在线课程管理系统的设计与实现

基于SpringBoot在线课程管理系统的设计与实现 摘要 本文首先介绍了在线课程管理系统的现状及开发背景,然后论述了系统的设计目标、系统需求、总体设计方案以及系统的详细设计和实现,最后对在线课程管理系统进行了系统检测并提出了还需要改进的问题。本系…

echarts vue 动画效果的水球图、波浪图教程

1、安装插件 前提是已经安装了echarts(我的版本是4.2.1) npm install echarts-liquidfill --save 我安装了3.1.0版本的,结果运行时报错"TypeError: wave.ensureState is not a function" 原因:echarts版本和echarts-l…

miniconda3彻底删除虚拟环境

退出虚拟环境:确保您不在要删除的虚拟环境中。如果在,使用命令 conda deactivate 来退出当前激活的虚拟环境。查看虚拟环境列表:运行命令 conda env list 或 conda info -e 来查看所有存在的虚拟环境及其路径。删除虚拟环境:使用命…

在VMware中安装CentOS 7并配置Docker

VMware安装CentOS 7 一、介绍 该文章介绍如何使用启动U盘在虚拟机里面安装系统,虚拟机版本为VMware Workstation 16 pro,Linux版本为CentOS Linux release 7.9.2009 (Core)。 二、安装 1、创建虚拟机 点击创建新的虚拟机 选择典型就可以了&#xf…

前缀和算法题(区间次方和、小蓝平衡和、大石头的搬运工、最大数组和)

一、前缀和的原理和特点 prefix表示前缀和,前缀和由一个用户输入的数组生成。对于一个数组a[](下标从1开始),我们定义一个前缀和数组prefix[],满足: prefix有一个重要的特性,可以用于快速生成p…

WordPress建站入门教程:如何安装本地WordPress网站运行环境?

有些站长想要搭建WordPress网站,又担心自己玩不转,白白浪费购买域名和主机空间的费用。像这种情况,最好的做法就是在自己电脑上安装一个WordPress网站运行环境,然后在本地电脑搭建WordPress,等熟悉掌握后再考虑购买域名…

多输入多输出 | MATLAB实现GWO-Elman灰狼优化循环神经网络多输入多输出预测

多输入多输出 | MATLAB实现GWO-Elman灰狼优化循环神经网络多输入多输出预测 目录 多输入多输出 | MATLAB实现GWO-Elman灰狼优化循环神经网络多输入多输出预测预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 Matlab实现GWO-Elman灰狼优化循环神经网络多输入多输出…

如何在电脑上中恢复已删除的视频

您可以在电脑中恢复已删除的视频,无需任何繁琐的工作。您所需要做的就是阅读本文,了解恢复已删除视频的最佳方法。 一次错误的点击可能会夺走您以视频形式存储的宝贵记忆。嗯,有些视频不适合删除,您希望永远保留它们。失去这些宝…

如何使用Docker搭建StackEdit编辑器并结合内网穿透实现远程办公

文章目录 前言1. ubuntu安装VNC2. 设置vnc开机启动3. windows 安装VNC viewer连接工具4. 内网穿透4.1 安装cpolar【支持使用一键脚本命令安装】4.2 创建隧道映射4.3 测试公网远程访问 5. 配置固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址5.3 测试…

优选算法|【双指针】|1089.复写零

目录 题目描述 题目解析 算法原理讲解 代码 题目描述 1089. 复写零 给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。 注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就…

LeetCode受限条件下可到达节点的数目

题目描述 现有一棵由 n 个节点组成的无向树,节点编号从 0 到 n - 1 ,共有 n - 1 条边。 给你一个二维整数数组 edges ,长度为 n - 1 ,其中 edges[i] [ai, bi] 表示树中节点 ai 和 bi 之间存在一条边。另给你一个整数数组 restr…

OJ:移除链表元素

203. 移除链表元素 - 力扣(LeetCode) 思路:这个题可以直接在原链表上进行修改,但是修改链表的指向是有点麻烦的,所以我们给两个指针,phead和ptail,这是新链表的两个指针,再给一个指针pcur来遍历…

Sqli-labs靶场第12关详解[Sqli-labs-less-12]

Sqli-labs-Less-12 #手工注入 post传参了 根据题目看,像一个登录页面,尝试使用布尔型盲注测试能否登录网站 1. Username输入a a" 测试是否会有报错,burp抓包 报错:syntax to use near "a"") and passw…

消息中间件之RocketMQ源码分析(二十七)

Broker提交或回滚事务消息 当生产者本地事务处理完成并且Broker回查事务消息后,不管执行Commit还是Rollback,都会根据用户本地事务的执行结果发送一个End_transaction的RPC请求给Broker,Broker端处理该请求的类是EndTransactionProcessor 第一步&…

volatile 关键字 (一)

volatile 关键字 (一) 文章目录 volatile 关键字 (一)如何保证变量的可见性?如何禁止指令重排序? 文章来自Java Guide 用于学习如有侵权,立即删除 如何保证变量的可见性? 在 Java 中…

【Linux安装软件命令及vim、gcc使用说明】

安装软件命令 Linux安装软件的命令首先要进入管理员权限 首先在终端输入sudo su切换到管理员界面 输入对应的密码,注意这里的密码不会显示出来,输完密码之后回车即可。当出现root就代表已经是管理员界面了。 如果相应退出管理员界面输入exit即可。 注…

数组、冒泡排序、函数、作用域、对象、Math

数组 1.定义数组: a)通过字面量的方式定义数组 let ary[1,2,3,4]b)通过定义构造函数的方式定义数组: let 数组名new Array(值,值,值);数组的操作方式 a)增 //在数组末尾添加值 arr.push(新增的内容) //在数组的开始添加值 arr.unshift(新增的内容)b…

Redis主从复制+Redis哨兵模式+Redis群集模式

Redis主从复制Redis哨兵模式Redis群集模式一、Redis主从复制1、主从复制的作用2、主从复制过程3、搭建Redis主从复制3.1 所有节点服务器安装redis3.2 修改Redis配置文件(Master节点操作)3.3 修改Redis配置文件(Slave节点操作)3.4 验证主从效果 二、Redis哨兵模式1、哨兵模式的作…

8、IBOScms代码审计

一、sql注入 1、sql注入(Ⅰ) 限制 rreport/api/getlist {"offset":0,"type":"send","keyword":{"subject":"111) AND (updatexml(1,concat(0x7e,(select user()),0x7e),1))-- qw"}}复现 POST /?rreport/api/…

Vue开发实例(十一)用户列表的实现与操作

用户列表的实现与操作 一、创建用户页面和路由二、表格优化1、表头自定义2、表格滚动3、加入数据索引4、利用插槽自定义显示 三、功能1、查询功能3、增加4、删除5、修改 一、创建用户页面和路由 创建用户页面 在 src/components/Main 下创建文件夹user,创建文件Us…