【DDD】学习笔记-软件开发团队的沟通与协作

领域驱动设计的核心是“领域”,因此要运用领域驱动设计,从一开始就要让团队走到正确的点儿上。当我们组建好了团队之后,应该从哪里开始?不是 UI 原型设计、不是架构设计、也不是设计数据库,这些事情虽然重要但却非最高优先级。试想,项目已经启动,团队却并不了解整个系统的目标和范围,未对系统的领域需求达成共识,那么项目开发的航向是否会随着时间的推移而逐渐偏离?用正确的方法做正确的事情,运用领域驱动设计,就是要先识别问题域,进而为团队提炼达成共识的领域知识

要做到这一点,就离不开团队各个角色的沟通与协作。客户的需求不是从一开始就生长在那里的,就好像在茫茫森林中的一棵树木,等待我们去“发现”它。相反,需求可能只是一粒种子,需要土壤、阳光与水分,在人们的精心呵护与培植下才能茁壮成长。因此,我们无法“发现”需求,而是要和客户一起“培育”需求,并在这个培育过程中逐渐成熟。

达成共识

“培育”需求的过程需要双向的沟通、反馈,更要达成对领域知识理解的共识。原始的需求是“哈姆雷特”,每个人心中都有一个“哈姆雷特”,如果没有正确的沟通与交流方式,团队达成的所谓“需求一致”不过是一种假象罢了。

由于每个人获得的信息不同,知识背景不同,又因为角色不同因而导致设想的上下文也不相同,诸多的不同使得我们在对话交流中好像被蒙了双眼的盲人,我们共同捕捉的需求就好似一头大象,各自只获得局部的知识,却自以为掌控了全局:

或许有人会认为客户提出的需求就应该是全部,我们只需理解客户的需求,然后积极响应这些需求即可。传统的开发合作模式更妄图以合同的形式约定需求知识,要求甲、乙双方在一份沉甸甸的需求规格说明书上签字画押,如此即可约定需求内容和边界,一旦发生超出该文档边界的变更,就需要将变更申请提交到需求变更委员会进行评审。这种方式从一开始就站不住脚,因为我们对客户需求的理解,存在三个方向的偏差:

  • 我们从客户那里了解到的需求,并非用户最终的需求;
  • 若无有效的沟通方式,需求的理解偏差则会导致结果大相径庭;
  • 理解到的需求并没有揭示完整的领域知识,从而导致领域建模与设计出现认知障碍。

Jeff Patton 在《用户故事地图》中给出了一副漫画来描述共识达成的问题。

这幅漫画形象地表现了如何通过可视化的交流形式逐渐在多个角色之间达成共识的过程。正如前面所述,在团队交流中,每个人都可能成为“盲人摸象的演员”。怎么避免认知偏差?很简单,就是要用可视化的方式表现出来,例如,绘图、使用便签、编写用户故事或测试用例等都是重要的辅助手段。在下一课,我会结合着领域场景分析来讲解这些提炼领域知识的手段。

可视化形式的交流可以让不同角色看到需求之间的差异。一旦明确了这些差异,就可以利用各自掌握的知识互补不足去掉有余,最终得到大家都一致认可的需求,形成统一的认知模型。

团队协作

在软件开发的不同阶段,团队协作的方式与目标并不相同。在项目的先启(Inception)阶段,团队成员对整个项目的需求完全一无所知,此时与客户或领域专家的沟通,应该主要专注于宏观层面的领域知识,例如,系统愿景和目标、系统边界与范围,还有主要的需求功能与核心业务流程。在管理层面,还需要在先启阶段确定团队与利益相关人(包括客户与领域专家)的沟通方式。

先启阶段

在敏捷开发过程中,我们非常重视在项目之初开展的先启阶段,尤其是有客户参与的先启阶段,是最好的了解领域知识的方法。如果团队采用领域驱动设计,就可以在先启阶段运用战略设计,建立初步的统一语言,在识别出主要的史诗级故事与主要用户故事之后,进而识别出限界上下文,并建立系统的逻辑架构与物理架构。

