领域驱动设计 Domain-Driven Design

时间:2022-08-31 12:41:27
Part I: Putting the Domain Model to Work
    领域驱动讲求将领域模型作为领域专家、分析人员、开发人员之间交流沟通的核心。传统的瀑布模型方式下,缺乏有效的反馈机制,在链路上领域知识以不同的表现形式进行传递,知识的丢失容易造成需求与实现之间的断层。传统的迭代方式下,软件产品的优秀程度取决于开发者对领域知识的兴趣和掌握程度。

Chapter One. Crunching Knowledge
    领域模型是领域专家和分析人员互相沉淀知识的一个工具,它帮助分析人员理解领域知识,也为领域专家提供一个规范的表达形式,有条有理的描绘领域知识,分析、解决领域问题。另外,领域模型也是开发团队知识沉淀的一种方式,帮助开发人员了解他所从事的特定领域,提高建模技能。

Chapter Two. Communication and the Use of Language
    领域模型其实是一种语言,领域专家与分析人员、开发人员之间交流的通用语言。
    一开始,分析人员与领域专家需要对这个通用语言达成一致,双方能熟练的运用领域模型描述问题,表达、分析、处理问题。
    1. 领域模型不是图,图只是让核心、关键的概念清晰的呈现出来。图的表达能力有限,模型必须配备描述 (需求采集会议中的口头描述,或文档中的文字描述),将图形所代表的意义,以及图形中没有呈现出来的规则、断言、细节进行补充,才能完整地表述需求。
    2. 领域模型的UML或者类UML图不能太细太完整,否则过于庞大的模型会干扰人的思维,阻碍对主要部分,或者复杂逻辑的梳理。业务总是被切分成一个个片断进行分析,在每一个片断里,画出几个主要的对象和交互逻辑,细节的部分用文字记录、描述。
    3. 领域模型中不应当出现设计、技术方面的术语,也不应当出现开发人员不理解的业务术语。
    领域驱动设计 Domain-Driven Design
    图:讨论过程中手绘领域对象模型

    说明性模型:最好不使用UML,从而与领域对象模型明显区别开。
    领域驱动设计 Domain-Driven Design
    图:Explanatory Models

    关于领域模型表达方法 (UML图?Visio图?)的选择问题,标准的形式是使用UML,但如果领域专家或开发人员根本无法对UML图例形成清晰的概念 (将UML图映射到领域对象、逻辑,或代码实现),将注定DDD方式应用失败。因为没有掌握语言就无法使用它正确交流。第三节中强调建模人员要直接与开发过程接触,重要的一个作用是确认开发人员对领域模型这个语言是否正确理解,确保代码实现保持了建模者要表达的意图。因此DDD的一个关键思想是建模人员自始至终维护领域模型这个语言,以及它表达的内容,向团队的各个角色 (领域专家、开发人员等)诠释各个建模元素的含义,让各个角色掌握这个语言,运用它来表达、实现实际需求。这是分析人员最关键的职责,贯穿整个过程。
    领域逻辑的表达不要受UML图的约束,否则可能是问题所在,适当的描述就可以很好的解决这个问题。
    领域模型是语言,所以表达的准确性很重要。对英语系国家,在英语理解、英语词汇提取方面很自然,可惜Rose、PowerDesinger等都无法象QQ一样,在用户昵称和备注之间切换显示。所以项目需要花精力克服这个障碍。

Chapter Three. Binding Model and Implementation
    整个模型庞大关系错综复杂,会导致无法实现,模型应当清晰,拥有明显的边界而分出子模型。
    分析模型独立于设计模型方式不可取,领域模型需要同步反映领域业务、分析、设计、编码实现。
    建模人员必须直接与开发人员、与代码接触,把握代码实现过程中模型的约束,接受实现层面对模型的反馈。开发人员必须一定程度的参与模型讨论,与领域专家接触。

Part II: The Building Blocks of a Model-Driven Design
    领域驱动设计 Domain-Driven Design
    图:A navigation map of the language of MODEL-DRIVEN DESIGN

    DDD跟Smart UI模式冲突。如何在界面快速的为Customer添加一个属性、定制一张报表,这些不是DDD需要考虑的。

Chapter Four. Isolating the Domain
    使用分层方式将领域模型隔离出来。
    基础结构层:例如ORM、消息服务、日志服务、安全服务等。领域层:纯粹的领域逻辑处理。应用层:协调、组织领域层、基础结构层,构造完整的业务操作,例如事务控制等应当在应用层完成。

