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

关键聚合器

结合来自业务不同部分的数据以支持关键决策

2022 年 1 月 19 日

Ian CartwrightRob HornJames Lewis

业务领导者通常需要做出受整个企业广泛活动影响的决策。例如,制造商了解销售利润率可能需要有关原材料成本、制造设施运营成本、销售水平和价格的信息。正确的信息,按地区、市场或整个组织汇总,需要以易于理解的形式提供。

关键聚合器是一个软件组件,它知道要“访问”哪些系统以提取这些信息,要检查哪些文件/表/API,如何关联来自不同来源的信息,以及聚合这些数据所需的业务逻辑。它通过打印表格、带有图表和表格的仪表板或进入消费者电子表格的数据馈送,将这些信息提供给业务领导者。

这些报告的本质是,它们涉及从业务的许多不同部分提取数据,例如财务数据、销售数据、客户数据等等。当使用封装和关注点分离等良好实践实施时,这不会造成任何特定的架构挑战。但是,我们经常在遗留系统(尤其是单片机或数据仓库)之上实施此需求时看到特定问题。

在遗留系统中,此模式的实现几乎总是利用能够直接访问子组件以在处理过程中获取其所需的数据。这建立了一种特别糟糕的耦合,因为上游系统无法演化其数据结构,因为存在破坏现在 侵入式关键聚合器 的风险。由于其在支持业务及其领导者方面的关键作用,这种故障的后果特别严重且明显。

图 1:使用无处不在的聚合器进行报告

工作原理

首先,我们定义生成输出(例如报告)所需的输入数据。通常,源数据已经存在于整体架构的组件中。然后,我们创建一个实现来“加载”源数据并对其进行处理以创建我们的输出。这里的关键是确保我们不会创建与源数据结构的紧密耦合,或者破坏现有组件的封装以访问我们需要的數據。在数据库级别,这可以通过 ETL(提取、转换、加载)或通过服务级别的 API 来实现。值得注意的是,ETL 方法通常会与源格式或目标格式耦合;从长远来看,这可能会成为改变的障碍。

处理可以逐条记录进行,但对于更复杂的场景,可能需要中间状态,处理的下一步将在中间数据准备就绪后触发。因此,许多实现使用管道,一系列 管道和过滤器,其中一步的输出成为下一步的输入。

数据的及时性是一个关键的考虑因素,我们需要确保我们在正确的时间使用源数据,例如在交易日结束之后。这会在聚合器和源系统之间创建时间依赖关系。

一种方法是在特定时间触发操作,尽管这种方法容易受到任何源系统延迟的影响。例如,在凌晨 3 点运行聚合器,但是如果任何源系统出现延迟,则聚合结果可能基于陈旧或损坏的数据。另一种更健壮的方法是让源系统在数据准备就绪后发送或发布源数据,并在所有数据可用后触发聚合器。在这种情况下,聚合结果会延迟,但至少应基于有效输入数据。

我们还可以确保源数据带有时间戳,尽管这依赖于源系统已经拥有正确的时间数据可用或易于更改,而对于遗留系统而言,这可能并非如此。如果时间戳数据可用,我们可以应用更高级的处理以确保一致且有效的结果,例如 版本化值

何时使用它

当我们需要真正了解业务中许多不同部分或域的整体视图时,通常是在我们需要将来自不同域的数据关联到用于决策支持的摘要视图或一组指标时,就会使用此模式。

遗留表现形式

鉴于过去网络带宽和 I/O 速度的限制,将数据处理与数据存储放在同一台机器上通常是有意义的。具有合理访问时间的大量数据存储通常需要专门的硬件,这导致了集中式数据存储解决方案。这两种力量共同作用,使此模式的许多遗留实现与源数据结构紧密耦合,依赖于数据更新计划和时间安排,并且实现通常与数据存储位于同一硬件上。

由此产生的 侵入式关键聚合器 将其根扎入整个系统的许多不同部分,因此很难提取。从广义上讲,有两种方法可以进行置换。第一种方法是创建关键聚合器的全新实现,这可以通过 转移流量 来完成,并结合其他模式,例如 恢复到源。另一种更常见的方法是保留聚合器,但使用 遗留模拟 等技术在整个置换过程中提供所需的数据。显然,最终需要一个新的实现。

侵入式关键聚合器 相关的挑战

关键聚合器的大多数遗留实现的特点是缺乏围绕源数据的封装,任何处理都直接依赖于各种源数据格式的结构和形式。它们还存在关注点分离不良的问题,处理和数据访问代码交织在一起。大多数实现是用批处理数据处理语言编写的。

反模式的特点是系统内部存在大量耦合,尤其是在实现直接访问源数据而没有任何封装的情况下。因此,对源数据结构的任何更改都会立即影响处理和输出。解决此问题的常见方法是冻结源数据格式或在所有源数据上添加更改控制流程。这种更改控制流程可能会变得非常复杂,尤其是在存在大型源数据和系统层次结构的情况下。

侵入式关键聚合器 随着数据量的增长也往往难以扩展,因为缺乏封装使得引入任何优化或并行处理变得很麻烦,我们看到执行时间往往随着数据量的增长而增长。由于处理和数据访问机制耦合在一起,这可能导致需要垂直扩展整个系统。这是一种非常昂贵的扩展处理方式,在封装更好的系统中,可以通过与任何数据存储分离的商品硬件来完成。

侵入式关键聚合器 往往容易受到时间问题的影响。源数据的更新延迟可能会延迟聚合或导致它在陈旧的数据上运行,鉴于聚合报告的关键性质,这可能会给企业造成严重问题。在处理过程中直接访问源数据意味着实现通常具有定义的“安全时间窗口”,在此窗口中,源数据必须是最新的,同时保持稳定且不变。这些时间窗口通常不是由系统强制执行的,而是通常是记录在其他地方的约定。

随着处理持续时间的增长,这可能会为生成源数据的系统创建时间约束。如果我们有一个固定的时间,最终输出必须准备就绪,那么任何处理时间的增加反过来意味着任何源数据必须在更早的时间内是最新的和稳定的。这些各种时间约束使得合并来自不同时区的數據变得很麻烦,因为任何隔夜的“安全时间窗口”都可能开始与世界其他地方的正常工作时间重叠。时间安排和触发问题是此模式非常常见的错误和 bug 来源,这些问题可能难以诊断。

由于处理和源数据访问之间关注点分离不良,修改和测试也具有挑战性。随着时间的推移,此代码会不断增长,以包含针对 bug、源数据格式更改以及任何新功能的解决方法。我们通常发现关键聚合器的大多数遗留实现都处于“冻结”状态,这是由于这些挑战以及数据错误的业务风险造成的。由于紧密耦合,任何更改冻结往往会扩展到源数据,进而扩展到相应的源系统。

我们还倾向于看到聚合器的输出“膨胀”,因为鉴于上述问题,扩展现有报告以添加新的数据片段通常比创建全新的报告更容易。这会增加实现的大小和复杂性,以及每个报告的业务关键性。它也可能使替换变得更加困难,因为我们首先需要分解聚合器输出的每个用途,以发现是否有单独的用户群体,他们的需求可以通过更简单、更具针对性的输出来满足。

在 COBOL 和汇编语言中看到此(反)模式的实现很常见,这表明了替换的难度,但也表明了输出对业务的重要性。

重大修订

2022 年 1 月 19 日