在先启阶段,与提炼领域知识相关的活动如下图所示:

上图列出的七项活动存在明显的先后顺序。首先我们需要确定项目的利益相关人,并通过和这些利益相关人的沟通,来确定系统的业务期望与愿景。在期望与愿景的核心目标指导下,团队与客户才可能就问题域达成共同理解。这时,我们需要确定项目的当前状态与未来状态,从而确定项目的业务范围。之后,就可以对需求进行分解了。在先启阶段,对需求的分析不宜过细,因此需求分解可以从史诗级(Epic)到主故事级(Master)进行逐层划分,并最终在业务范围内确定迭代开发需要的主故事列表。

迭代开发阶段

在迭代开发阶段,针对迭代生命周期和用户故事生命周期可以开展不同形式的沟通与协作。在这个过程中,所有沟通协作的关键点如下图所示:

迭代生命周期是针对迭代目标与范围进行需求分析与沟通的过程。团队首先要了解本次迭代的目标,对迭代中的每个任务要建立基本的领域知识的理解。在迭代开发过程中,我们可以借鉴 Scrum 敏捷管理的过程。

Scrum 要求团队在迭代开始之前召开计划会议,由产品负责人(Product Owner)在会议中向团队成员介绍和解释该迭代需要完成的用户故事,包括用户故事的业务逻辑与验收标准。团队成员对用户故事有任何不解或困惑,都可以通过这个会议进行沟通,初步达成领域知识的共识。每天的站立会议要求产品负责人参与,这就使得开发过程中可能出现的需求理解问题能够及时得到解答。Scrum Master 则通过每天的站立会议了解当前的迭代进度,并与产品负责人一起基于当前进度和迭代目标确定是否需要调整需求的优先级。迭代结束后,团队需要召开迭代演示会议,除了开发团队之外,该会议还可以邀请客户、最终用户以及领域专家参与,由团队的测试人员演示当前迭代已经完成的功能。这种产品演示的方法更容易消除用户、客户、领域专家、产品负责人与团队在需求沟通与理解上的偏差。由于迭代周期往往较短,即使发现了因为需求理解不一致导致的功能实现偏差,也能够做到及时纠偏,从而能够将需求问题扼杀于摇篮之中。

每一个功能的实现、每一行代码的编写都是围绕着用户故事开展的,它是构成领域知识的最基本单元。用户故事指导着开发人员的开发、测试人员的测试,其质量会直接影响领域驱动设计的质量。

敏捷方法非常重视发生在用户故事生命周期中的各个关键节点。对于用户故事的编写,敏捷开发实践强调业务分析人员与测试人员共同编写验收测试的自动化测试脚本,这在《实例化需求》一书中被称之为“活文档(Living Document)”。测试人员与需求分析人员的合作,可以为需求分析提供更多观察视角,尤其是异常场景的识别与验收标准的确认。

当用户故事从需求分析人员传递给开发人员时,不管这个用户故事的描述是多么的准确和详细,都有可能导致知识流失。因此,在开发人员领取了用户故事,并充分理解了用户故事描述的需求后,不要急匆匆地开始编码实现,而是建议将需求分析人员与测试人员叫过来,大家一起做一个极短时间的沟通与确认,我们称这一活动为“Kick Off”,这种方式实际就是对“盲人摸象”问题的一种应对。在这个沟通过程中,开发人员应尽可能地多问需求分析人员“为什么”,以探索用户故事带来的价值。只有如此,开发人员才能更好地理解业务逻辑与业务规则。同时,开发人员还要与测试人员再三确认验收标准,以形成一种事实上的需求规约。

当开发完成后,是否就意味着我们可以将实现的故事卡移交给测试呢?虽然通过迭代开发以及建立特性团队已经大大地拉近了开发人员与测试人员的距离,缩短了需求从开发到测试的周期。但我们认为,有价值的沟通与交流怎么强调都不过分!磨刀不误砍柴工。我们认为从开发完成到测试开始也是一个关键节点,建议在这个关键节点再进行一次交流活动,即在开发环境下,由开发人员向需求分析人员与测试人员“实地”演示刚刚完成的功能,并对照着验收标准进行验收,我们称这个过程为“Desk Check”,是一个快速迷你的功能演示,目的是快速反馈,也减少了任务卡在开发与测试之间频繁切换的沟通成本。

