此模式是 "遗留置换模式" 的一部分

提取产品线

按产品线识别和分离系统。

2021 年 7 月 21 日

Ian CartwrightRob HornJames Lewis

许多应用程序都是为了从同一个物理系统中提供多个逻辑产品而构建的。这通常是由对重用的渴望驱动的。“嗯,消费贷款看起来很像商业贷款”或“衣服是一种产品,定制窗帘也是,它们能有多不同?”。我们遇到的一个主要问题是,这些产品表面上看起来很相似,但在细节方面却大不相同。

随着时间的推移,为多个产品提供服务的单个系统可能会变得过于通用,代码会不断发展以处理所有可能的产品组合。例如,对于一个旨在处理 n 个产品且每个产品有 n 个更改的通用系统,必须执行的测试数量是 n 的阶乘。这是一个快速变大的数字。它也解释了为什么作者遇到的许多此类应用程序在自动化测试覆盖方面几乎没有,而是依赖于庞大且通常是手动的回归套件。测试如此多的不同代码路径根本不可能。

因此,问题通常是经济问题。很难接受,按产品开发和维护系统可能比开发和维护单个系统更经济。通过按产品拆分,我们利用了可以同时对多个产品进行更改的事实,并通过避免可能在不必要的地方引入缺陷的组合爆炸来降低风险。

当然,这是一个权衡——我们不希望为红色裤子与黑色裤子创建单独的应用程序,但我们可能希望为现成与定制创建单独的应用程序,或者为房屋保险与宠物保险创建单独的应用程序。

不同产品线也通常具有非常不同的价值流,如 提取价值流 中所述。

工作原理

识别系统中的产品或产品线。这将构成要构建/迁移的薄切片。尝试识别现有系统提供的全部功能,并将它们映射到新产品。在识别功能、数据、流程、用户等方面,我们倾向于从不同的角度进行观察。

识别共享功能。确定不同的产品线是否具有共享的 BusinessCapabilities。有几种方法可以解决这个问题,我们在 UnderstandingYourBusinessCapabilities 中介绍了这些方法。我们的建议始终是重视使用而不是重用,因此如有疑问,请尽量减少共享功能的数量。

选择谁先走。你将首先迁移哪些产品线?我们喜欢的一种方法是考虑风险。在了解迁移对业务的风险后,我们通常喜欢选择 *第二危险的产品线*。这可能感觉违反直觉,我们应该首先选择风险最小的选项,但实际上,我们喜欢识别一个足够重要的产品,以吸引业务的关注并促使他们优先考虑资金,但又不至于太危险,以至于如果出现问题,业务会失败。

确定目标软件架构。我们很少建议进行大爆炸式替换,在这种情况下,这意味着一次性构建所有产品的全部软件。相反,尝试确定步骤 1 中识别的薄切片的适当架构。

确定技术迁移策略。正如我们在技术迁移模式部分所讨论的,根据当前的约束,可以部署许多不同的选项。如果它是一个简单的 Web 应用程序,则可以使用 ForkByUrl。在其他情况下,如果 ForkingOnIngress 适用,则最好选择 MessageRouter 模式。请记住,可能需要 TransitionalArchitecture。

何时使用它

你有一个系统,其中包含易于识别的产品线,这些产品线将从以下方面受益

  1. 独立工作。将系统分解成单独的产品意味着可以围绕单个产品组建团队,从而能够取得进展,而不会出现典型的更改协调问题,包括合并地狱和漫长的回归周期。
  2. 具有不同的非功能特性。你希望能够为每个产品提供不同的 SLO。例如,给定延迟的不同负载要求。
  3. 不同的变化率。一些产品线很稳定,而其他产品正在积极开发中。分解系统意味着你不会冒对稳定产品进行更改的风险。

保险示例

在保险领域,不同的产品类型具有非常不同的特性。例如,汽车保险通常是高销量但低利润率,而房屋保险则相反。它也是高度竞争的,因此快速进行更改的能力非常重要。我们与之合作的一家保险公司开发了一个 3 层架构,该架构用作该保险公司提供的所有不同产品的报价引擎,包括汽车、人寿、房屋和宠物保险,如图 1 所示。

了解你想要实现的结果

业务的产品负责人越来越沮丧于更改的交付时间,它很长而且越来越长。他们决定让 Thoughtworks 来看看他们的架构和开发流程,看看我们是否能够确定问题所在。开发流程的价值流图识别了一些导致交付时间爆炸的约束。虽然每个技术领域都与其他领域分离,但不同的业务领域却紧密耦合在一起。这意味着为汽车产品添加一个新的需求通常会影响房屋、人寿等。这些更改需要仔细考虑和广泛的回归测试,然后才能进行通常很困难的部署。多产品架构还限制了能够安全地处理代码库的人数,进一步减缓了进度。

最后,由于汽车产品线的销量要求和业务的增长,底层数据存储必须经常扩展,这需要对系统中托管的所有其他产品进行停机维护。

决定如何将问题分解成更小的部分

因此,保险公司决定从围绕技术功能组织的 n 层架构迁移到按产品线组织的架构。产品已经确定:汽车、房屋、人寿和宠物保险。一旦了解了这些产品,他们各自需要的不同功能就被识别出来,例如,个人问题集、报价和客户帐户,以及更技术性的功能,例如身份验证和授权。客户帐户被确定为所有产品线都将使用的关键共享功能,这是从 EA 魔力象限中采用协调方法的一个很好的例子。

接下来需要做的是确定从哪里开始。按公司收入排序,产品线排名为汽车、房屋、人寿和宠物。而按客户数量排序,顺序则相反。因此,决定将房屋保险作为第一个单独实施的产品线。这平衡了出现问题对业务的风险,并获得了足够的收入,使这一选择变得重要。

成功交付这些部分

上图显示了团队开始构建的事件驱动架构。与报价引擎和客户管理功能的通信是通过 RabbitMQ 消息总线传递的事件进行的。这些事件也会传播到现有的企业数据仓库,以用于报告目的。

随着团队在现有系统旁边构建新的系统,他们制定了将流量从旧系统切换到新系统的准备工作。将整个产品线迁移的一个缺点是,必须完整地实现问题集,客户才能切换到新系统。由于这个限制,新系统进入了 Beta 阶段,一些客户可以选择加入使用 Beta 版本。加入的用户也有机会对新系统的外观和感觉提供反馈。随着新系统不断改进,最终的装饰功能也添加完毕,团队做出了上线与否的决定,并在接下来的几周内逐步将客户引导到新系统。首先是 1%,然后是 5%,然后是 10%,以此类推。这使得团队和业务更有信心,新系统在功能和非功能方面都按预期运行。最终,新系统为房屋保险服务了 100% 的流量。然后,团队转向持续的产品开发。

改变组织结构,使这种迁移能够持续进行。

在第一次迁移成功后,团队将注意力转向下一个挑战,即使用相同的方法迁移汽车保险,并重复这个模式,直到迁移完成,旧系统关闭。

与此同时,整个技术组织逐渐从基于项目的开发方式转变为以产品为中心的开发方式。这当然也遇到了一些问题。产品所有权需要随着时间的推移不断积累,因此转型是一个渐进的过程。他们还将这种方法应用于传统的 IT 运维,并在 CTO 和首席架构师的指导下,转向平台产品工程方法,用于按需基础设施、数据和分析。

重大修订

2021 年 7 月 21 日