复仇者不联盟

领域驱动设计(一)

2018-02-02  本文已影响36人  MaxZing

领域驱动设计(Domain Driven Design)一下简称DDD
DDD是为了创造一个可测试的,可伸缩的,组织良好的软件模型。

一,采用DDD的原因:

二,DDD主要关注三个方面:

  1. 将开发和产品设计聚集在一起,可以反映出产品是设计思维模型
  2. 关注业务战略,包含战略和战术设计。从而实现面向服务架构和业务驱动架构
  3. 通过建模工具,将领域设计转换成软件,同时设计出的软件可测试,具有良好的伸缩性,允许分布式计算

未经过DDD,导致出现大量的贫血对象
贫血对象:在领域中,有些对象几乎没有业务操作,并且Getter 和Setter大部分是为了映射数据库里的领域模型。

  • 主要是些公有的getter和setter方法, 并且几乎没有 业务逻辑,或者甚至完全没有业务逻辑——对象嘛,主要就是用来容纳屈 性值的?
  • 软件组件经常使用的领域对象是否包含了系统主要的业务逻辑,并且多数 情况下你需要调用那些getter和setter? 你可能会将这样的客户代码称为服务层(Service Layer)或者应用层(Application Layer) (4, 14)代码。

——摘自《领域驱动设计》

大段大段的setter方法让人崩溃

customer.setCustomerFirstName(customerFirstName); 
customer.setCustomerLastName(customerLastName); 
customer.setStreetAddressl(streetAddressl);
customer.setStreetAddress2(streetAddress2);
customer.setCity(city); 
customer.setStateOrProvince(stateOrProvince);
customer.setPostalCode(postalCode); 
customer.setCountry(country); 
customer.setHomePhone(homePhone); 
customer.setMobilePhone(mobilePhone); 
customer.setPrimaryEmailAddress(primaryEmailAddress); 
customer.setSecondaryEmailAddress (secondaryEmailAddress); 

如果这时候有一个保存方法

saveCustomer(customer);

咋一看,是保存客户信息。但是在领域中,你不知道业务是何时使用这个方法的,即无法映射实际的业务场景。

然后过一段时间,customer的属性发生了变化,添加了一两个属性,并且属性的参数需要符合一定的要求,就会出现下列代码:

if (customer == null) {
  customer = new Customer(); 
  customer.setCustomerid(customerid);
}
if (customerFirstName 1= null) {
  customer.setCustomerFirstName(customerFirstName); 
}
if (customerLastName ! = null) { 
  customer.setCustomerLastName(customerLastName); 
}
...
...
...

这种代码已经表示数据-映射开始变糟了。你已经不清楚什么时候参数是必须的,该不该保存customer。
而且随着时间的流逝,方法变化的来龙去脉越来越模糊,弄清楚需要花费大量的时间。

三,DDD 的《通用语言》

通用语言是用来交流领域中(业务),行业专家和程序编写者沟通交流的语言,既不是行业专家使用的专业术语,也不是专门的业务语言。通用语言主要是方便团队中的行业专家以及开发人员沟通,比如,模型图,领域文档等(包含领域中术语与术语含义的解释,通常有个表格)

按照通用语言的业务划分,不应该出现贫血对象,那么上述代码在DDD的指导下会变成:

public interface Customer { 
  void changePersonalName( String firstName, String lastName); 
  void postalAddress(PostalAddress postalAddress); 
  void relocateTo(PostalAddress changedPostalAddress); 
  void changeHomeTelephone(Telephone telephone); 
  void disconnectHomeTelephone(); 
  void changeMobileTelephone(Telephone telephone); 
  void disconnectMobileTelephone (); 
  void primaryEmailAddress(EmailAddress emailAddress); 
  void secondaryEmailAddress(EmailAddress emailAddress); 
}

这样,业务就会体现在接口中,而方法对应的应用层也会随之变化:

@Transactional
public void changeCustomerPersonalName( String customerld, String customerFirstName, String customerLastName) { 
  Customer customer = customerRepository.customerOfId(customerid); 
  if (customer == null) {
    throw new IllegalstateException("Customer does not exist");
  }
  customer.changePersonalName(customerFirstName, customerLastName); 
}

你会说,这个方法太单一了。没错,在DDD中,参照模型对数据修改,不需要使用一个方法,然后后面跟上一大堆null去判断了。

通用语言不是万能的,而是在领域中普遍的,表达清楚的;一般会有领域界限上下文限定,如果想映射到其他领域中,大部分情况会失败

作为开发者,我们都是技术思想者,技术实现对于我们来说并不是什么难事。我并不是说技术地思考不好,只是说有时少从技术层面去思考会更好。这么多年来,我们都习惯了单从技术层面完成软件开发,那么现在,是时候考虑一种新的思考方式了。为你的业务领域开发一门通用语言是一个好的出发点。

一 一 实现领域驱动设计

上一篇下一篇

猜你喜欢

热点阅读