2017.10.28, Ken Fang, 深圳
I. 前言:
产品级敏捷 2.0 是我在 2016 年所创建的。
创建产品级敏捷 2.0 最主要的目的是:
在产品级敏捷与 Design Sprint 的基础之上, 结合敏捷开发与软件工程, 而使团队成员可高效的协作; 系统化、精益化、严谨的进行产品服务化的设计◦ 使得产品能随著时间、版本的演进, 而能不断的提升其价值与对用户正面的影响力。
II. 本文:
在谈论产品级敏捷 2.0 前, 我们先回顾下, 产品级敏捷与 Design Sprint。
产品级敏捷共有五大核心工程实践:
Slice Board (切片板) : 使得每一轮版本的发布, 都能以最少的产出, 却能对客户产生最大的影响。
Architecture Map (架构地图): 轻量级的设计每一轮版本的架构设计, 并识别每一轮版本在架构上的风险。
Story Wall (故事墙): 使得开发人员与测试人员, 认同 User Story 的价值, 并从产品外部的视角, 清楚的明白: User Story 完成的定义或标准为何?
Scenario Tree (场景树): 轻量级且可视化的 Story 的设计与定义完成。
Feature API (特性API): 从外部的视角, 使得特性对外所提供的 API, 均能代表ㄧ有价值的 “业务概念”。
[图一: 产品级敏捷]
Design Sprint 共分为五个过程:
Understand (了解): 探索产品上的问题、用户的问题、用户平常都在做些什么?
Diverge (分歧): 产生各种的想法。
Converge (聚合): 决定最佳的想法。
Prototype (原型): 建立有使用者体验的原型。
Test (试验): 经由观察用户使用原型的情况, 验证所提出的想法。
[图二: Design Sprint]
所以, 在整个产品开发的生命周期中:
Design Sprint 能协助我们在产品的规划上具备前瞻性; 协助我们能做对的事情。
产品级敏捷能协助我们快速的响应用户的变化与交付符合用户期望的产品; 协助我们快速的将事情给做对。
[图三: Design Sprint vs. Product Backlog vs. 产品级敏捷]
产品级敏捷 2.0 则期望能藉由 Cloud Native 的架构, 使得产品能随著时间、版本的演进, 而能不断的提升其价值与对用户正面的影响力。
产品级敏捷 2.0 共有三大核心工程实践:
Boundary (分界线): 使得微服务能从特性业务场景的纬度、分布式调用技术的纬度、团队成员能力成熟度的纬度, 进行设计上的隔离; 划分出适当的微服务的粒度。
Service API (服务API): 使得微服务对外部不同类型的产品、系统, 均能提供一标准、完整且易于使用的 Rest 接口与版本演进的机制。
Service Event (服务事件): 使得微服务间能藉由事件驱动的方式, 以达到事务的一致性, 而使得微服务可更有弹性的扩展、可更自主的运作著。
所以, 产品级敏捷 2.0 是藉由 Boundary (分界线)、Service API (服务API)、Service Event (服务事件), 而使得产品的架构能拥有更佳的 ”关注点隔离 (Separation of Concern)”; 当我们的产品能拥有更佳的关注点隔离时, 我们的产品便自然而然的能随著时间、版本的演进, 而能不断的提升其价值与对用户正面的影响力。
[图四: 产品级敏捷 2.0: 打造服务化的架构; 使得产品能随著时间、版本的演进, 而能不断的提升其价值与对用户正面的影响力 ]
产品级敏捷 2.0 的三大核心工程实践详述如下:
1) Boundary (分界线):
微服务到底应该如何的识别? 微服务的粒度为何? 微服务该如何的分析与设计?
这些问题的答案, 取决于: 为何需要微服务?
为何需要微服务?
目的只有一个: 使得我们的产品能随著时间、版本的演进, 而能不断的提升其价值与对用户正面的影响力。
所以, 微服务的分析与设计, 决不是单纯的只考量技术上的解决方案。
微服务的分析与设计, 必需要掌握两个核心的原则:
从外部的业务场景, 驱动微服务的分析与设计。
经由微服务分析与设计出的 Cloud Native架构, 必需是能演进与能扩展的架构。
让我们开始探索微服务的分析与设计:
任何的产品; 不论是会与用户直接发生互动的应用系统, 或是提供给众多产品使用的平台; 都应该要先有一个完整的产品特性树。
产品特性树将使得我们可以很清楚的知道, 从外部用户或外部产品的视角, 产品的 Cloud Native 架构, 最终应提供哪些有价值的服务?
而团队中针对产品特性树中的每一个特性, 都应该要有一个主要的特性负责人; 每一个特性都会有一个主要的特性负责人负责, 每一个特性负责人, 都将负责多个特性。
在微服务分析与设计中, 特性负责人主要的责任便是:
经由与团队中各不同领域的成员; 架构师, 开发骨干人员, 测试经理, 资深测试人员; 共同具体分析出每个特性的业务场景与微服务的 Boundary (分界线)。
特性负责人与团队成员协作, 分析每个特性业务场景的主要步骤如下:
1. 特性负责人, 分析特性是由哪些业务活动所构成的?
2. 特性负责人, 针对特性中的某个业务活动, 分析出此业务活动的基本流。
3. 团队成员, 以特性负责人所分析出的基本流为基础, 分析出相关的扩展流与异常流。
4. 特性负责人, 决定团队成员所分析出的扩展流与异常流, 哪些是需在这个版本中, 置入到微服务的架构中, 来进行开发的。
5. 特性负责人, 再选取特性中的其他业务活动, 并重复步骤二至步骤五。直到特性中的所有业务活动均已分析完毕为止。
当特性负责人, 将特性的所有业务活动均分析出, 其各自的基本流, 扩展流与异常流之后, 特性负责人便可经由组合基本流, 扩展流与异常流, 而分析出从外部用户或外部产品的视角, 有价值的端到端的业务场景切片。
特性负责人经由与团队成员的协作:
团队成员, 分析出扩展流与异常流; 团队成员作加法。
特性负责人, 从团队成员所分析出扩展流与异常流中, 删除不需要置入微服务的架构中, 去进行开发的扩展流与异常流; 特性负责人作减法。
团队成员作加法, 特性负责人作减法; 此种团队协作的方式, 不仅使团队成员间能对需开发的微服务场景 (需求), 迅速的达成一致的共识, 并且能使得每个微服务, 都能以最少的场景 (需求), 却能对外部用户或外部产品, 产生最大的正面影响。
[图五: 特性负责人与架构师, 开发骨干人员, 测试经理, 资深测试人员协作; 共同分析出特性中的基本流、扩展流与异常流]
[图六: 特性负责人组合基本流, 扩展流与异常流, 而分析出从外部用户或外部产品的视角, 有价值的端到端的业务场景切片; 处理国内订单、处理国外订单、审核客户的信用额度]
当特性负责人, 将特性的所有业务活动均分析出, 其各自的基本流, 扩展流与异常流之后, 特性负责人便可经由组合基本流, 扩展流与异常流, 而分析出从外部用户或外部产品的视角, 有价值的端到端的业务场景切片后, 特性负责人便可将各业务场景切片中, 共同的场景提取出, 成为所谓的 infrastructure services。
也就是说:
对外部的用户或外部的产品而言, 有价值的端到端的业务场景切片, 便构成了所谓的 functional services; 可供外部用户或外部产品经由 api layer 来调用。
各 functional services 中所存在的共同场景, 便构成了所谓的 infrastructure services; 只供 functional services 调用, 外部用户或外部产品是无法经由 api layer 来调用的。
[图七: api layer: 构建在微服务与外部的用户界面、系统或设备之间]
[图八: Order Microservice: functional services; 提供服务给外部的用户或外部的产品处理国内外订单。Customer Microservices: functional services; 提供服务给外部的用户或外部的产品审核客户的信用额度。Product Microservice: infrastructure services; 提供服务给 Order Microservice 获取产品的信息]
现在的问题是:
经由基本流, 扩展流与异常流的组合, 所构成的业务场景切片, 是否就能形成 functional services 这类微服务的最佳 Boundary (分界线)?
要能回答这个问题, 需先思考下面的六个问题:
1. functional services 的 Boundary (分界线) 内的业务场景切片是否过于庞大与复杂? 而使开发人员或测试人员不易于理解?
2. functional services 的 Boundary (分界线)内的业务场景切片是否过于庞大与复杂? 使得在版本开发中, 会产生过多的变更或缺陷, 而使得版本升级的速度与质量因此而下降?
3. functional services 的 Boundary (分界线)内的业务场景切片是否过于庞大与复杂? 而使得在版本开发中, 此 functional services 已无法由单一的团队所完成?
4. functional services 的 Boundary (分界线)内的业务场景切片, 实际是否仅是能完成某业务活动中的某个功能点? 因而, 使得此 functional services 需要远程调用其他多个的 functional services, 才能完成某业务活动中的某个业务切片; 别忘了, functional services 之间的远程调用, 往往可能会引入性能、可靠性、甚至是安全性等的问题。
5. functional services 的 Boundary (分界线) 内所包含的数据库是否需要与其他的 functional services 发生数据一致性的问题。别忘了, 在分布式微服务的架构下, 微服务间的数据是一定会延迟的, 所以, 假如, 某个 functional services 的 Boundary (分界线) 内所包含的数据库, 是需要与其他的 functional services 维持数 据的一致性时, 则将会因过长的数据延迟, 而使得使用者的体验不佳。当然, 要维持众多 functional services 间的数据一致性, 在开发上也不是件容易的事。
6. 是否会因过多的 functional services , 而使得在自动化配置、测试与自动化部署的难度与风险增加?
所以, 当特性负责人, 经由基本流, 扩展流与异常流的组合, 所构成的业务场景切片, 而形成 functional services 这类微服务的 Boundary (分界线) 后, 便需与团队中各不同领域的成员; 架构师, 开发骨干人员, 测试经理, 资深测试人员; 再共同的协作, 针对每个 functional services, 反覆的推敲、分析、回答上述的六个问题, 直到获得大家都认可的 Boundary (分界线) 为止。
[图九: Order Microservice, Customer Microservices 的 Boundary (分界线) 是否过大? 是否过小?]
2) Service API (服务API):
特性负责人与架构师, 开发骨干人员, 测试经理, 资深测试人员, 经由协作, 完成了: 微服务的 Boundary (分界线) 的界定后, 接下来特性负责人便可:
将微服务内部的业务场景切片, 依场景或功能点, 拆分成一个或多个 User Stories。
将微服务会与其他微服务产生交互的场景, 拆分成一个或多个 User Stories。
[图十: Order Microservice 的 Microservices Team Backlog: Story: 审核客户订单, 负责 Order Microservice 与 Customer Microservice 间的交互]
特性负责人, 并且需针对每一个 User Stories, 提供以下的信息给开发人员与测试人员:
会与 User Story 直接产生交互的外部用户、系统、设备或事件。
外部用户、系统、设备或事件, 和 User Story 直接产生交互的目的。
外部用户、系统、设备或事件, 和 User Story 直接产生交互的主要场景。
User Story 完成标准 (验收条件)。
特性负责人, 说服开发与测试人员, 能认同微服务中的 User Story 的价值, 并使开发与测试人员能从产品外部的视角, 清楚的明白: 外部用户、系统、设备或事件所期望的微服务中的 User Story 完成的定义或标准为何后…
开发人员与测试人员便必需协作, 藉由 “Story 场景树”, 针对微服务中的每个 User Stories, 共同的完成:
从产品外部的视角, 分析出 User Story 最佳的易用性业务流活动步骤。
分析出 User Story 每个业务流的活动步骤, 对外依赖的接口, 数据库或端口。
分析出 User Story 每个业务流活动步骤完成后, 其所产出的实体。
设计出关键的纬度, 经由这些关键的纬度, 便能校验出 User Story 每个业务流活动步骤完成后, 其所产出的实体是正确、不正确、合法或不合法。
[图十一: Order Microservice 的 Story 场景树的分析]
每个微服务依照场景或功能点, 分解成一到多个的 User Stories。每个 User Story 经过开发人员与测试人员协作, 藉由 “Story 场景树”, 分析出微服务中包含哪些 “Entity (实体)” ?
每一个微服务中的实体应能只明确代表微服务中的某个单一的业务概念; 同样的, 微服务中的某个业务概念应也只能由微服务中某个单一的实体所代表。
所以, 在微服务中的 Story 场景树中, 假如, 识别出有一个以上的实体; 名称不同, 但这些实体所代表的业务概念, 却是同一个的业务概念; 则开发与测试人员, 便应该将这些代表相同业务概念的实体, 合并为单一的实体。最后, 开发与测试人员便从所分析出的实体中, 决定那个或那些的实体是微服务的 Root(s)。开发与测试人员便可从 Root(s) 著手, 设计出微服务对外的 Rest API; Service API (服务API) 。
[图十二: Orders 与 Order Status 代表同一个的业务概念, 所以, 合并为单一个实体; Orders。Order Microservice 中共有 Items, Cart, Orders 三个实体]
[图十三: 实体 Orders, 实体 Cart 都是 Order Microservice 的 Roots]
如图十三所示, 实体 Orders, 实体 Cart 都是 Order Microservice 的 Roots。
而由实体 Order 设计出 Order Microservice 对外的 Rest API; Service API (服务API); 便如下表所示:
这里有两点要强调的是:
一个微服务会有一个或多个的 Root(s) 。
当某个微服务有一个以上的 Root(s) 时, 团队成员便需要再共同的讨论; 确认先前所设计的微服务 Boundary (分界线) 是否适当? 微服务的粒度是否过大?
设计出了微服务的 Service API (服务API) Endpoints, 便可接著设计 Content-type; 如: application/json; 与 HTTP Status Code。
HTTP Status Code 超过 70 个; 没有人可以完全记得住。
产品级敏捷 2.0 建议:
团队应统一会用到那些 HTTP Status Code。
团队会用到的 HTTP Status Code 不要超过 8 个。
[图十四: 建议用到的 HTTP Status Code 不要超过 8 个]
产品级敏捷 2.0 建议 Service API (服务API) 版本演进的策略如下:
同时支持两个版本在线上运作; 以使使用 Service API (服务API) 的开发人员, 能有足够的时间进行升级到新版的 Service API (服务API) 。
在线上只有主要 (Major) 版本, 主要 (Major) 版本只在 URL 中体现;
如: GET https://api.a9.com/v1/orders。以避免因为过多的版本, 而使使
用 Service API (服务API) 的开发人员, 产生不必要的困扰与麻烦。
Service API (服务API) 的每个主要 (Major) 版本都有以下的三个属性; 控制著某个主要 (Major) 版本, 有那些使用 Service API (服务API) 的开发人员可以/ 不可以使用?
1. Available: 使用前一版本的开发人员也能使用。
2. Deprecate: 只有曾使用过前一版本的开发人员才能使用。
3. Retire: 任何的开发人员都将无法再使用。
举个例子:
[图十五: 同时支持两个版本在线上运作]
Q1/2017, Service API (服务API) 的 Version-1 上线; Version-1 版本属性在 Q1/2017 : Available。
张三, 在 Q1/2017 使用了Service API (服务API) 的 Version-1。
Q2/2017, Service API (服务API) 的 Version-2 上线。Version-2 版本属性在 Q2/2017 : Available。而 Version-1版本属性在 Q2/2017 变为: Deprecate。
David, 在 Q2/2017 使用了Service API (服务API) 的 Version-2。
因为, 张三在 Q1/2017 曾使用了 Service API (服务API) 的 Version-1, 所以, 张三在 Q2/2017 仍能使用了 Service API (服务API) 的 Version-1。当然, 张三也能使用 Service API (服务API) 的 Version-2。
因为, Version-1 的属性在 Q2/2017 已变为: Deprecate。而且, David 没在 Q1/2017 使用Service API (服务API) 的 Version-1, 所以, David 将不能使用 Version-1。
Q3/2017, Service API (服务API) 的 Version-3 上线。Version-3 版本属性在 Q2/2017 : Available。
Q3/2017, Version-2 的属性变为: Deprecate。
Q3/2017, Version-1 的属性变为: Retire; 任何的开发人员都将不能再使用 Version-1。
3) Service Event (服务事件):
微服务间能藉由事件驱动的方式, 以达到事务的一致性, 将能使得微服务可更有弹性的扩展、可更自主的运作著。
微服务间事件模型的设计, 产品级敏捷 2.0 是采用 Event Storming; Vaughn Vernon。
Event Storming; 1-2 天的 workshop; 特性负责人与业务领域专家, 架构师, 开发骨干人员, 测试经理, 资深测试人员协作, 活动的内容如下:
按照事件发生的先后顺序, 依时间的顺序, 分析出微服务之间会发生的事件。
[图十六: 事件: 以橘色的自粘贴纸代表]
针对每个事件, 分析出触发事件的命令 (Command) 。
当然, 单一的命令会触发一个或多个的事件。
触发事件的方式不外乎两种:
• 同时并行触发多个事件。
• 按时间的顺序, 循序触发多个事件。
[图十七: 命令: 以蓝色的自粘贴纸代表]
分析出会处理命令, 产生事件的 Aggregate。
[图十八: Aggregate: 以黄色的自粘贴纸代表]
将需在同一事务完成的实体, 聚合在同一个 Bounded Context 中。
[图十九: Order Bounded Context vs. Payment Bounded Context]
设计 Bounded Context 的 Policy; Aggregate 该如何响应事件? 该生成什么命令, 告诉其他的 Aggregate 该作什么?
[图二十: Policy: 以绿色的自粘贴纸代表]
III. 结论:
Design Sprint:
协助我们能做对的事。
产品级敏捷:
协助我们快速的将事情给做对。
产品级敏捷 2.0:
使得产品能随著时间、版本的演进, 而能不断的提升其价值与对用户正面的影响力。
这些年来, 我最幸运的一件事便是: 永远和一群比我聪明的伙伴们, 共同探索著敏捷开发与软件工程。
非常感谢这些年来, 和我一起努力走过, 这一段挺辛苦又挺孤独道路的伙伴们。
我们将持续的探索著, 期待著下一段的旅程将会更精彩、更有趣。
我们将探索著将人工智能应用在产品的开发上。
我们坚信: 我们将能打造更开心、更健康、更有价值的产品开发生态系统。
期待著你也来加入!
谢谢。