IDDD 实现领域驱动设计-一个简单业务用例的回顾和理解
作者:网络转载 发布时间:[ 2015/5/22 15:46:56 ] 推荐标签:测试用例
这篇博文是对《实现领域驱动设计》第一章后半部分内容的理解。
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()));
}
...
}
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11