通过 Desk Check 的用户故事卡才会被移动到“待测试”,不用等到迭代结束,更不用等到版本发布,只要开发人员完成了用户故事,测试人员就应该在迭代周期内进行测试,未经过测试的用户故事其交付价值为 0,可以认为这张用户故事卡没有完成,这也是大多数敏捷实践对所谓“完成(Done)”的定义。无数研究与实践也证明了,修改 Bug 的成本会随着时间的推移而增加,如果在开发完成后即刻对其进行测试,一旦发现了 Bug,开发人员便能够快速响应,降低修改 Bug 的成本。当然,测试的过程同样是沟通与交流的过程,是最有效的需求验证和质量保障的手段。

敏捷思想强调个体和团队的协作与沟通,强调快速反馈与及时响应。前面探讨的这些敏捷实践都是行之有效的沟通机制和交流手段,可以帮助团队对需求的理解更加全面、更加准确。只有频繁的沟通,才能就业务需求达成整个团队的共识;只有良好的协作,才能有助于大家一起提炼领域知识,建立统一语言;只有快速反馈,才能尽可能保证领域模型与程序实现的一致。这些都是实践领域驱动设计的基本前提。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/351171.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux常见的管理命令

1. whoami 作用: 显示出当前有效的用户名称,Linux是多用户多任务 语法:whoami(选项) 选项: --help:在线帮助 --version:显示版本信息和退出 场景使用: 1. 当用户想要查看当前登录系统的用户…

时间数据前端显示格式化

背景 在实际我们通常需要在前端显示对数据操作的时间或者最近的更新时间,如果我们只是简单的使用 LocalDateTime.now()来传入数据不进行任何处理那么我们就会得到非常难看的数据 解决方式: 1). 方式一 在属性上加上注解,对日期进行格式…

【Py/Java/C++三种语言详解】LeetCode每日一题240122【贪心】LeetCode670、最大交换

文章目录 题目链接题目描述解题思路为什么是贪心一个带图的例子 代码pythonjavacpp时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 题目链接 LeetCode670、最大交换 题目描述 给定一个非负整数数组 nums 和一个整数 k ,你需要将这个数组分成 k 个非空的连…

深入浅出 diffusion(4):pytorch 实现简单 diffusion

1. 训练和采样流程 2. 无条件实现 import torch, time, os import numpy as np import torch.nn as nn import torch.optim as optim from torchvision.datasets import MNIST from torchvision import transforms from torch.utils.data import DataLoader from torchvision.…

智能分析网关V4智慧冶金工厂视频智能监管方案

一、背景与需求 随着工业4.0的推进,冶金行业正面临着转型升级的压力。为了提高生产效率、降低能耗、保障安全,冶金智能工厂视频监管方案应运而生。该方案通过高清摄像头、智能分析技术、大数据处理等手段,对工厂进行全方位、实时监控&#xf…

matlab appdesigner系列-图窗工具2-工具栏

工具栏,就是一般在任意软件界面上方的工具菜单栏 示例:工具菜单绘制正弦函数 操作步骤如下: 1)将坐标区和工具栏拖拽到画布上 2)点击工具栏的号,可以看到可以添加2种工具,按钮工具和切换工具&#xff0c…

Unity 代理模式(实例详解)

文章目录 实例1:资源加载代理(Asset Loading Proxy)实例2:网络请求代理(Network Request Proxy)实例3:性能优化代理(Performance Optimization Proxy)实例4:权…

LC 2846. 边权重均等查询

2846. 边权重均等查询 难度: 困难 题目大意: 现有一棵由 n 个节点组成的无向树,节点按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] [ui, vi, wi] 表示树中存在一条位于节点 …

银行数据仓库体系实践(11)--数据仓库开发管理系统及开发流程

