第一章:让模型发挥作用

  领域驱动设计是一种开发复杂软件的方法,我们在其中:

  1. 专注于核心领域
  2. 通过领域从业者和软件从业者的创造性协作探索模型。
  3. 在明确有界的上下文中讲一种无处不在的语言

  这个DDD的三点摘要取决于术语的定义,这些术语在本手册中定义。

  许多项目最终进行建模工作而没有获得太多实际利益。DDD的模式从项目中提炼成功实践,这些项目从建模中获得了巨大的好处。总之,他们提出了一种完全不同的建模和软件开发方法,从精细的细节到高级的愿景。严格的建模惯例必须与非技术人员合作,自由探索模型。战术和战略必须结合起来才能成功,DDD同时解决战术和战略设计问题。

有界上下文

  任何大型项目都有多种模型可供使用。他们出现的原因有很多。两套子系统通常服务于非常不同的用户社区,具有不同的工作,其中不同的模型可能是有用的。独立工作的团队由于缺乏沟通其实以不同的方式解决了同样的问题,工具集也可能不同,这意味着无法共享程序代码。

  多个模型是不可避免的,但是当基于不同模型的代码组合在一起时,软件变得越来越不可靠,不可靠并且难以理解,团队成员之间的沟通变得困惑。通常不清楚在什么上下文下不该应用哪种模型。模型表达式与自然语言中任何其他短语一样,仅在上下文中具有意义。

因此:

  明确定义模型应用的上下文。根据团队组织,应用程序特定部分的使用情况以及代码库和数据库模式等物理表现形式明确设置边界。应用持续集成以保持模型概念和术语在这些范围内严格一致,但不要被外部问题分心或混淆。在上下文中标准化在单个开发过程,不需要在其他地方使用。


无所不在的语言

For first you write a sentence,

And then you chop it small; 
Then mix the bits, and sort them out 
Just as they chance to fall: 
The order of the phrases makes 
No difference at all.

-Lewis Carroll,“Poeta Fit,Non Nascitur”

  要创建一个灵活的,知识丰富的设计,需要一种多功能,共享的团队语言,以及在软件项目中很少使用的语言的生动实验。

  在一个有界的上下文中,语言可能会破坏应用复杂建模的努力。如果该模型仅用于为团队的技术成员绘制UML图表,那么它不会为DDD的核心创意协作做出贡献。

  领域专家使用他们的行话,而技术团队成员有自己的语言在设计方面讨论领域,日常讨论的术语与代码中嵌入的术语(最终是软件项目中最重要的产品)脱节,甚至同一个人在言语和写作中使用不同的语言,因此领域中最精辟的表达通常以一种从未在代码中甚至在写作中捕获的瞬态形式出现。

  翻译使沟通变得迟钝,使知识变得贫穷。

  然而,这些方言中没有一种可以成为共同语言,因为没有一种方言满足所有需要:

  领域专家应该反对那些尴尬或不足以传达领域理解的术语或结构; 开发人员应注意会导致设计绊倒的模糊或不一致。

  在谈论系统时,请使用模型。使用模型的元素和交互来大声描述场景,以模型允许的方式组合概念。找到更简单的方式来说出您需要说的内容,然后将这些新想法反馈到图表和代码中。

  使用无处不在的语言,该模型不仅仅是一个设计工件。它成为开发人员和领域专家共同努力的一部分。

因此:
  使用模型作为语言的支柱。让团队在团队内部和代码中的所有沟通中坚持不懈地运用该语言。在有界的上下文中,在图表,书写,尤其是语音中使用相同的语言。

  认识到语言的变化是模型的变化。

  通过尝试反映替代模型的替代表达来消除困难。然后重构代码,重命名类,方法和模块以符合新模型。解决对话中术语的混淆,就像我们就普通词的含义达成一致一样。


持续集成

  一旦定义了有界上下文,我们必须保持完整。

  当许多人在相同的有界上下文中工作时,模型很有可能破碎。团队越大,问题就越大,但只有三四个人会遇到严重的问题。然而,将系统分解为越来越小的上下文最终会失去宝贵的整合和一致性。

因此:

  建立一个频繁合并所有代码和其他实现工件的流程,使用自动化测试快速标记碎片。随着概念在不同人的头脑中演变,坚持不懈地运用无处不在的语言来敲定模型的共同视图。


