问题:
1、 何为领域驱动设计(DOMAINDriven DESIGN)?
2、 UBIQUITOUS LANGUAGE(领域通用语言)应该是如何去描述
3、
作者:Eric Evans
第二部分 模型驱动设计的构造块
而主要的业务模型及在领域层。
模型主要包括Entity、Value Object和service
问题:
Entity、Value Object和Service是如何定义的?
它们是如何进行区分和划分的?
Entity模型具有标识的,模型必须定义出:“符合什么条件才算是相同的事务”。
要求:保持实体的简练、不要将注意力集中在属性或行为上,应该摆脱这些细枝末节
当我们只关心一个模型元素的属性时,应该将其归类到value object中。我们应该使这个模型元素表示出属性的意义,并为它提供相关功能。Value object应该是不可变的。不要为它分配任何标识,而且不要把它弄的和entity一样复杂。
在考虑到分布式环境中时:value object的两种方式应该如何适用,两个方式是复制和共享,都分别的特点是什么呢?
好的Service有以下三个特征:
1、 与领域概念相关的操作不是entity或value object的一个自然的部分
2、 接口是根据领域模型的其他元素定义的
3、 操作时无状态的(无状态指可以使用某个service的任何实例)。
当领域中的摸个重要的过程或转换操作不属于实体或值对象的自然职责时,应该在模型中添加一个作为独立接口的操作,并将其声明为service。定义接口时要使用模型语言,并确保操作名称是ubiquitous language中的术语。此外,应该将service定义为无状态的。
每个对象模型都应该找到它的聚合根,其他仅对该模型有效的应通过聚合根来得到而不应该直接访问该对象
该模式的规则:
1、 根Entity具有全局标识,它最终负责检查固定规则
2、 根Entity具有全局标识,边界内的entity具有本地标识,这些标识只有在Aggregate内部才是唯一的
3、 Aggregate外部的对象不能饮用除根Entity之外的任何内部对象。根Entity可以把对内部Entity的引用传递给它们,但这些对象只能临时使用这些引用,而不能保持引用。
4、 作为上一条规则的推论,只有aggregate的根才能直接通过数据库查询获取。所有其他对象必须通过关联的遍历才能找到
5、 Aggregate内部的对象可以保持对其他Aggregate根的引用
6、 删除操作必须一次删除Aggregate边界内的所有对象。
7、 当提交Aggregate边界内部的任何对象修改时,整个Aggregate中的所有固定规则都必须被满足。
应该将创建复杂对象的实例和聚合的职责转移给一个独立的对象,这个对象本身在领域模型中可能没人职责,但它仍是领域设计的一部分。提供一个封装所有复杂装配操作的接口,而且这个接口应该不需要客户引用要被实例化的对象的具体类。在创建Aggregate时要把它作为一个整体,并确保它满足固定规则。
好的工厂应满足两个基本需求:
1、 每个创建方法都是原子方法,而且满足被创建对象或者Aggregate的所有固定规则。
2、 Factory应该被抽象为所需的类型而不是创建出具体的类。
其实Repository就相当于我们平时所说的Dao层,但在领域模型中要对其做一些限制。
为每种需要全局访问的对象类型创建一个对象,这个就对象就相当于该类型的所有对象的在内存中的一个集合的“替身”。通过一个众所周知的接口来提供访问。提供添加和删除对象的方法,用这些方法类封装在数据存储中实际插入或删除数据的操作。提供根据具体标准来挑选对象的方法,并返回属性值满足查询标准的对象获对象集合(所返回的对象是完全实例化的),从而将实际的存储和查询技术封装起来,只为那些确实需要直接访问的Aggregate根提供Repository。让客户始终聚焦于模型,而将所有对象存储和访问操作交给Repository来完成。
三个用户层的应用程序功能:
1、 第一个类时TrackingQuery(跟踪查询)
2、 第二个类是BookingApplication(预定应用)
3、 第三个类是IncidentLoggin Application(事件日志应用)
第三部分通过重构来加深理解
深层模型能够穿过领域表象,清楚地表达出领域专家们的主要关注点以及最相关的知识。
倾听领域专家使用的语言。有没有一些术语能够简洁地表达出复杂的概念?他们有没有纠正过你的用词?当你使用某个特定短语时,他们脸上还流露出迷惑的表情吗?这些都暗示了某个概念也许可以改进模型。
业务规则通常不适合作为entity或value object的职责,而且规则的变化和组合也会掩盖领域对象的基本含义。但是将规则移出领域层的结果会更糟糕,因为这样一来,领域代码就不再表达模型了。
逻辑变成提供了一种概念,即“谓词”这种可分离、可组合的规则对象,但是要把这种概念用对象完全实现是很麻烦的。同时,这种概念也非常笼统,在表达设计意图方面,它的针对性不如设计的好。
个人观点:Specification模式其实就是建立检查接口或方法,针对某个entity编写校验规则,返回为布尔类型已验证是否继续下去。
如:
一些有助于获得柔性设计的模式
在命名类和操作时要描述他们的效果和目的,而不要表露它们是通过何种方式达到目的的。这样可以使客户开发人员不必去理解内部的细节。这些名称应该与Ubiquitous language保持一致,以便团队成员可以迅速推断出它们的意义。在创建一个行为之前先为它编写一个测试,这样可以促使你站在客户开发人员的角度上来思考它。
尽可能把程序的逻辑放到函数中,因为函数是只返回结果而不产生明显副作用的操作。严格地把命令(引起明显的状态改变的方法)隔离到不返回领域信息的,非常简单的操作中。当发现了一个非常合适承担复杂逻辑职责的概念时,就可以把这个复杂逻辑移到Value Object 中,这样可以进一步控制副作用。
把操作的后置条件和类及Aggregate的固定规则表述清楚。如果在你的编程语言中不能直接编写Assertion,那么就把他们编写成自动的单元测试。还可以把他们写到文档或图中。
寻找在概念上内聚的模型,以便使开发人员更容易退出预期的Assertion,从而加快学习过程并避免代码矛盾。
把设计元素(操作、接口、类和Aggregate)分解为内聚的单元,在这个过程中,你对领域中一切重要划分的直观认识也要考虑在内。在连续的重构过程中观察发生变化和保证稳定的规律性,并寻找能够解释这些变化模式的底层Conceptual contour。使模型与领域中那些一致的方面(正是这些方面使得领域成为一个有用的知识体系)相匹配。
在对象设计时保持低耦合是一个基本要素。把其他所有无关概念提取到对象之外。这样类就变成完全孤立的了,这就使得我们可以单独地研究和理解它。每个这样的孤立类都极大的减轻了因理解Module而带来的负担。
在适当的情况下,在定义操作时让它返回类型与其参数的类型相同。如果实现者的状态在计算中会被用到,那么实现者实际上就是操作的一个参数,因此参数和返回值应该与实现者有相同的类型。这样的操作就是在该类型的实例集合中的闭合操作。闭合操作提供了一个高层接口,同时又不会引入对其他感念的任何依赖性。
我们需要把过程中的易变部分提取到模型的一个单独的“策略”对象中。将规则与它所控制的行为区分开。按照strategy设计模式来实现规则或可替换的过程。策略对象的多个版本表示了完成过程的不同方式。
定义一个把composite的所有成员都包含在内的抽象类型,在容器上实现一些用来查询信息的方法,这些方法可用来收集与容器内容有关的信息。“叶”节点基于它们自己的值来实现这些方法,客户只需使用抽象类型,而无需区分“叶”和容器。
明确地定义模型所应用的上下文。根据团队的组织、软件形同的各个部分的用法以及物理表现(代码和数据库模式等)来设计模型的边界。在这些边界中严格模型的一致性,而不要受到边界以外问题的干扰和混淆。
建立一个经常把所有代码和其他实现2工件合并到一起的过程,并通过自动测试来快速查明模型的分裂问题。严格坚持使用Ubiquitous Language,以便在不同人的头脑中演变出不同的概念时,使所有者对模型都能达成一个共识。
识别每个模型在项目中的作用,并定义其Bounded Context。这包括非面向对象子系统的隐含模型。为每个BoundedContext命名,并把名称添加到Ubiquitous language中。
描述模型之间的接触点,明确每次交流所需的转换,并突出任何共享的内容。
从领域模型中选出两个团队都同意共享的一个子集。当然,除了模型的这个子集以外,这还包括与该模型部分相关的代码子集,或数据库设计的子集。这部分明确共享的内容具有特殊的状态,而且一个团队在没与另一个团队商量的情况下不应该擅自更改它。
在两个团队之间建立一种明确的客户/供应商关系。在计划会议中,下游团队相当于上游团队的客户。根据下游团队的需求来协商需要执行的任务并为这些任务做预算,以便每个人都知道双方的约定和进度
两个团队一起开发自动验收测试,用来验证预期的接口。把这些测试添加到上游团队的测试套件中,以便作为持续集成的一部分运行。这些测试使上游团队在作出修改时不必担心对下游团队产生副作用。
通过严格遵从上游团队的模型,可以消除在Bounded context之间进行转换的复杂性。尽管这会限制下游设计人员的风格,而且可能不会得到理想的应用程序模型,但选择conformity模式可以极大的简化集成。此外,这样还可以与供应商团队共享一种ubiquitous language。供应商处于驾驶者的位置上,引擎最好使他们能够容易沟通。他们从利他主义的角度出发,会与你分享信息。
创建一个隔离层,以便根据客户资金的领域模型来为客户提供相关的功能。这个层通过其现有接口与另一个系统进行对话,而只需对那个系统做出很少的修改,甚至无需修改。在内部,这个层在两个模型直接进行必要的双向转换。
定义一个协议,把你的子系统作为一组service供其他系统访问。开放这个协议,以便所有需要与你的子系统集成的人都可以使用它。当有新的集成需求时,就增强并扩展这个协议,但个别团队的特殊需求除外。满足这种特殊需求的方法是使用一次性的转换器来扩充协议,以便使公事协议简单并内聚。
把一个良好文档化、能够表达出所需领域信息的共享语言作为公共的通信媒介,必要时在其他的信息与该语言之间进行转换。
对模型进行提炼。找到Core domain 并提供一种易于区分的方法把它与那些辅助作用的模型和代码分开。最有价值和最专业的概念要轮廓分明。尽量压缩Core DOMAIN。
把内聚的子领域(他们不是项目的动机)识别出来。把这些子领域的通用模型提取出来,并放到单独的MODULE中。任何专有的东西都不应放到这些模块中。
写一份Core domain的简短描述以及它将会创造的价值,也就是“价值主张”。那些不能讲你的领域模型与其他领域模型区分来的方面就不要写。展示出领域模型是如何实现和均衡各方利益的。这份描述要尽量精简。
把概念上的Cohesive Mechanism(内聚机制)分离到一个单独的轻量级框架中。要特别注意公司算法或那些有完备文档的算法。用一个Intention revealing interface来公开这个框架的功能。现在,领域中的其他元素就可以只专注与如何表达问题(做什么)了,而把解决方案的复杂细节(如何做)转移给了框架。
对模型进行重构,把核心概念从支持性元素(包括定义得不清楚的那些元素)中分离出来,并增强Core的内聚性,同时减少它与其他代码的耦合。把所有通用元素或支持性元素提取到其他对象中,并把这些对象放到其他的包中-即使这会把一些紧密耦合的元素分开。
把模型中最基本的概念识别出来,并分离到不同的类、抽象类或接口中。设计这个抽象模型,使之能够表达出重要组件之间的大部分交互。把这个完整的抽象模型放到它自己的module中,而专用的、详细的实现类则留在由子领域定义的Module中。
大比例结构什么意思????
模式:Evolving order
模式:system metaphor
模式:responsibility layer
模式:Knowledge level
模式:Pluggable component framework