数据仓库管理着整个银行或公司的数据,数据结构复杂,数据量庞大,任何一个数据字段的变化或错误都会引起数据错误,影响数据应用,同时业务的发展也带来系统不断升级,数据需求的不断增加,数据仓库需…

EventSource 长链接执行

EventSource 说明文档MDN 其他参考文档 一、利用node启服务 import fs from fs import express from express const app express() // eventSource 仅支持 get 方法 // 服务器端发送的数据必须是纯文本格式,不能是二进制数据。 app.get(/api, (req, res) > …

项目性能优化之用compression-webpack-plugin插件开启gzip压缩

背景:vue项目打包发布后,部分js、css文件体积较大导致页面卡顿,于是使用webpack插件compression-webpack-plugin开启gzip压缩 前端配置vue.config.js 先通过npm下载compression-webpack-plugin包,npm i compression-webpack-plug…

Android SharedPreferences源码分析

文章目录 Android SharedPreferences源码分析概述基本使用源码分析获取SP对象初始化和读取数据写入数据MemoryCommitResultcommitToMemory()commit()apply()enqueueDiskWrite()writeToFile() 主动等待写回任务结束 总结 Android SharedPreferences源码分析 概述 SharedPrefer…

EXCEL VBA抓取网页JSON数据并解析

EXCEL VBA抓取网页JSON数据并解析 链接地址: https://api.api68.com/CQShiCai/getBaseCQShiCaiList.do?lotCode10036&date2024-01-26 Sub test() On Error Resume Next Sheet.Select Sheet1.Cells.ClearContents [a1:g1] Split("preDrawIssue|preDrawTi…

Ubuntu20.04添加桌面启动、侧边栏启动和终端启动

桌面启动 新建XX.desktop文件 在桌面新建一个XX.desktop文件,以QtCreator为例。 (注意这里不能使用sudo,因为这样会把文件的权限归为root,导致后续设置可执行程序不方便) gedit qtcreator.desktop在XX.desktop文件中…

第14次修改了可删除可持久保存的前端html备忘录:增加一个翻牌钟,修改背景主题:现代深色

第14次修改了可删除可持久保存的前端html备忘录&#xff1a;增加一个翻牌钟&#xff0c;修改背景主题&#xff1a;现代深色 备忘录代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta http-equiv"X…

设计模式—行为型模式之责任链模式

设计模式—行为型模式之责任链模式 责任链&#xff08;Chain of Responsibility&#xff09;模式&#xff1a;为了避免请求发送者与多个请求处理者耦合在一起&#xff0c;于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链&#xff1b;当有请求发生时&am…

Flink面试题

0. 思维导图 1. 简单介绍一下Flink♥♥ Flink是一个分布式的计算框架&#xff0c;主要用于对有界和无界数据流进行有状态计算&#xff0c;其中有界数据流就是值离线数据&#xff0c;有明确的开始和结束时间&#xff0c;无界数据流就是指实时数据&#xff0c;源源不断没有界限&a…

ES文档索引、查询、分片、文档评分和分析器技术原理

技术原理 索引文档 索引文档分为单个文档和多个文档。 单个文档 新建单个文档所需要的步骤顺序&#xff1a; 客户端向 Node 1 发送新建、索引或者删除请求。节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3&#xff0c;因为分片 0 的主分片目前被分配在 …

Android源码设计模式解析与实战第2版笔记(二)

第二章 应用最广的模式 — 单例模式 单例模式的定义 确保某一个类只有一个实例&#xff0c;而且自行实例化并向整个系统提供这个实例。 单例模式的使用场景 确保某个类有且只有一个对象的场景&#xff0c;避免产生多个对象消耗过多的资源&#xff0c;或者某种类型的对象只应…

DT浏览器浏览网页的技巧

DT浏览器浏览网页的技巧&#xff1a; 1. 使用书签和收藏夹&#xff1a;将常用的网页添加到书签或收藏夹中&#xff0c;方便快速访问。 2. 善用搜索引擎&#xff1a;使用搜索引擎可以快速找到你需要的信息。在搜索时&#xff0c;可以使用关键词和筛选条件来精确查找。 3. 注意网…