Chapter Five. A Model Expressed in Software
    关键因素:Entity、Value Object、Service。
    1. 关联:关联设计的考虑 (方向的设计等),简化关联的实现。NHibernate基于主映射端做级联更新。
    2. Entity:An object defined primarily by its identity is called an ENTITY,所以Hibernate在Identity映射、生成方式上提供强力支持,而对组合主建映射比较弱。
    3. Value Object:基于值是否可改变,是否可以共享考虑。Hibernate中数据类型的IsMutable属性是对这种情况的处理。
    4. Service:常见的形式如Manager等,特点是只做事情,无状态,不是实体和值对象固有的。注意区分领域服务和其它层服务,前者处理领域逻辑。
    5. 模块:好的模块划分提高系统清晰度,复杂的划分导致领域实现的分散甚至无法实现,丧失DDD的优势。

Chapter Six. The Life Cycle of a Domain Object
    1. Aggregates.
Aggregate root and boundary, reference issues, 实现生命周期、加锁范围等控制.
    复杂的关联导航会导致实现上陷入泥潭,丧失对领域焦点的把握能力,什么时候使用关联实现内聚,什么时候使用查询实现解耦,需要良好的把握。
    2. Factories. 工厂并非领域对象,只是一种设计构造,但它担任的是领域层的职责。从数据库加载聚合的各个部分,构造整个聚合对象,也是一种创建过程。
    3. Repositories. 也是设计构造,主要职责是CRUD,以及使用各种属性进行query的方法。Hibernate中Session的一个重要角色,就是作为全局通用repository基础 (当然还有Unit of Work等其它职责)。它还不能完全算是repository,因为repository的目的,其一是封装数据库检索技术的繁杂性导致对领域模型的关注重点发生偏移,其二是防止领域逻辑封装的泄漏,Session的HQL、SQL Query等违背了第二点。对每个聚合体 (例如采购订单)设计一个repository类,其实就是poor model中Impl/Manager类增删改查部分的逻辑。
    Repository与factory的区别,repository主要职责是查询,它将对象创建、加载委托给factory。

Chapter Seven. Using the Language: An Extended Example
    完整的示例,贯穿讲解整个第二部分的内容:Isolating the Domain, Distinguishing ENTITIES and VALUE Objects, Designing Associations, AGGREGATE Boundaries, Selecting REPOSITORIES, Walking Through Use Cases, Object Creation (Factory), Refactoring, MODULES等等。

Part III: Refactoring Toward Deeper Insight
    Deep models: 经过多次迭代重构之后,抽象层次较深,表现力较强的模型。
    Supple Design: 柔性设计,不断重构的产物。

Chapter Eight. Breakthrough
    这个例子excellent,06年刚好碰到过完全类似的情况。一个项目中客户对一个功能模块提出了挑战性的需求,经过仔细的分析,我发现应当从核心领域对象中再分离一个概念出来,不仅完美的解决当时的需求,之前10多家客户实施过程中的相关问题都能够非常优雅的解决。尽管最终的表现形式是设计上,你的类结构、关系不同了,但最关键的是领域模型上的突破,是基于对领域的深层理解消化后,在业务分析上的突破。一般它会给领域模型带来一些新的名词概念、系统功能模块,甚至是操作流程,但这些领域专家极为认同,更能应对复杂的领域业务。
    Don't become paralyzed trying to bring about a breakthrough. 没有什么标准、定律指导我们去完成某一个具体业务的分析、优化设计,参考成熟优秀的系统,参考象BPML、WfMC这样有限的参考模型和《Analysis Patters》,是一个捷径,但很多时候这些参考是空白的。基于DDD思想,在不断迭代重构的过程中加深对领域知识的理解沉淀,为突破创造条件,是一个不错的实践型方法。

Chapter Nine. Making Implicit Concepts Explicit
    开发人员必须敏锐的捕捉隐含概念:仔细聆听团队的表达用语,研究设计中不协调的地方以及专家看似矛盾的说法,查阅领域相关的文献,尽量挖掘隐含概念。