模型驱动设计

  将代码与基础模型紧密关联可以得到代码含义并使模型相关。

  如果设计或其中心部分未映射到域模型,则该模型价值不大,并且软件的正确性是可疑的。同时,模型和设计功能之间的复杂映射难以理解,并且在实践中,随着设计的变化无法维护。分析与设计之间存在致命的鸿沟,因此在每项活动中获得的洞察力都不会影响到另一项活动。

  从模型中抽取设计中使用的术语和责任的基本分配。代码成为模型的表达式,因此对代码的更改可能是对模型的更改。它的影响必须相应地影响项目的其余部分。

  将具体实现与模型联系起来通常需要软件开发工具和支持建模范例的语言,例如面向对象的编程。

因此:

  设计软件系统的一部分以非常直观的方式反映域模型,因此映射是显而易见的。重新审视模型并对其进行修改以便在软件中更自然地实现,即使您希望使其更深入地了解域。除了支持流利的无处不在的语言外,还需要一个能够很好地满足这两个目的的模型。


建模者

  如果编写代码的人不对模型负责,或者不了解如何使模型适用于应用程序,那么该模型就会与软件无关。如果开发人员没有意识到更改代码会改变模型,那么他们的重构将削弱模型而不是加强模型。同时,当建模者与实施过程分离时,他或她从未获得或很快失去对实施约束的感觉。模型驱动设计的基本约束 - 模型支持有效实现并将关键洞察力抽象到域中 - 已经消失了一半,结果模型将是不切实际的。最后,

因此:

  任何为模型做出贡献的技术人员都必须花一些时间来触摸代码,无论他或她在项目中扮演什么角色。负责更改代码的任何人都必须学会通过代码表达模型。每个开发人员都必须参与有关模型的某种级别的讨论,并与领域专家联系。那些以不同方式做出贡献的人必须有意识地通过普遍存在的语言与那些触摸代码的人进行动态的模型思想交流。

banq补充举例:下面是订单的模型图,使用UML表达:

对应的代码类如下:

@Table("order_table")
public class Order {

private Collection<OrderItem> items;
private OrderStatus m_OrderStatus;
private Address m_Address;
@org.springframework.data.annotation.Id
private String Id;

public Order() {
items = new ArrayList<>();
m_OrderStatus = new OrderStatus(0);
}

public void addItem(OrderItem item) {
items.add(item);
}

public Address getM_Address() {
return m_Address;
}

public void setM_Address(Address m_Address) {
this.m_Address = m_Address;
}

public Collection<OrderItem> getItems() {
return items;
}

public void setItems(Collection<OrderItem> items) {
this.items = items;
}

public OrderStatus getM_OrderStatus() {
return m_OrderStatus;
}

public void switchState() {
if (getM_OrderStatus().getState() == 0) {
this.m_OrderStatus = new Payment();
} else if (getM_OrderStatus().getState() == 1) {
this.m_OrderStatus = new Delivery();
}
}

public String getId() {
return Id;
}

public void setId(String id) {
Id = id;
}
}

保持这两者统一,因为他们都是同一个模型的不同表示方法,前者是UML图,后者是Java代码,Java代码会因具体数据库持久方式不同

对模型实现类有所不同,比如这里使用Spring-data-jdbc,而不是JPA,注意到Order类上面的@Table() 和@org.springframework.data.annotation.Id,在具体后续实现中,开发人员可能会修改Order模型代码,这需要及时反映到Order的UML模型图那里。不同的技术框架有不同的技术影响和污染,下图是Order模型在Spring Boot中微服务实现结构:


重构更深入的洞察力

  使用经过验证的基本构建块以及一致的语言为开发工作带来了一些理智。这留下了真正找到一个精辟模型的挑战,这个模型捕捉到领域专家的微妙关注,并可以推动实际设计。一个模糊的表面和捕捉必要的模型是一个深层模型。这应该使软件更符合领域专家的思维方式,并更好地响应用户的需求。

  传统上,重构是根据具有技术动机的代码转换来描述的。重构也可以通过洞察域以及模型的相应细化或其在代码中的表达来激发。

  复杂的域模型很少有用,除非通过重构的迭代过程开发,包括域专家与有兴趣了解域的开发人员的密切参与。

鲍勃大爷:先设计对象的行为,再设计数据库的表结构!

第二章:模型驱动设计MDA的实现

领域驱动设计参考