关闭

IDDD 实现领域驱动设计-一个简单业务用例的回顾和理解

发表于:2015-5-22 15:35

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:田园里的蟋蟀    来源:51Testing软件测试网采编

  这篇博文是对《实现领域驱动设计》第一章后半部分内容的理解。
  Domain Experts-领域专家
  这节点内容是昨天的一个讨论引发的思考。
  什么是领域专家?简单来说,就是对某一业务领域精通的人,这个人可以是医生、学者、作家、艺术家等等,不管是什么职业,什么身份,只要对某一业务领域精通,都可以称之为领域专家。这样说可能会让你感到茫然,我举一个例子,比如你们软件公司要开发一套快递行业的业务系统,然后你需要到实际企业去了解业务流程等等,暂时把这个实际企业想象成很小(非三通一达),那么你到这个企业第一时间找的是谁呢?准确来说,应该是这个公司的 CEO,因为只有他最最了解他们公司的业务,毕竟是他创办的公司,CEO 不了解,还有谁还了解呢,那么,这个公司的 CEO 就可以看作是领域专家。CEO 一般是蛮忙的,有很多的琐事需要处理,所以,在你和他聊天了解业务的时候,最好是先准备一杯咖啡!
  当我们开发人员自己开发一套系统的时候,在开发团队之间,领域专家的概念就慢慢淡化了,为什么?因为领域专家变成了我们开发人员自己,自己给自己布置业务,然后自己再去完成,这样虽然很高效,因为没有非技术人员的参与沟通,但是这样就会造成一些问题,比如,开发人员在思考业务流程的时候,会按照开发人员的思路去理解,比如,一个简单的业务操作描述,开发人员会首先想到的什么呢?一个表单和一个 Button,然后就是对这个表单和 Button 操作的具体实现了,等项目开发完成后,需要交付真正的客户去检验,客户让你演示这个业务操作,然后你就开始对表单和 Button 进行操作了,说这就是业务操作,但是,客户突然来一句:我们不要表单和 Button 操作,UI 需要重新搞,这时候,你就傻眼了,因为你所有的内容代码实现都是围绕着表单和 Button。说了这么多,到底是什么意思呢?在这个过程中,你并不了解这个业务操作背后所蕴含的业务含义,首先,业务不是 UI,UI 只不过是业务的一部分体现,有时候,业务仅仅只是领域专家的一段描述,开发人员需要对这个业务描述,进行一点一点的抽离,把术语和操作分离开,然后再和领域专家进行深入的探讨,这个过程可能会花很多的时间,但是是非常重要的,做完这些前期工作,你再去实现业务操作,你会发现,不管 UI 如何变化,这个业务操作的本质是没有发生变化的,也就是说你的内部代码不需要进行修改,UI 修改那就交给前端工程师就可以了,和你没太大关系。总的来说,就是不要让 UI 驱动你开发,而是让业务驱动你开发。
  对上面的内容,我还需要补充一点,就是开发人员需要领域专家,开发人员和领域专家的身份最好不要重叠,要不然会造成一系列的问题,还有就是,在整个领域驱动设计的过程中,开发人员和领域专家的地位是相同的,不要有任何的轻视心态,要用平等的心态去沟通交流。领域专家的概念,让我想到一个很相似的事,就是苹果在开发一个产品的时候,会请很多的非技术人员参与,这些人遍布各行各业,医生、学者、作家、艺术家等等,苹果为什么要请他们,就是想让他们参与产品的设计,因为他们就是产品的使用者,他们提出的想法就是实实在在的用户建议,这个产品开发过程,其实就可以看作是领域(产品)驱动设计,这些参与产品设计的非技术人员,就可以看作是领域(产品)专家。
  一个简单业务用例的回顾和理解
  这个简单业务用例描述是这样的:一个 Scrum 模型,我们需要将一个待定项(Backlog Item)提交到冲刺(Sprint)中去。
  这是最简答的描述,没有经过和领域专家进行深入沟通的,Scrum 是敏捷开发中的概念,这个就不说明了,因为我也不懂,你只需要知道上面的操作就可以了,一般的实现方式(属性访问):
public class BacklogItem extends Entity {
private SprintId sprintId;
private BacklogItemStatusType status;
...
public void setSprintId(SprintId sprintId) {
this.sprintId = sprintId;
}
public void setStatus(BacklogItemStatusType status) {
this.status = status;
}
...
}
  客户端调用:
  // client commits the backlog item to a sprint
  // by setting its sprintId and status
  backlogItem.setSprintId(sprintId);
  backlogItem.setStatus(BacklogItemStatusType.COMMITTED);
  上面的实现过程,完全和上一篇 saveCustomer 的实现方式一样,这样做没什么不可以,因为我也这样干过,只是你会总感觉有哪些不对劲的地方,首先,在实现待定项提交到冲刺这个操作的时候,你首先查看的是 BacklogItem 中的属性,然后就是对这个属性进行设置,在这个过程中,你忘记了你实现的是一个行为操作,而不是一个属性赋值操作,这样说来,是不是有点脚本模式开发,还有就是如果客户端第二个属性赋值 setStatus 出现了错误,因为第一个 setSprintId 已经成功完成,这个该怎么进行处理,即使有处理,这个操作也完全放在了客户端去完成,像 saveCustomer 一样,如果再增加一个属性赋值操作,你的实现将越改越乱,最重要的是,再客户端暴露了 BacklogItem 模型的具体结构,这个应该是要避免的。
  我们再来看另一种实现方式:
public class BacklogItem extends Entity {
private SprintId sprintId;
private BacklogItemStatusType status;
...
public void commitTo(Sprint aSprint) {
if (!this.isScheduledForRelease()) {
throw new IllegalStateException(
"Must be scheduled for release to commit to sprint.");
}
if (this.isCommittedToSprint()) {
if (!aSprint.sprintId().equals(this.sprintId())) {
this.uncommitFromSprint();
}
}
this.elevateStatusWith(BacklogItemStatusType.COMMITTED);
this.setSprintId(aSprint.sprintId());
DomainEventPublisher
.instance()
.publish(new BacklogItemCommitted(
this.tenant(),
this.backlogItemId(),
this.sprintId()));
}
...
}
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号