Chapter Ten. Supple Design
    柔性设计是隐含概念的补充,隐含概念挖掘出来后你的手头已经拥有了足够的materials,将这些材料组织成灵活的设计形式就是柔性设计范畴。要防止柔性设计变成overengineering,避免过分的抽象等间接层次的出现。
    领域驱动设计 Domain-Driven Design
    图:Some patterns that contribute to supple design

    1. Intention-Revealing Interfaces: 名字即表达用意,主要在接口 (公共对象的public方法)设计上,不要让写client的人对接口方法名摸不着头脑。是应该好好学英语呢还是应该开发个中文语言?
    2. Side -Effect-Free Functions: 边际效应/副作用 是状态的记录、改变造成,象deep copy (Hibernate等ORM中就用的很多)就是为了避免side effect。
    3. Assertions: design by contract的产物,就是在方法的组织设计上,避免出现理解上的多义性。通常情况下大家都是通过注释来进行说明,但DDD讲究代码就是对领域模型的一种描述。
    4. Conceptual Contours: 粒度、边界的精细设计优化问题。
    5. Standalone Classes: 降低关联依赖。
    6. Closure of Operations: 指返回值跟操作对象类型相同,主要用在值对象上,例如实数相乘结果仍然是实数,xslt转换xml后结果可以仍然是xml。在开源框架中这种模式经常可以见到。
    7. Declarative Design: 声明性设计的运用比较广泛,例如Hibernate使用xml配置映射关系,这就是一种声明,框架使用这个声明实现对象与数据库记录的转换操作,其它的例如流程引擎、一些MVC框架等等。
    一般通过反射或者代码生成实现,缺点:a) 表达能力的不足可能使模型受限,开发束手束脚;b) 代码生成的破坏性可能扰乱迭代过程。
    其它的形式:Domain-Specific Language, From the Ground Up (一些函数编成方法)等。

Chapter Eleven. Applying Analysis Patterns
    实例说明accounting model分析模式的使用。

Chapter Twelve. Relating Design Patterns to the Model
    说明领域模式和设计模式的区别:领域模式为领域问题提供模型,设计模式侧重于代码实现层面。
    拿Strategy, Composite模式示例了一下。

Chapter Thirteen. Refactoring Toward Deeper Insight
    低级的重构是觉得代码实现不合理,refactory一下。深层次的重构指领域模型层面,不断与领域专家对话,查看领域文献,参考分析模式等成熟模型。
    They see the risk of changing code and the cost of developer time to make a change; but what's harder to see is the risk of keeping an awkward design and the cost of working around that design. 这句话说的很好。

Part IV: Strategic Design
    把整个系统的设计图画在一面墙上,估计没有几个人能看懂;协作开发过程中,边界上的约束、断言等清晰程度,影响着开发过程控制和质量。因此在较高的层次,一些策略型的措施必不可少。

Chapter Fourteen. Maintaining Model Integrity
    领域驱动设计 Domain-Driven Design
    图:A navigation map for model integrity patterns

    作者蜻蜓点水的提到下面这样一些开发场景,如何控制开发方式,确保模型的完整性,提供一点参考:
    1. Bounded Context: 两个团队协作开发,模型的重叠/交互部分,边界上下文是什么?两个团队都清楚吗?
    duplicate concepts: 同一个概念在两个团队中存在不同的实现。false cognates: 对某个东西,两个团队以为理解是一致的,实际上存在分歧/偏差而没有被察觉。
    2. Continuous Integration: 不是直到最后期限才进行集成,协作开发过程中分步骤地进行集成、测试,降低风险。但它会带来一些额外的成本开销。
    3. Context Map: 透过边界的代码复用、数据功能的集成是一种风险,应当使用一个转换实现,提供一个全局的上下文映射视图和文档说明,集中管理,避免边界上下文的混乱。
    4. Shared Kernel: 将两个团队模型重叠部分的一些子集作为共享部分维护。
    5. Customer/Supplier Development Teams: 协作团队之间存在上、下游关系,典型的一些情况是底层框架开发团队与具体应用开发团队之间。
    6. Conformist: 上下游团队不是同一个部门,或者由不同的人领导。
    7. Anticorruption Layer: 与外部系统集成时隔离层的设计。
    8. Separate Ways: 尽量避免集成。
    9. Open Host Service: 过多的子系统集成不能都使用转换来实现,应当提供标准的协议/服务。
    10. Published Language: 两个系统间的集成,实用任何一方的模型可能都不是理想的,采用一种标准/规范的交互协议可能是更好的选择。
    11. Unifying an Elephant: 多个模型的集成问题。

Chapter Fifteen. Distillation

Chapter Sixteen. Large-Scale Structure

Chapter Seventeen. Bringing the Strategy Together

    精髓思想在前面几章中,后面的实在看不下去了。前面描述的是DDD思想,实例说明,以及一些方法论上的东西。后面部分侧重在一些实现、项目管理过程控制层面,尽管是作者的一些宝贵经验之谈,但这种发散性的解说应用于实际项目状况是有待推敲的事情了。更何况很多情况,大家使用不同的设计思想、开发模式时,都有遇到和处理过。
    花了一整天看完,希望能给大家一些参考。