读书笔记: 领域驱动设计

https://book.douban.com/people/fleure/annotation/5344973/

创建 Segregated Core 的代价

<原文开始>有时候,把 core 分离出来会使得它与那些紧密耦合的非 core 类的关系变得更晦涩,甚至更复杂,但 core domain 更清晰了,而且更易于处理,因此获得的好处还是足以抵偿这种代价。 Segregated Core 使我们能够提高 core domain 的内聚性。.. 有时在创建 segregated core 时,可以把一个内聚性很好的 module 拆分开,通过牺牲这种内聚性来换取 core domain 的内聚性。这样做是值得的,因为企业软件的最大价值来自于模型中企业的那些特有方面。 </原文结束> <原文开始>当系统有一个很大的、非常重要的 bounded context 时,但模型的关键功能被大量支持性的功能掩盖了,那么就需要创建 Segegated Core 了。</原文结束> ## 模式:Generic Subdomain <原文开始>识别出那些与项目意图无关的内聚子领域。把这些子领域的通用模型提取出来,并放到单独的 Module 中。任何专有的东西都不应放在这些模块中。 把它们分离出来以后,在继续开发的过程中,它们的优先级低于 core domain 的优先级,并且不要分派核心开发人员来完成这些任务(因为他们很少能够从这些任务中获取领域知识)。此外,还可以考虑为这些 Generic Subdomain 使用现成的解决方案或者“公开发布的模型”(Published Model)。 </原文结束> 比如账户认证这类。 ## 选择重构目标 <原文开始>当你遇到一个杂乱无章的大系统时,应该从哪里入手呢?在 XP 社区中,答案往往是以下之一: 1. 可以从任何地方开始,因为所有的东西都要重构; 2. 从影响你工作的那部分开始——也就是完成具体任务所需要的那个部分; 这两种做法我都不赞成。第一种做法并不可行,只有少数完全由顶尖的程序员组成的团队才是例外。第二种做法往往只是对外围问题进行了处理,只治标而不治本,回避了最严重的问题。最终这会使得代码变得越来越难以重构。 因此,如果你既不能全面解决问题,又不能“哪儿痛治哪儿”,那么该怎么办呢? 1. 如果采用“哪儿痛治哪儿”这种重构策略,要观察一下根源问题是否涉及 core domain 或 core 的支持元素的关系。如果确实涉及,那么就要接受挑战,首先修复核心。 2. 当可以自由选择重构的部分时,应首先集中精力把 core domain 更好地提取出来,完善对 core 的分离,并且把支持性德子领域提炼成通用子领域。 以上就是如何从重构中获取最大利益的方法。 </原文结束> ## 深层模型/柔性设计 <原文开始>如果每次对模型和代码所进行的修改都能反映出对领域的新理解,那么通过不断的重构就能给系统最需要修改的地方增添灵活性,并找到简单快捷的方式来实现普通的功能。戴久了的手套在手指关节处会变得柔软;而其他部分则依然硬实,可起到保护的作用。</原文结束> <原文开始>深层模型使设计更具表现力;同时,当设计的灵活性可以让开发人员进行试验,而设计又能清晰地表达出领域含义时,那么这个设计实际上就能够将开发人员的深层理解反馈到整个模型发现的过程中。</原文结束> ## 模式: Module <原文开始>分层架构可能导致模型对象实现的分裂。</原文结束> <原文开始>当使用 J2EE 早期版本时,一种常见的做法是把数据和数据访问放到“实体bean”中,而把相关的业务逻辑放到“会话bean”中。这样做除了导致每个组件的实现变得更复杂以外,还破坏了对象模型的内聚性。</原文结束> <原文开始>有时,方面的分离也是有帮助的,具体来讲,把持久化代码移出来可以减少很多混乱。</原文结束> <原文开始>但最重要的是,这个项目的框架要求将每个层放到单独的一组包中,并根据层的标识惯例来命名。这一下子就把我们所有的注意力都吸引到分层上来。</原文结束> <原文开始>这种框架设计是再尝试解决两个合理的问题。一个问题是关注点的逻辑划分:一个对象负责数据库访问,另外一个对象负责处理业务逻辑,等等。这种划分方法是人们更容易(在技术层面上)理解每个层的功能,而且更容易切换各个层。这种设计的问题在于没有顾及应用程序的开发成本。</原文结束> <原文开始>这些打包方案的另一个动机是层的分布。如果代码实际上被部署到不同的服务器上,那么这会成为这种分层的有力论据。但通常并不是这样,应该在需要时才寻求灵活性。</原文结束> <原文开始>除非真正有必要讲代码分布到不同的服务器上,否则就把实现单一概念对象的所有代码放在同一个模块中。</原文结束> ## 模式:Service <原文开始>所谓 Service,它强调的是与其他对象的关系。与 Entity 与 Value Object 不同,它只是定义了能够为客户做什么,Service 往往是以一个活动来命名,而不是以一个 Entity 来命名。</原文结束> <原文开始>好的 Service 有以下三个特征: - 与领域概念相关的操作不是 Entity 或 Value Object 的一个自然组成部分。 - 接口是根据领域模型的其他元素定义的。 - 操作是无状态的。 </原文结束> ## 粒度 <原文开始>由于应用层负责对领域对象的行为进行协调,因此细粒度的领域对象可能会把领域层的知识泄露到应用层中。这产生的结果是应用层不得不处理复杂的、细致的交互,从而使领域知识蔓延到应用层或用户界面代码中,而领域层会丢失这些知识。</原文结束> ## 关联 <原文开始>尽可能地对关系进行约束是非常重要的。双向关联意味着只有将这两个对象放在一起考虑才能理解它们。当应用程序不要求双向遍历时,可以指定一个遍历方向,以便减少相互依赖,并简化设计。</原文结束> ## “大声地”建模 <原文开始>改善模型的最佳方式之一就是通过对话来研究,试着大声说出可能的模型变化中的各种结构。这样不完善的地方很容易被听出来。</原文结束> <原文开始>事实上,我们的大脑似乎很擅长处理口语的复杂性。例如,当具有不同语言背景的人凑在一起做生意时,如果没有公共语言,他们就会创造一种称为“混杂语”的公共语言。混杂语虽然不想讲话者的母语那样详尽,但它适合当前任务。</原文结束> ## 文档和图 <原文开始>尽管存在所有这些细节,但属性和关系只是对象模型的一部分。这些对象的行为以及这些对象上的约束就不那么容易表示了。对象交互图可以阐明设计中的一些复杂之处,但却无法用这种方式来展示大量的交互,就是工作量太大了,既要制作图,还要学习这些图而且交互图也只能暗示出模型的目的。要想把约束和断言包括进来,需要在 UML 中使用文本。</原文结束> <原文开始>操作名称可能会暗示出对象的行为职责,对象交互图中也会隐含地展示出这些职责,但无法直接表述。</原文结束> <原文开始>如果确实能使用 UML 这样的绘图语言来编写可执行程序,那么 UML 图就会退化为程序本身的另一种视图,这样,“模型” 的真正含义就丢失了。</原文结束> <原文开始>图是一种沟通和解释手段,它们可以促进头脑风暴。简洁的小图能够很好地实现这些目标,而涵盖整个对象模型的综合性大图反而失去了沟通或解释能力</原文结束> <原文开始>设计的重要细节应该在代码中体现出来。良好的实现应该是透明的,清楚地展示其背后的模型。</原文结束> ## 知识丰富的设计 <原文开始>当我们的建模不再局限于寻找实体和值对象时,我们才能充分吸取知识,因为业务规则之间可能会存在不一致。领域专家在反复研究所有规则、解决规则之间的矛盾以及常识来弥补规则的不足等一系列工作中,往往不会意识到她们的思考过程有多么复杂。</原文结束>