概述
- 软件和领域相处的最佳方式是让软件成为领域的反射(映射)。
- 软件需要具现领域里重要的核心概念和元素,并精确实现它们之间的关系。软件需要对领域进行建模。
- 首先需要建立领域的抽象,选择抽象知识来描绘和传达一个模型。
- 模型是对目标领域的内部展现方式,会贯穿设计和研发的全过程。
- 领域驱动设计的一个核心原则是使用一种基于模型的语言。模型是软件满足领域的共同点,适合作为通用语言的构造基础。
- 软件开发的重点:必须以业务领域为中心。
软件设计的方法
- 瀑布设计方法:
知识只有单一流向。主要问题是业务专家得不到分析人员反馈信息,分析人员也得不到开发人员的反馈信息。
- 敏捷方法学:
敏捷方法视图解决的另一个问题称为“分析瘫痪”。
问题和局限性:缺乏了真实可见的设计原则,开发人员的持续重构导致代码更加难理解或更难改变。
建模型图
UML适合用来构建模型:
UML擅长表现类,它们的属性和相互之间的关系。但类的行为和约束不容表现。
用文档创建模型图:
文档可以创建文本和解释图,以多个方面解释领域中的各个部分。文档必须跟模型同步。避免陈旧、使用了错误的语言、或者不能如实反映模型的文档。
模型领域设计
任何领域都能被表现成多种模型,每一种模型都能用不同的方式表现成代码。
分析模型这个设计技巧是业务领域分析的结果。用分析模型可用来理解领域。同样存在一个问题:分析不能预见模型中存在的某些缺陷以及领域的所有复杂关系。
在模型构建时就考虑到软件和设计,能紧密关联领域模型和设计。如果设计或者设计中的核心部分不能映射到领域模型,模型基本就没有什么价值。
面向对象非常适合对模型的实现,面向对象编程语言让监理模型对象、对象关系与编程副本之间的映射称为了可能。模型驱动设计中不推荐过程化编程。面向对象编程提供了对象的类和类之间的关联关系、对象实例、以及对象实例之间的消息通信。
一个通用领域驱动的架构性方案包含4个概念层:
用户界面/展现层 |
负责向用户展现信息以及解释用户命令。 |
应用层 |
用来协调应用的活动。它不包含业务逻辑。它不保留业务对象的状态,但它保佑应用任务的进度状态。 |
领域层 |
本层包含关于领域的信息。这是业务软件的核心。在这里保留业务对象的状态,对业务对象和它们状态的持久化被委托给了基础设施层。 |
基础设施层 |
本层作为其他层的支撑库存在。提供了层间的通信,实现对业务对象的持久化,包含对用户界面层的支撑库等作用。 |
值对象是用来描述领域的特殊方面,且没有标识符的一个对象。如果值对象是可共享,那么它们应该是不可变的。值对象应该尽量的简单。
服务在领域中封装了一个概念,服务在技术框架中是通用的,担当了提供操作接口的作用。服务具有以下3个特征:
- 服务执行的操作设计一个领域概念,这个额领域概念通常不属于一个实体或者值对象。
- 被执行的操作涉及到领域中的其他的对象。
- 操作是无状态的。
使用服务时保持领域的隔离非常重要。
模块
模块被用来作为组织相关概念和任务以便降低复杂性的一种方法。
将高关联度的类分组到一个模块以提供尽可能大的内聚。
模块是一种增进内聚和消除耦合的方法。它由功能上或者逻辑上属于一体的元素构成,以保证内聚。在模块中访问一个接口的方式代替调用模块中的三个对象,可降低耦合。低耦合降低了复杂性并增加了可维护性。
内聚的类型:
常用的有两种:①通信性内聚②功能性内聚(最佳的内聚类型)
聚合
- 聚合是一个用来定义对象所有权和边界的领域模式。
- 工厂和资源库是处理对象的创建和存储问题的两个设计模式。
- 聚合是针对数据变化可以考虑成一个单元的一组相关的对象。
以下情况使用构造函数:
①构造过程并不复杂
②对象的创建不涉及到其他对象的创建,所有的属性需要传递给构造函数。
③类是特定的类型,不涉及到继承,所以不用再一系列的具体实现中进行选择。
④客户程序对实现感兴趣,希望选择使用策略模式。
重构
关于建模:
首先阅读业务规范,从中寻找名词和动词。名词被转换成类,而动词转换成方法。产生浅层次的模型。
规则应该被封装到一个负责它的对象中,成为客户的规约,并且被保留在领域层中。
界定的上下文:
将大模型分解成数个较小的部分。只要遵守想绑定的契约,整合得好的小模型越来越有独立性。每个模型都应该有一个清晰的边界,模型之间的关系也应该被精确地定义。
通常的做法是先定义上下文,然后为每个上下文创建模型,再用一个约定的名称指明每个模型所属的上下文。在上下文之间,共享内核(Shared kernel)和客户——供应商(consumer-Supplier)是具有高级交互的模式。隔离通道(Separate Way)是在想让上下文高度独立和分开运行时要用到的模式。还有两个模式,开放主机(open Host Service)和防崩溃层(Anticonrruption Layer),处理系统和继承系统或者外部系统之间的交互。
共享内核的目的是减少重复,但仍保持两个独立的上下文。
供应商团队的模型做得好的情况下,客户团队遵守供应商的模型。在不更改内核的情况下,只能做自己模型的一部分,在所提供的接口为它简单的创建适配器,用户模型和组件模型之间做转换。
防崩溃层用外部语言和外部模型交流。一个非常好的方案是将这个层看作从客户端模型来的一个服务。服务会处理所需要的转换,使自己模型保持独立。把服务看成一个Facade,可能需要一个适配器,来完成两个系统之间的转换。
独立方法模式适合一个企业应用由几个较小的应用组成,而且从建模的角度来看彼此之间很少有或者没有相同之处的情况。
当一个子系统需要和其他子系统集成时,定义一个能以服务的形式访问你子系统的协议。开放,优化和扩展这个协议,使其可以处理新的集成需求。对与特殊需求,使用一个一次性的转换器增加协议,使共享的协议保持简洁和精干。
一个大的领域会有一个大的模型。在多次重构后,也依然会很大。定义一个代表领域本质的核心域(Core Domain)。精炼过程的副产品将是组合领域中其他部分的普通子域(Generic Subdomain)。
一个系统的核心域有可能会变成另一个系统的普通子域。正确标识核心,以及和其他模型之间的关系是非常重要的。
下面几种方法实现普通子域:
- 购买现成的方案。
带来的问题:学习曲线;代码错误只能等待别人来解决;与自己的系统集成也不容易。
- 外包。
- 已有模型。
一个取巧的方案,使用一个已经创建的模型。
- 自己实现。
需要额外的付出,包括维护的压力。