原文:
zh.annas-archive.org/md5/e2f167345d4aed05295f494546f1d634译者:飞龙
前言
感谢您购买这本书!它源于工业化的信息系统需求。软件无处不在,但除了少数高成本应用外,它充满了错误,维护成本很高,大多数开发者有一半的时间在重新发明轮子或与信息系统抗争以添加一些新功能。
这是我从超过 25 年的行业经验以及为许多不同组织专门从事数字化转型工作几年来的严酷观察。这些不同的经历使我确信,根本问题始终相同:软件行业实际上并不遵循工业化的规则,即分解系统为更小的系统,并使用遵守标准接口的模块将模块连接起来。
与学者们合作帮助我正式化这些业务/IT 对齐的问题,以及它们的来源和可能的解决方案。但我的大多数客户都告诉我,我之所以能作为一个顾问脱颖而出,是因为我不仅能够为董事会提供清晰的转型计划,而且还能提供从语义到 API 合约的咨询建议,甚至在代码的难点部分提供一些实际的帮助。他们说这使得整个合作更具可感知性,并提供了可见的业务成果,因此这就是我想让这本书成为的样子:首先是一些理论,因为我们需要改变我们思考和创建信息系统的思维方式,但也需要一些代码,因为太多的人现在效率不高。
看到许多公司,从小型律师事务所到工业/物流企业,再到一些大型农业合作社,业务效率的积极影响,我确信将工业概念应用于软件设计和架构将变得越来越具有成本效益,尤其是在软件和数据成为大多数组织当今竞争支柱的情况下。
使用 API 接口的规范和标准,外部化诸如身份验证、授权、电子文档管理等功能,最重要的是,从业务功能而不是技术角度思考,如果要将信息系统从成本中心转变为组织的强大资产,这是至关重要的。
这种方法已经帮助了许多公司和政府机构,我希望它也能为您的信息系统带来同样的效果!
这本书面向的对象
这本书旨在帮助您以使其更具灵活性和为您的组织提供更多业务价值的方式创建或演进信息系统架构。由于它是理论性的、面向业务的,并且以.NET 应用为技术应用,它可以在多个层面上被许多人阅读:
-
董事会和高级管理人员可以通过阅读前几章来了解数字化转型及其战略规划。了解信息系统中最常见的问题应有助于他们投资于正确的领域并优先处理工作,不仅要考虑功能,还要在合理的情况下优先考虑技术债务的减少。
-
技术领导、架构师和研发经理也可以从阅读下一章中受益,这些章节使策略更加具体,并解释了在构建信息系统、选择工具和方法以及在其工作中应用一致性方面的最佳实践。
-
- 最后,开发人员或实际操作的架构师和技术总监将继续阅读本书的最后几章,通过编码不同的概念、连接外部服务以及编排 API 和 webhooks 来创建一个虽小但实用的示例系统。
本书涵盖的内容
第一章,信息系统的悲哀状态,解释了什么阻止了大多数信息系统提供应有的价值。它还提出了一些衡量信息系统效率的方法,重点关注大多数观察到的问题的根本原因。
第二章,将工业原则应用于软件,首先解释了工业化的特点,然后提出了一种将转型应用于信息系统管理的方法。它侧重于标准和规范的重要性,以降低大多数虚拟系统中的复杂性。
第三章,实现业务一致性,为前几章分析的问题增加了一层理论,解释了康威定律以及一些其他关于业务/IT 一致性的原则、模式和反模式。本章还介绍了四层图,该图将在本书的其余部分中使用。
第四章,处理时间和技术债务,阐述了技术债务的概念,并解释了信息系统通常一开始与其环境非常适应,但随着时间的推移逐渐偏离效率。它还反思了如何采用敏捷方法来处理架构。
第五章,乌托邦式的完美 IT 系统,讲述了一个绝对完美的信息系统愿景,该系统将完全符合业务需求,并且变更将像原始设计一样简单。即使这个乌托邦式的系统在现实中永远不会存在,它也会帮助你理解外部化功能和严格分离责任如何使现有的信息系统更加高效,并随着时间的推移简化其演变。
第六章, 从代码到系统的 SOLID 原则,开始了一系列更技术性的章节。它讨论了通常适用于代码的 SOLID 原则,并将它们应用于系统架构。我们还展示了将作为本书其余部分应用实例的示例信息系统。
第七章, C4 及其他方法,列出了软件设计和架构中最著名的几种建筑方法。它分析了它们之间的差异和共同点,并为你提供了一些根据具体情况选择最适合的方法的途径。
第八章, 面向服务和 API,从对历史上用于实现服务概念的所有技术的描述开始,解释了人们对这一概念有什么期望。它将解释标准和关键格式在服务中的有用性。
第九章, 探索领域驱动设计和语义,关于拥有一个清晰的业务领域语义定义的重要性。尽管这听起来可能有点远离软件架构的概念,但它实际上是业务/IT 对齐方法的核心。
第十章, 主数据管理,提供了一个结构化的视角,用于管理一个良好对齐的信息系统中的数据。主数据管理远不止是在数据库中持久化数据。它涉及定义其生命周期和相关的治理,并确保数据管理不断进步,尤其是在一些遗留数据存储库已经存在的情况下。
第十一章, 业务流程和低代码,解释了业务流程在信息系统中的位置,以及如何使用如 BPMN 等标准正式描述它们。尽管这仍然是一个理论章节,但它描述了可以在信息系统中用于实施业务流程管理的工具。
第十二章, 业务规则外部化,在数据和流程管理之后,完成了理想 IT 系统三个部分的闭环。它解释了什么是业务规则管理系统,并展示了某些实施示例。
第十三章, 授权外部化,解释了身份和授权管理的概念,并展示了如何将其外部化以促进整个信息系统的发展。例如,基于角色的访问控制(RBAC)和基于属性的访问控制(ABAC)等范例也通过实例进行了说明。
第十四章,分解功能职责,是我们开始将前几章的知识应用到示例中的地方;我们将通过代码和服务来实际操作。本章首先使用四层图来建模系统,然后为将在外部应用中更好地考虑的功能准备技术规范。
第十五章,插入标准外部模块,考虑了这些规范,并为已识别的不同功能提出了一些现成的软件解决方案,例如 Apache Keycloak 用于身份和访问管理(IAM)、MongoDB 用于持久化、Alfresco 用于电子文档管理等等。
第十六章,创建只写数据参照,专注于示例信息系统的业务领域数据管理。它展示了如何用.NET 编写一个 API 服务器,以尽可能尊重标准以及之前描述的所有业务/IT 对齐原则来提供服务。
第十七章,向数据参照服务添加查询,在前一章的基础上增加了数据管理系统的一些功能,同时也展示了如何以工业方式对其进行测试。本章使用了如开放数据协议(Open Data Protocol)等标准来改进实现。
第十八章,部署数据参照服务,展示了如何部署前几章创建的服务以及如何将它们插入到 IAM 中。由于安全性是一个如此重要且复杂的主题,因此这是本章专门讨论的主题。
第十九章,设计第二个数据参照服务,使用前一章中提出的所有原则来构建第二个 API 实现,这次由于该服务必须使用 webhooks 并在本地缓存数据以减少对第一个数据参照服务的调用而变得复杂。尽管它没有增加任何新的原则,但它确实展示了如何在实践中实现松散耦合。
第二十章,创建图形用户界面,为我们的示例信息系统添加了一些图形界面,目前该系统仅暴露 API 端点。在展示基于 Web 的单页应用程序并解释业务/IT 对齐的概念和建议也适用于 GUI 领域之后,它详细说明了如何将数据分页机制与后端连接,并观察到职责的严格分离。
第二十一章,扩展接口,在前一章的基础上,将一些行业标准应用到之前构建的 GUI 上,即通过自动化测试它,同时也展示了如何自动化导入数据以将系统从其遗留数据管理迁移过来。它最后展示了良好的组件分离如何帮助,结合合适的技术栈,快速构建移动应用。
第二十二章,整合业务流程,通过解释如何通过 GUI、BPMN 引擎或 n8n 等低代码/无代码现代编排工具将业务流程集成到示例应用中,结束了与示例应用对应的章节集。它最后解释了何时需要专用服务来实现编排或编排。
第二十三章,对系统进行修改,通过展示已构建的信息系统现在可以通过受到副作用的影响或失去进一步发展的能力来支持各种变化,总结了本书的整个想法。在这一章中,我们将更改数据结构、GUI 和业务规则,特别是授权,甚至调整业务流程,希望展示初始架构规划使所有这些变得容易。
为了最大限度地利用本书
Docker 已被用于尽可能减少所需的工具。由于所有镜像都在线可用(甚至为示例信息系统创建的定制镜像,可在hub.docker.com/repositories/demoeditor找到),你只需要 Docker(有关安装说明,请参阅 https://docs.docker.com/engine/install/)来与该应用程序一起工作。镜像版本遵循代码分支。
如果你想要调试应用程序并对它进行一些更改以遵循书中的说明,那么你还需要.NET 8.0 SDK (dotnet.microsoft.com/download)、Visual Studio Code (code.visualstudio.com/download)和 Git (git-scm.com/book/en/v2/Getting-Started-Installing-Git)。Postman (www.postman.com/)也将被用来快速注入数据。
| 本书涵盖的软件/硬件 | 操作系统要求 |
|---|---|
| .NET 8.0 SDK | Windows, macOS, 或 Linux |
| Visual Studio Code | |
| Git | |
| Docker | |
| Postman |
如果您正在使用本书的数字版,我们建议您亲自输入代码或从书的 GitHub 仓库(下一节中提供链接)获取代码。这样做将帮助您避免与代码的复制和粘贴相关的任何潜在错误。
下载示例代码文件
您可以从 GitHub 下载本书的示例代码文件 github.com/PacktPublishing/Enterprise-Architecture-with-.NET。如果代码有更新,它将在 GitHub 仓库中更新。这个 GitHub 仓库是根据本书中示例构建步骤创建的分支:github.com/PacktPublishing/Enterprise-Architecture-with-.NET/tree/main/DemoEditor#versioning。
我们还提供其他来自我们丰富图书和视频目录的代码包,可在 github.com/PacktPublishing/ 获取。查看它们吧!
使用的约定
本书使用了多种文本约定。
文本中的代码:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“想象一下,我们使用一本书和author实体之间的链接。”
代码块设置如下:
{
"rel": "author",
"href": "https://demoeditor.com/authors/202312-007",
"title": "JP Gouigoux",
"authorMainContactPhone": "+33 787 787 787"
}
任何命令行输入或输出都应如下编写:
catch (Exception ex) {
transac.Rollback();
throw new ApplicationException("Transaction was cancelled",ex);
}
粗体:表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个示例:“一旦连接,您将看到欢迎页面界面,您可以通过点击Business Central或屏幕左上角的家图标在任何时候返回该界面。”
小贴士或重要注意事项
看起来像这样。
联系我们
我们始终欢迎读者的反馈。
一般反馈:如果您对本书的任何方面有任何疑问,请通过电子邮件发送至 customercare@packtpub.com,并在邮件主题中提及书名。
勘误:尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将不胜感激,如果您能向我们报告这一点。请访问 www.packtpub.com/support/errata 并填写表格。
盗版:如果您在互联网上发现我们作品的任何非法副本,无论形式如何,如果您能提供位置地址或网站名称,我们将不胜感激。请通过电子邮件发送至 copyright@packt.com 并附上材料的链接。
如果您有兴趣成为作者:如果您在某个领域有专业知识,并且有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com
分享您的想法
一旦您阅读了《.NET 企业架构》,我们很乐意听听您的想法!请点击此处直接进入此书的亚马逊评论页面并分享您的反馈。
您的评论对我们和科技社区都很重要,并将帮助我们确保我们提供高质量的内容。
下载本书的免费 PDF 副本
感谢您购买本书!
您喜欢在路上阅读,但又无法携带您的印刷书籍到处走?
您的电子书购买是否与您选择的设备不兼容?
别担心,现在,每购买一本 Packt 书籍,您都可以免费获得该书的 DRM 免费 PDF 版本。
在任何地方、任何设备上阅读。从您最喜欢的技术书籍中直接搜索、复制和粘贴代码到您的应用程序中。
优惠不会就此停止,您还可以获得独家折扣、时事通讯和每日收件箱中的精彩免费内容。
按照以下简单步骤获取优惠:
-
扫描下面的二维码或访问以下链接!https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_QR_Free_PDF.jpg
-
提交您的购买证明
-
就这样!我们将直接将您的免费 PDF 和其他优惠发送到您的电子邮件
第一部分:与业务对齐的架构及其解决的问题
本书的第一部分完全不涉及技术,旨在提供对业务/IT 对齐的深入了解。它解释了现有信息系统中存在的困难和不足,以及一些工业化方法可能如何帮助克服这些问题。这对于面临数字化转型的人来说很有用。对于那些 IT 系统无法实现其功能承诺的人来说,很难发展,而且成本效益不高,也应该更好地理解为什么是这样。在解释原因和症状之后,本书的这一部分提出了一种理论方法,即乌托邦式的系统,将消除所有这些不足,并将指导下一部分的工作。
本部分包括以下章节:
-
第一章,信息系统的不堪现状
-
第二章,将工业原则应用于软件
-
第三章,实现业务对齐
-
第四章,处理时间和技术债务
-
第五章,乌托邦式的完美 IT 系统
第一章:信息系统的不幸状态
在提出解决方案之前,分享对情况的彻底诊断是至关重要的。在信息系统和更广泛地,计算机使用的情况下,任何用户都知道“bug”这个术语,并经历过与故障相关的挫败感,有时这种挫败感具有很高的个人影响(个人数据丢失、收入后果等)。对于公司来说,IT 故障可能具有严重的后果,因为它们越来越依赖计算机来实现其业务运营,从而实现其财务目标。
在定义信息系统是什么以及解释其效率(或缺乏效率)如何计算之后,我们将尝试分类导致此类问题的原因。至于解决方案,这将是本书其余部分的主题。但到目前为止,我们必须了解信息系统出了什么问题,它是如何发生的,更重要的是,为什么会出现这种情况。
在本章中,我们将涵盖以下主要主题:
-
什么是信息系统?
-
为什么软件开发仍然是一门手艺,既有好的一面也有坏的一面?
-
如何评估信息系统的效率
-
如何分类可能发生在信息系统上的不同影响,以及它们的起因和后果
什么是信息系统?
在讨论信息系统状态之前,给出一个清晰的信息系统定义,甚至系统的定义可能是有用的。
系统是一组共同运作以实现共同目标的物品集合。这是系统与个体联合的基本区别:系统的各个部分共同朝着愿景努力。
信息系统(我们有时将其缩写为IS)进一步将此定义为一组共享信息以实现共同目标的物品集合。严格来说,信息系统不一定由软件组成,尽管本书中我们将讨论的大部分内容都是关于计算机化信息系统。即使在最复杂的信息系统中,也仍然存在一个不可忽视的信息部分,这部分信息不是软件包含的。这种情况我们将讨论,但本书的主要部分,我们将假设基于软件的信息系统,因为它们现在几乎在每家公司和组织中都很普遍。
因此,信息系统被理解为一系列旨在实现目标的软件工具。对于拥有系统的大多数公司来说,这个目标通常被设计为一个业务流程。需要注意的是,软件始终依赖于硬件,但这种依赖性越来越被隐藏在幕后,信息系统越来越被视为软件手段,通过有效地实施功能流程,组织在一起以实现商业目标。
信息系统简史
当处理一般主题,如信息系统的质量时,回顾过去并分析其向当前情况的演变总是很有趣。
如果我们遵循之前提出的定义,信息系统只有在至少两个实体协作时才会出现。在我们基于软件的信息系统假设中,这意味着至少有两台计算机已经连接。这意味着最初需要占用整个房间的计算机,尽管它们经常被称为“大型系统”(在这种情况下,组装的物品是计算机制、短期内存和长期内存),但不应该被视为系统。
让我们再向前推进一点时间,谈谈 IBM 的客户-服务器大型机:这些是我们可以认为是信息系统的第一批,因为它们有连接到中央计算机的客户工作站,信息在它们之间流动。该协议是专有的,这使得 IBM 能够提供这些系统的高质量服务(其中许多至今仍在运行)。由于协议的唯一实现由同一团队定义,因此兼容性和互操作性并不是一个大问题。这是遗产,但当系统运行良好且现代化风险很高时,企业逻辑上会选择不移动任何东西(这是信息系统管理的第一规则:如果它运行良好,就不要去动它)。
快进到九十年代,这是个人电脑(PCs)的时代。尽管全球都在努力保持机器之间的兼容性,但当时使用电脑的人都知道,如果某个软件不支持嵌入式显卡,那么这无疑是部分失败。当然,从那时起,事情有了很大的改善,视频电子标准协会(VESA)为显卡和屏幕制定了国际标准,如 VGA 或 VESA,以及许多其他规范,使得在当今时代更换 PC 的组件而不会破坏整个系统成为可能。然而,对信息系统造成的损失相当大:当单台机器的组件都难以组装时,我们怎能期望机器网络能良好地协同工作呢?专有格式、不同的编码、几乎完全缺乏强大的数据交换协议:所有这些都导致了困难的情况,只有高预算的公司才能在专家的帮助下操作复杂的计算机化系统,这些专家会对电子板上的跳线、编译器参数等任何东西都感到紧张。
幸运的是,随着互联网的扩张和其对标准化计算机之间交换的激进方法,Y2K 问题得到了解决。现在,每个遵守 TCP/IP、HTTP、Unicode 和其他互联网标准协议的计算机都可以与世界上任何地方的另一台计算机交换数据,无论其硬件和操作系统实现如何。这是 IT 历史上的一大步,也是我们今天“现代”信息系统根本定义的根源。
在此基础上增加了几层软件,以便于在系统中重用功能。它从低级开始,通过例程进行本地代码重用,然后是库和组件。随着网络容量的出现,这演变为分布式组件、Web 服务,最终是面向服务的架构(SOA)。这也是引入 n 层架构的时候,在软件应用内部建立了第一层责任分离,包括图形用户界面(GUI)管理、功能服务的展示、业务规则的实现和持久性管理。
最后一点,SOA,导致许多公司遭受了昂贵的失败,绝大多数尝试实施此类架构的努力都导致了重要的经济损失和项目的放弃。技术步骤太高,难以顺利运行,出现了更轻的替代方案来消除 SOA 方法的地方性困难:
-
标准化消息中间件:为了对抗在 SOA 上运营并使用它来锁定其客户的那些大型软件公司提出的专有交换协议
-
REST 方法:为了减轻 SOAP/WSDL Web 服务和所有相关规范的重量
-
企业服务总线:这项技术用于降低中间件的重要性,达到“哑管道”范式,其中参与系统的软件应用能够相互通信,而无需一个中心软件,该软件可能会成为单点故障
正如一些参考书籍(Sassoon, Longépé, 和 Caseau)所示,设计一个强大且具有进化能力的 IS 的最佳实践在 20 世纪 90 年代末就已经存在,尽管并不广为人知。但直到 21 世纪初,这些实践在社区中的份额才有所增加,SOA 和其他基于服务的途径蓬勃发展,导致在 2010 年代初出现了微服务架构。这套实践在撰写本文时仍被视为参考架构,尽管我们将看到,并非所有建议都应在没有对其在研究环境中有用性进行强烈分析的情况下应用。正如我们将看到的,服务的粒度是获得高效 IS 的关键。但到目前为止,我们将讨论软件构建的一般性,并试图理解这个所谓的“行业”的当前局限性。
软件构建——仍然是工艺
本书对信息系统精确定义的解释,以及其演变简史,已经给出。这个历史不仅非常简短,而且展示了许多最近的演变,其中大多数与之前的技术状态截然不同。这种非常快速的演变表明,信息系统设计不是一种可以被认为是稳定和完全理解的东西。
在软件信息系统设计和部署中,仍保留着大量的工艺。工艺有其优点:对细节的关注、定制化的功能、独特性以及更多。它也有许多缺点,如成本高、难以在受控方式下进化、依赖少数创作者等。这些缺点在现代公司中超过了其优点,因为信息系统已经成为运营的骨干。
精心构建的信息系统是从任意演化的系统中演变而来的,工匠的工作没有什么是可耻的,但今天的道路是走向信息系统的工业化方法。这正是本书的主题。
工艺与之前缺乏质量相对立
在 IT 的多个领域,工艺被用于与较老、更随意和自我组织的旧方法相对立。例如,许多 IT 会议在其名称中包含“工艺”一词,作为他们解决质量和异质问题的意愿的声明。
信息系统之所以长期存在,仅仅是因为系统的各个部分被随意组合并连接起来,而没有对整个系统本身进行任何反思。这种情况通常发生在所谓的“点对点集成”中,在这种集成中,不同软件模块之间的连接仅考虑了链接的源和目的地,而没有考虑所有存在的链接的映射,有时甚至复制了一个已经存在的链接或反转了两个模块之间功能依赖关系的初始预期方向。
这种在没有人考虑整个功能的情况下诞生的系统,几乎没有机会长期保持稳定。在仅创建少数几个连接的罕见情况下,系统可以正常运行,但我们都知道 IT 发展非常快,业务需求不断增加(“系统中唯一稳定的东西就是其需要进化”)。如果没有人对整个系统有一个全局的视角,那么其整体进化的理想发展就无从谈起。这将是纯粹的运气,而墨菲定律指出,如果软件系统中有可能出错,那么它肯定会发生。
软件工艺涉及不愿意让系统自行创建并错误发展,而是要额外关注软件质量和构建持久且可进化系统的方法。当应用于代码(这通常发生在代码中)时,软件工艺包括测试自动化、重构方法、质量指标监控以及许多其他超越最小实践的方法和技术。
我们可以争论这些实践与工艺相悖,但这仅在已经建立的行业中才是正确的,在这些行业中,工艺与标准化的工业生产相对立。在 IT 领域,工业化尚未发生。IT 运动中使用的“工艺”一词,甚至可以说,是进入软件领域工业化的第一步,因为这是第一次坚决的行动,旨在使代码干净,不让它变成 IT 术语中通常称为“一团糟”的东西。
或许看起来我反对工艺和工业化,但一个并不比另一个更好:它们只是领域发展的两个阶段——在我们的案例中,是软件。就我个人而言,在 38 年的编程生涯中,我始终追求干净利落,因此我认为自己是工匠;我的目标是这本书——以及我过去 15 年的架构师生涯——谦卑地帮助迈出一步,使软件摆脱其幼稚问题,成为一个成熟的行业。
作为一个附带说明,软件架构师之间讨论的一个主题是我们的工作领域是否能够成为行业。我倾向于认为,成为一名优秀的软件工程师所需的创造力将使软件生产完全工业化变得不可能,但许多事情应该被工业化以达到这个成熟、成人的阶段,届时 IT 将最终实现其全部价值。
关于“持续架构”一词
持续架构的概念是,在某些人类构建(主要是软件应用)中,架构并非一开始就确立,而是在对象的构建过程中逐渐显现。这个概念通常与敏捷方法一起使用,在敏捷方法中,软件构建是迭代的,而不是一次性完成一系列阶段,如“V 型循环”方法。敏捷软件开发不是一次性的设计/开发/测试步骤系列,而是在这些步骤上多次循环,每次都基于前一个周期逐步向最终愿景迈进,甚至在步骤中可能还会演变。
在这种情况下,每一步都涉及实现循环所需的最小设计活动,以保持简单的心态。因此,没有最终架构的完整愿景,这有时可能被视为敏捷方法的严重局限,但同时也是它们的优势,因为它们能够不断适应。尽管如此,这种做法也可能出错,这通常发生在项目参与者期望架构自然出现的情况下。这种混淆在软件开发中很常见,其中没有架构师参与,开发者认为个人最佳实践、善意和工艺将对结果产生积极影响。但现实是,这些实践将积极影响敏捷步骤的每个个体的结果,但不会将整体架构引导到任何合理的位置,因为没有长期的方向。
因此,理解持续架构确实存在,但需要积极参与才能逐步实现这一点非常重要。它自然发生在模块级别,在那里单个开发者会仔细精炼和重构代码。但要在系统级别工作,也需要同样的参与度。
工艺与工业化方法的对比
在上一节中,我们将手工艺与代码中缺乏质量意图相对立,从而展示了积极手工艺的积极面。在这里,我们将指出它与工业化相对时的局限性。手工艺承载着高度熟练的个人谨慎操作的想法,与质量相比,花费的时间并不重要。
尽管手工艺(在其崇高的意义上是对手工打磨、高质量工作的奉献)值得赞扬,但它也表明该领域的成熟度仍然较低。当达到成熟时,学科往往会将工作的不同步骤分开,自动化其中的一些步骤,标准化实践和工具,并总体上提高效率,甚至将质量提升到仅靠个人人类方法无法达到的高度。此外,工业化使整个过程规范化,并使遵循规范的人——而不仅仅是高技能的人——能够达到这一高水平的质量。
这就是所有行业(这也是我们为什么这样称呼它们的原因)所做的事情,这是人类工人试图达到的自然演变。在软件领域,可预测性、质量和上市时间是被追求的品质,而工业方法对于实现这些品质是必要的。
必须指出,非工业方法并没有什么错误。软件还不是一种行业。毕竟,桥梁已经存在了 4000 多年,所以这已经成为一种受控和成熟的作业方式是完全可以理解的。另一方面,软件构建只存在了几十年,仍然处于起步阶段。
但这里重要的信息是,尽管手工艺(尽管它有所有这些优点)是工业化的前一步,但现在的许多信息系统所有者真的渴望一个能够达到这一步的 IT 团队。对于他们中的某些人来说,竞争优势主要来自信息系统。有人说过“今天所有的公司都是软件公司”,这再次强调了信息系统的重要性以及达到更高质量水平的绝对必要性。
技术债务的概念
技术债务是一个通过隐喻来解释的概念,它说明了软件开发质量低下如何对未来的发展产生负面影响。在金融债务中,你必须支付定期的利息,这取决于你借入的金额。在软件隐喻的这一面,通过削减成本和降低整体质量来争取一些时间,只要低质量的模块保持活跃,就必须定期支付。修复错误和维护模块将花费一些时间,团队将无法为新功能投入价值对用户有意义的资源。模块的质量越低,“利率”就越高——在我们的案例中,就是维护所需的时间。在最坏的情况下,软件的质量如此之低,以至于所有可用的资金/开发时间都只用于支付债务的利息(保持应用程序运行),这意味着没有资金剩余来偿还债务/修复软件,更不用说支付具有更高价值的功能了。
这个概念将在第四章中详细讨论,但既然我们在谈论工艺,现在立即解释这两个概念之间的联系是很好的。
工艺通常被视为一种软件开发方法,其中技术债务保持在尽可能低的水平,有时几乎不存在。一位优秀的工匠开发者会以拥有零缺陷软件、100%的自动化测试覆盖率、完全自动化的集成和部署系统为荣。
与工艺相抵触的工业化方法主要导致整体质量的提高,但在处理技术债务方面也超越了这一点。与粗心开发让技术债务可能不受控制、失去控制相反,工业化方法管理技术债务。金融隐喻仍然适用:而不是完全拒绝任何债务,一个深思熟虑的运营商将仔细管理他们的资本,如果确实有利可图,就会借款。与工匠相比,工业化导向的开发者将更加意识到上市时间的重要性,如果这有助于他们在竞争对手之前接触到用户,就会采取受控的技术债务水平,从而带来部分受技术债务减少影响的利益。
软件与机械系统长期比较
软件行业与机械行业(在大多数已知案例中,尤其是汽车行业)的比较,已经成为双方竞争者的习惯立场,以至于这种比较已经变得陈旧。此外,比较并不一定具有逻辑性,这取决于它们是如何实现的。例如,在著名的“微软与通用汽车”的梗中,将一辆汽车与一个软件产品进行比较,从每一方都得出了一些奇怪的结论(“如果汽车行业像软件行业一样快速发展,我们将会拥有每加仑行驶 1000 英里的汽车”/“如果汽车行业像软件行业一样运作,汽车每行驶 1000 英里就会意外撞车”)。这种不恰当比较的潜在错误在于,两边的复杂性水平并不相同。如果你要比较一个在工业工厂中制造的单一汽车,其成本应该与信息系统中的一个功能性过程的单一操作进行比较,因为只有一个功能是单独操作的(例如,汽车是运输少数人,软件应用是计算工资等)。如果我们反过来,想要比较软件的内部操作与其可能的上十万行甚至数百万行代码,正确的比较应该是与汽车工厂本身进行比较,因为它具有必要的模块化来改变其汽车型号的生产,并且它还包含成千上万的函数和更多的运动部件来完成这样的移动任务。
简而言之,错误的比较可能会非常误导人,并且几乎没有实际意义。但是,将信息系统构建与汽车工厂的设计进行比较,例如,更接近每个系统的复杂性的现实,并且可以提供有趣的见解,只要你仔细地将其置于上下文中。正如之前所述,如果我们保持行业标准年龄的基准,将信息系统与桥梁进行比较可能是相关的。IT 活动,作为一个只有几十年历史的行业,如何达到一个已经发展了几千年的活动的成熟水平呢?
我们现在将停止这种比较,但在这本书中,你将了解到几个软件“行业”与更传统行业之间的比较,这些行业更值得这样的称号。我们将努力使比较尽可能有帮助和合理。再次强调,说 IT 尚未完全工业化并没有任何评判的意思:一些系统无疑是工业化的,而一些则不是。这不应该被用来反对任何人,因为行业需要许多人类世代才能达到目前的水平。基于软件的信息系统只是没有足够的时间做到这一点。这本书是关于帮助走向这个方向的方法,考虑到其他行业的实验,同时牢记比较有时可能会误导人,并且在使用时应注意其适用性。
既然我们已经确定了信息系统的定义以及它可能因为该领域尚未工业化而存在的许多幼稚问题,就需要一个合理的工程方法来评估这种不成熟。
信息系统的效率
工程学是关于以受控的方式制造事物;信息系统不会避开这个备受期待的转型,因为它们的效率可以提升。为了做到这一点,我们需要指标、衡量标准和一种方法来获取给定信息系统的结果。幸运的是,在这个专业领域,对此有一致意见,正如我们现在将要看到的。
系统效率的衡量
由于系统是一组朝着既定目标共同工作的项目,如前所述,衡量整体的效率意味着不仅仅是知道每个项目的效率指标并将它们以某种方式相加。常有人说,一个好的信息系统的价值远远超过其各个移动部件的价值,而最积极的影响来自于它们的相互作用。
评估信息系统效率的一个好方法就是衡量它在其支持的功能过程中帮助节省了多少时间和金钱。然而,这相当复杂去衡量,因为一个单独的信息系统通常运行多个过程,并且它们各自的重要性和成本都应该被评估。如果你考虑到过程也是部分由人操作的,并且效率的提高并不仅仅来自软件部分,而是来自功能团队使用应用程序的方式、他们的培训、投入硬件性能的投资等等,那么使用这些指标来评估整个系统的效率就会变得困难。此外,输出可能更接近于投资回报率的计算,而不是效率指标的计算。
正因如此,信息系统的效率通常通过更简单、更易实现的指标来评估,即用于维护的成本百分比。这个简单的比率使我们能够知道有多少钱被用于保持系统运行(这是一个运营成本),以及有多少钱被投入设计它并使其变得更好(这是一个投资成本)。由于功能特性是系统所要求的东西,为了保持其运行而进行的维护越多,与从系统中获得价值相关的钱就越少。当然,关于软件效率有许多观点,我们将在本书中一起浏览很多这些观点。但维护只是开始评估信息系统状态的最简单方法。
维护成本
维护可能是软件仍然最缺乏的领域。关于软件设计和开发的方法论已经出现,并开始帮助将手工艺转变为更工业化的方法。但设计和开发只是软件旅程的开始。一旦投入生产,应用程序就必须部署、修补(有时在运行时进行)、改进,并在长期内维护。这就是不良设计、技术债务和维护成本高昂显示出设计阶段缺陷的地方。
再次强调,情况并非全然糟糕。毕竟,有经过验证的方法可以在软件应用程序运行时对其进行升级,如果我们将其与在运行时更换汽车轮胎的机械对应物进行比较,这确实是一项了不起的成就。但分析信息系统维护的总成本表明,这仅仅是冰山一角,总体情况相当糟糕。
一项由 Gartner 在约 10 年前进行的基于 3700 家公司的研究表明,IT 预算的 72%用于维护。你读得没错:IS 的成本几乎有四分之三不是由设计和改进决定的,而是在生产过程中进行调整和保持其运行。再次想象一下,一旦你为房屋的建设支付了费用,每年只需支付三倍的费用来防止其倒塌!这表明了今天信息系统所处的悲惨状态。
你会发现关于这方面的许多其他研究,但 Gartner 的研究可能是最知名的。为了提供另一个证实观点,同一时期的一项研究显示,“大约 20%的技术支持项目是失败的,并且这个数字随着项目规模和复杂性的增加而增加。然而,失败的项目在执行开始之前往往表现出明显的失败迹象。”
好消息是,我们并非注定要继续这样下去。在汽车制造业的初期,与最初的制造成本相比,维护成本巨大。最初的汽车并非工业化生产,而是手工制造,其年度维护成本几乎与汽车本身相当,轮胎需要每隔几千公里更换一次,机油需要频繁添加,以至于除了汽油罐外,还必须有专门的油罐,而且发动机每隔几个月就需要由机械师进行检修。
现在,当你查看工业化产品的维护指标时,情况要好得多。一家值得信赖的公司生产的新车在首次进维修厂之前可以轻松行驶数万公里,而且随着发动机制造技术的改进,添加机油已经成为一件往事。另一个例子是机油滤清器,其标准化程度如此之高,以至于两种尺寸几乎覆盖了整个欧洲个人汽车市场。
在工厂中,由于财务收益高度依赖于维护统计数据,我们有数据可以量化维护成本。一般来说,工厂的成本可以分为三组,这些组的比例对于确定整个工厂的效率很重要:
-
固定费用:这些是与生产量无关的费用。通常,建筑成本(通过债务偿还或租赁,取决于公司是否拥有或租赁场地)并不取决于你生产多少。为了提高效率,你希望减少这些成本,但提高你的生产水平也会有所帮助,因为它将减少它们对你的收入的相对影响。
-
增值相关费用:这些是与你的生产直接相关的费用。你生产越多,你为供应商提供的商品支付的金额就越多。你会得到更多的收益,除非你以低于收入价格的价格销售,这在大多数国家都是被禁止的,而且通常是一个非常糟糕的商业决策。总的来说,这些费用被认为是好的费用,因为它们越高,带来的钱就越多!
-
维护费用:这些是你需要为生产工具持续平稳运行而计划的开支。维护费用是一头难以驯服的野兽,因为如果你为了小利而忽视它们,它们可能会反过来狠狠地咬你(在便宜的润滑油上节省一点,你的百万美元机器可能在几年后就会损坏)。维护成本有两个缺点。首先,与固定费用相反,维护费用会随着生产量的增加而增长(你的机器运行越多,它们需要的维护就越多,而且随着旧机器需要更多维护,这种增长是指数级的)。其次,与第二种费用相反,它们不会给你的产品增加可见的价值:如果你在销售的汽车中加入了更好的引擎,相关的价值会立即被感知;如果你购买更好的油来维护用于制造这些引擎部件的机器,没有任何客户会意识到这一点,更不用说为此付费了。
因此,我们理解为什么工厂经理非常关注维护成本:它们是“坏”开支,也是难以管理的开支。固定价格需要较少的关注,因为……好吧,它们是固定的!而且增值成本也不是什么大问题,因为,当它们增加时,这意味着你的业务正在增长。所以,维护是关键,大多数工厂经理的业绩将由所有者或他们的上司根据他们的维护指标来评判。如果你不控制维护,成本激增,你将被另一位经理取代。如果你对维护预算过于严格,可能会出现代价高昂的故障,而且,再次,他们将被认为是混乱的负责人。
统计数据帮助管理者通过将其与过去在类似情况下所做的事情进行比较,找到这个复杂方程的正确方法。例如,在重工业工厂,合理的成本分配被认为是以下这样:
-
固定费用:预算的 10%
-
增值相关费用:预算的 85%
-
维护费用:预算的 5%
当然,这些数字之间有一些可容忍的差异,但维护比率高于 7%必须得到证明,超过 10%则是对维护不再受控的强烈警报。
现在,让我们回到 Gartner 的研究,其中被调查的 3,700 位 IT 领导者中,用于“维持运营”的平均预算为 72%。再次,这种比较对于所谓的 IT 行业来说似乎很负面:糟糕了 10 倍!但是,必须考虑一些情况。首先,由于设计仅在大脑中进行,不需要像重工业那样昂贵的材料原型,因此 IT 中的成本自然和最优共享必然是不同的。此外,由于“作为服务”的物品的可用性,固定成本的比例正在变得越来越低。在几十年前,购买大型计算机对预算有很大的影响,尤其是在最初几年,它只部分使用时,云操作使我们能够按需购买计算机能力,这使得这些成本落入投资成本类别。
因此,我们可以认为这些数字并不直接可比。尽管如此,IT 行业在维护成本方面存在问题。作为工程师,我们立刻想到的问题是:这从何而来?下一节将有望向您展示,原因——大多数情况下——是容易确定的,因为当设计信息系统时,会犯一些通用的错误。这些错误是观察到的不足的根本原因。
IT 系统禁止演变的例子
到目前为止,事情可能有点理论化。大多数信息系统都是没有明确的计划和全局、架构化的视野而创建的,这反映在整个系统的维护成本——以及总拥有成本——上。但在实践中这意味着什么呢?这是否很糟糕?
您可能听说过“意大利面盘”或“数据孤岛”这样的表达。在前一种情况下,IS 的模块之间交织得如此紧密,以至于无法触及系统的某个部分而不对另一个部分造成副作用。在这种情况下,演变变得复杂。第二个表达与 IS 的模块相关,这些模块彼此之间非常紧密地分离,无法共享公共数据。这通常会导致数据重复、质量损失,有时在整个系统中出现矛盾的过程。这些只是几个关于可能发生的通用问题的例子。
以下章节将深入探讨此类失误,并详细说明哪些反应链使信息系统变慢、操作困难,在最坏的情况下,甚至完全停止工作。作为一名近 10 年的架构师,然后是小型到大型公司的信息系统演变顾问,我观察到了足够多的阻碍信息系统的情况,能够创建一个分类,说明出了什么问题以及如何进行分析。这种与法国一个研究实验室分享的经验导致了多篇科学论文的发表,其中对这种分析进行了形式化处理,并记录了业务/IT 对齐反模式。其中一些将在接下来的章节中详细说明。
原因分类
下面的图表是从 2021 年发表的一篇科学文章中提取的——《业务-IT 对齐反模式:从实证角度的思考》——Dalila Tamzalit,LS2N / CNRS,以及我在 2022 年 6 月的 INFORSID 会议上展示的。
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_01_1.jpg
图 1.1 – 业务/IT 对齐反模式的分类
小图是识别不同不匹配模式的视觉代码,其含义将在下一章中变得更加清晰。现在,只需定义不同块的位置。
横轴表示在已研究的信息系统中找到该模式频率的表达。诚然,这些是有问题的信息系统(ISs),因为像我这样的顾问被雇佣来处理它们。然而,在几乎 100 个来自不同背景的组织/行业系统中积累了十年的经验表明,这在信息系统(ISs)中——遗憾的是——是非常普遍的,而高质量、快速发展和成本效益高的信息系统却极为罕见。这些系统只为那些需求有限的小公司或从一开始就知道他们的 IT 系统是其脊柱和大脑并相应投资的大公司和富有的公司所保留。
图表的垂直轴评估反模式对系统功能的影响。反模式的位置越高,它对 IS 的正确运行和/或进化的阻碍就越大。
这种分类模式的结果是,右上角的反模式(级别 1)影响最大,观察到的频率也最高。这种情况对应于业务流程直接在系统的软件层中实现。在第三章中,我们将考虑 IS 在四个不同层上的分解,但就目前而言,只需说这些层之间的良好对齐是质量的最重要来源,而四层图的重要性如此之大,以至于记录的 14 个反模式的符号都基于它。
如前图右上角所示的前三个反模式(第 2 级)比第一个稍微少一些,但造成了大量观察到的困难。它们对应以下三种情况:
-
具有多种实现方式的特性:这导致根据使用的软件不同,业务规则也不同,并由此产生明显的错误作为后果
-
孤岛:这些导致数据重复和额外的工作,以及由于同步或缺乏同步而导致的错误
-
单体:Heer,一个单独的软件应用程序集中了如此多的业务功能,其进化变得复杂,并成为整个系统的瓶颈,有时甚至成为阻塞点
剩余的 19 个反模式(第 3 级)观察较少且/或对信息系统的发展或正确运行的危险性较小,但了解它们可以帮助我们在系统图中发现它们并改善情况。
下面的图表(自愿模糊以保护客户信息)显示了信息系统快速地图如何帮助我们直观地找到“热点”。在这种情况下,两个接收了大量“硬耦合”(我们将在第四章)数据流的应用程序导致了进化问题,尤其是其中一个已经过时,另一个由于商业和监管原因难以进化:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_01_2.jpg
图 1.2 – 一个信息系统手工绘制的地图,揭示了两个软件应用之间的高度耦合
这个图表背后的故事以及它为什么在这里展示,尽管不可读,是因为在设计这个信息系统地图时,一位非技术董事会成员走进房间,并立即指向两个带有大量红色电线指向它们的笔记,说:“我想我知道问题在哪里。”这让我们意识到我们不需要继续更精确地绘制地图,因为现有的分析已经足够清楚,我们可以开始对主要的耦合问题采取行动。还发现这两个应用程序也是最过时的。
研究所有可能引起信息系统问题的反模式组合超出了本书的范围,因为主题是这些系统的架构,我们将主要集中讨论如何从设计阶段开始避免这些问题。尽管如此,如果你对此好奇,欢迎阅读 Business-IT Alignment Anti-Patterns: A Thought from an Empirical Point of View 论文(参考 进一步阅读 部分),以获得关于分类的学术性和更正式的介绍。
阻塞信息系统的经典症状
在原因的背后,存在 ISs 的症状,包括一些(有时很多)这些反模式。如果你有经验,那么你肯定会熟悉其中的一些:
-
软件应用程序或流程的测试时间呈指数增长。发布时间增加,有时甚至长达数年
-
一个版本会影响那些对包含的进化或功能不感兴趣的客户或用户,因为它是在他们不使用的功能上
-
IS 模块的一些副作用无法解释(已确立的缺陷,但还有性能影响、不可预测的行为等)
-
内部信息系统的满意度低,参与外部/客户信息系统的应用程序市场份额流失
大型系统的更新率
当这种影响发生,并且随着时间的推移变得更大、更困难时,通常感知的解决方案是丢弃现有的 IS 并构建一个新的,这通常被称为大爆炸方法。这不仅是最昂贵的解决问题的方式,而且它还碰巧是最有风险的,因为现有遗留应用程序中发现的错误注定会在其中一些重新出现,或者被新的错误所取代,这使得达到令人满意的情况变得极其缓慢。甚至有很高的可能性,在重写过程的最后,新系统也将远远偏离预期的行为,因为在此期间业务需求已经再次发生变化。
这就是为什么始终逐步改进现有系统,依靠其良好的行为,并逐步改进给用户带来最多麻烦的模块总是更好的。
根据信息系统质量水平,可能会发生以下情况:
-
它可能处于如此精细的状态,以至于所有进化都可以实现,而不会对不相关的功能产生任何副作用
-
实施一个简单的新工作功能并部署它可能需要几天的工作
-
它可能存在一些耦合,使得实现它的时间更长,但并不更困难
-
它可能充满了问题,以至于实现新功能变得复杂,需要专门的项目管理和影响分析
-
它可能处于一种状态,进化几乎不可能,或者以整个系统更高不稳定性的代价为前提
ISs 的进化通常基于持续几年的阶段。经过 2 到 3 年,在最佳情况下 5 年,业务的未来变得如此难以预测,以至于规划底层 IS 的进化没有任何意义。在高度波动的活动中,甚至 2 年的计划也可能被认为太长。
当然,许多信息系统不能在短短几年内完全纠正,需要几个计划来重新调整它们。在这种情况下,方法与敏捷软件开发项目中的方法相同:首先实现一个步骤——尽管时间更长,通常在学期左右——以重新调整最紧迫的问题,此时目标将重新分析,然后进行后续的重新调整步骤,以此类推,采用持续改进的方法。
尽管在某些情况下(尤其是对那些不掌握与稳定化相关的困难的技术人员来说)非常有吸引力,但大爆炸方法很少是解决方案,如果你必须处理一个低效的信息系统,你很可能会计划逐步演变。当你在一个信息系统中进行这样的改变时,这很快就会对遗留模块有所帮助。尽管 IT 行业很快就会将旧技术视为垃圾(只需在浏览器中输入任何技术名称,然后跟“is dead”,你就会意识到这一点),但对待信息系统演变的负责任的方法是尊重遗留系统。它成为遗留系统的原因是它已经为很长时间提供了价值。
摘要
在本章中,我们讨论了什么是信息系统以及为什么,尽管在软件设计方面可以给予真正的手工艺技术所有可能的专业知识和关注,但将这些应用程序联系在一起的系统可能会出现许多问题,主要是在维护成本和随时间演变以及应对新的业务流程和功能请求的能力方面。许多症状可以提醒我们某个特定信息系统的状态,但它们都归结为一个主要原因:与其它实际产业相比,IT 尚未达到真正工业化领域的状态,因为它仍然是一项非常新的人类活动。
在下一章中,我们将讨论工业化原则如何应用于软件。这可以总结为两个行动:降低复杂性和标准化接口。
进一步阅读
-
Longépé, C. (2019). 信息系统城市化项目 – 第四版. Dunod/InfoPro. EAN 9782100802432.
www.dunod.com/sciences-techniques/projet-d-urbanisation-du-si-cas-concret-d-architecture-d-entreprise-0. -
Sassoon, J. (1998). 信息系统城市化. Hermès. EAN 9782866016937.
www.fnac.com/a270920/Jacques-Sassoon-L-urbanisation-des-systemes-d-information. -
Caseau, Y. (2011). 城市化、SOA 和 BPM – 第四版. Dunod / InfoPro. EAN 9782100566365.
www.dunod.com/sciences-techniques/urbanisation-soa-et-bpm-point-vue-du-dsi. -
Gouigoux, J. P. & Tamzalit, D. (2021). 商业-IT 对齐反模式:从实证角度的思考. 收录于 E. Insfran, F. González, S. Abrahão, M. Fernández, C. Barry, H. Linger, M. Lang, & C. Schneider 编著的《信息系统开发:信息系统(IS)开发与运维(DevOps)之间的边界跨越》(ISD2021 会议论文集)。西班牙瓦伦西亚:瓦伦西亚理工大学。
aisel.aisnet.org/isd2014/proceedings2021/managingdevops/3/.
第二章:将工业原则应用于软件
本章解释了如何使信息技术成为一个真正的行业,这始于应用工业化的主要原则,即把复杂性切割成小块,然后标准化模块及其接口。我们将与城市发展进行比较,其中水管的标准化、电力的标准化和其他接口的标准化已经允许持续进化。
在本章中,我们将解释行业的概念,因为这个名称被非常频繁地使用,但并不一定每次都能精确理解其含义。我们还将了解通过将复杂问题切割成小问题,然后通过标准化使小问题变得简单和可重复,工业化的运作方式。我们还将了解从这种做法中可以得出什么好处,特别是在信息系统方面。
本章我们将涵盖以下主题:
-
什么是行业?
-
复杂性的管理
-
标准和规范的好处
-
信息系统中的城市主义隐喻
什么是行业?
在上一章中,我们将手工艺与工业化进行了比较,希望表明,虽然前者没有什么可羞愧的,但后者是其随时间自然演化的结果。所有行业都是从工匠开始的,随着工作越来越受控和可重复,最终可能成为真正的行业,工匠逐渐转化为工程师能力和工作。大多数人无需解释就能理解这一点,因为这在许多日常经验中都可以看到。例如,当一个人第一次尝试完成一项新任务(比如剪发)时,最初的尝试与随后的尝试不可比。经过一段时间,这个过程开始变得更加规律(头发被剪得很细,最初接受成为你的小白鼠的客户也不再抱怨)。经过足够的训练,一个人就会在该领域获得专业知识,并发展出一套常规(头发被剪到定义的长度和预期的形状,以一种可以在未来的剪发中精确复制的方式)。
尝试将工业化背后的东西形式化如何?换句话说,我们如何描述构成工业化的要素?正如我们所见,有一个可复制的概念,这意味着应该达到一个可测量的规范。此外,这个规范在所有领域的知识人士之间是共享的,这意味着它成为了一个标准。在我们的理发例子中,有剪发的名称,该领域的每个人都了解“修剪”或“缩短”的含义,这使得顾客不会带着他们没有预期的发型离开理发师。此外,从一位理发师到另一位理发师,一旦使用正确的词汇表达出来,就可以期待获得一个全球相似的结果。我们就是这样获得同质化的质量的。
在工业化过程中,就像在理发一样,也存在关注整体中各个小部分的概念。除非你想要看起来像名人,否则你不会将你的发型描述为整体,而是描述构成完整发型的各个小部分。例如(尽管从美学角度来看不是很好):后面长,顶部短,侧面渐细。关注部分而不是整体,将复杂问题分解成简单的小问题,这些小问题可以简单地解决,这是工业化的基础,也是工程乃至整体问题解决的基础。
一个好的理发师可能是一位优秀的工匠,但一旦你可以在该领域的许多专业人士那里得到类似的发型,它就简单地变成了一个行业。在接下来的章节中,我们将应用这个定义来探讨信息技术领域,并展示工业化是如何发生的。为了做到这一点,我们首先需要更深入地了解实现该概念背后的真正含义。
工业化的两个根源——模块化和标准化
在简单介绍我们将应用于信息技术领域时所说的工业化概念之后,我们将更深入地探讨概念中的两个相关运动,即把大问题分解成小问题,这可以称为模块化(因为我们期望整个系统的小模块),以及用标准化的方法解决这些小问题以达到均匀的质量,这可以称为标准化。
模块化以降低复杂性
在解释复杂性的概念之前,让我们通过另一个例子来看看它与模块化的关系。这次,我们将通过分析汽车的各个模块来进行机械比较。现代汽车是工程学的杰作,将许多部件以复杂的方式组合在一起,以至于一个人单独构建这样的系统几乎是不可能的。当将汽车分解为模块时,肯定有明确、分离的模块,每个模块都有其目的:
-
发动机将为汽车提供动力
-
车身将保护驾驶员和乘客
-
轮胎和驱动系统将把动力转化为运动
-
底盘将以刚性的方式将其他模块固定在一起,等等
发动机本身仍然相当强大,但我们可以进一步将其分解为子模块:
-
注射系统会将气体引入气室
-
活塞会将爆炸转化为线性运动
-
曲轴将线性运动转化为旋转运动
-
润滑系统将确保系统不会因磨损、加热等原因而退化
再次,复杂性已经降低,如果我们进一步分解模块,润滑系统可以描述如下:
-
泵确保油液循环
-
油液起到冷却作用并允许无摩擦运动
-
油滤清器可以去除可能增加摩擦和磨损的小碎片,等等
这次,我们已经达到了如此低的复杂性水平,以至于几乎任何人都可以对这些模块采取行动:换油可以由任何知道在哪里倒油的汽车车主完成;更换油滤清器就像拧下旧的,然后拧上新的,放在同一个地方一样简单。
模块化,实际上是将复杂事物切割成更小部分以便于管理的艺术。如果模块化做得好,每一步都会降低复杂性。想象一下,如果我们把发动机分成左右两部分:我们肯定不会让它更容易观察和维护。确实,模块化不仅仅是系统的切割;它是在智能方式下切割的艺术,以便降低复杂性。但我们如何做到这一点呢?这就是工匠的经验和长期制造历史的帮助发挥作用的地方,提供了足够的专长来知道系统应该在何处,以及什么会使它更简单。最初的发动机肯定没有油滤清器,但经过一段时间,在行驶了几百公里后被迫从发动机中取出所有油,过滤它,然后再倒回发动机,很明显,在发动机油流中插入一个滤清器是明智之举。如果我们试图用一句话来总结这一点,模块应该在功能之后被切割。油滤清器之所以存在,是因为在润滑过程中,必须有一个过滤功能。将这个功能分配给一个模块是有意义的。
为了确保模块化,标准化是有帮助的
不同模块之间的关系、它们如何组合以及它们如何相互作用,这些都是必须考虑的其他标准。仅仅减少模块数量是不够的:如果想让整个系统正常运作,定义更小的模块是第一步,但一旦创建,它们必须重新组合以达到全局目标。这正是模块切割方式的重要性所在,我们之前已经解释过,它应该遵循功能划分。
但如何将它们重新组合呢?如果模块与功能对齐,我们如何确保它们能够很好地结合?实际上,这个问题解释起来很简单,但有时解决起来却极其困难,需要大型工程团队来完成:我们必须确保它们共享的公共功能完全相同。如果两个功能需要重新组装,这意味着它们有一个小的连接子功能是共同的,这通常被称为接口。这个接口必须在两边以类似的方式定义。
让我们再次以我们的油滤清器为例:它已经从润滑系统和其他发动机部分分离出来进行定义,但它也必须放回发动机系统中才能运行并参与更高层次的功能,即向汽车提供动力。为此,已经解释了油滤清器必须拧回到发动机中的位置,这就是需要接口的地方。这个接口只是一个螺纹:油滤清器将提供一个螺纹油,发动机在滤清器必须放置的位置提供一个螺纹凸起,当然,它有一个孔,允许油流入和流出滤清器。接口本身由功能定义:
-
它应该提供稳定的连接
-
它应该足够紧以防止漏油
-
它应该允许足够的流体循环,等等
我们向前迈出了一步,但为了达到工业化,还有另一步要做:接口必须标准化,这意味着所有前面的功能都应该以易于替换的方式指定,以便每个供应商只需了解接口就可以参与更高层次的模块。在我们的例子中,为了参与发动机系统,油滤清器必须遵守以下规定:
-
使用精确的螺纹直径(对于欧洲油滤清器,它是 20 毫米直径,螺纹步进为 1.5 毫米)
-
通过一个直径为 62 毫米的圆形接口确保了防漏油
-
基于流体循环保留碎屑的能力,以及因此滤清器使用的持续时间,由滤清器的体积决定,有两种标准尺寸,等等
这里是一个如何将油滤清器连接到汽车发动机的非常低级的示意图:油滤清器上的螺纹孔与发动机外部的带孔金属螺纹件相适配,以便于访问:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_02_1.jpg
图 2.1 – 油滤清器在汽车发动机上的示意图位置
这就是我们要找的!如果我们现在回到解释,我们会发现模块非常标准化,可以在任何地方购买,它们将具有相同的接口,尽管它们的内部功能可能不同;组合在一起的模块将各自具有其功能,但为它们共同形成的全局系统提供更高层次、更复杂的功能。再走几步,由许多模块和子模块组成的整个系统将具有这种工业方法无法解决的问题的复杂性。
以另一个与我们日常经验更相关的例子来说明,小型电池和充电器目前正受到政府标准化推动的目标。这在欧洲共同体中尤为明显,USB-C 甚至被强推给像苹果这样的大规模反对者。大型公司几十年来一直在使用许多不同的非兼容连接器和充电器,导致电子系统的大量浪费和用户日常生活中的复杂性,迫使他们在许多不同的设备之间来回切换。这项法律已经在使公众为手机充电变得更加简单方面产生了一些效果。
谈及复杂性,我们不得不更精确地定义这个术语背后的含义,这正是我们将在下一节中要做的。
复杂性的管理
术语复杂性指的是由许多不同部分组成的某物的特性。它常常与复杂性混淆,后者意味着某物难以理解。大多数信息系统都是复杂的,而如何处理这种复杂性可以使它们变得复杂。
复杂性的不同类型
在谈论如何通过将大型、难以操作的系统切割成更小的、更容易处理的小系统来减少复杂性时,我们之前引入了复杂性的概念。在本节中,我们将回到这个复杂性的概念,并首先声明存在两种类型的复杂性,即内在的、功能性的和可避免的、偶然的。第一种来自功能本身,如果一个模块要提供这个功能,它不能做得少于这一点。第二种是在实现功能时添加的一切,这不能被视为功能本身的纯粹必要部分。当然,整个目标将是尽可能减少第二种,因为两者相加,而第一种根据定义不能减少。
在我们关于汽车发动机油滤清器的例子中,滤清器内部吸收纸的折叠是内在的复杂性,因为不同的纸张堆叠以及它们如何形成复杂的油流路径是滤清器工作的方式,在纸张的折叠处保留重金属颗粒,而油以更清洁、更纯净的特性到达滤清器的输出端。实际上,滤清器的金属外壳不能被视为过滤操作的参与者。当然——它有助于将纸张片固定在一起并便于操作,但它并不参与过滤:这是油滤清器中的偶然复杂性。
信息系统中充满了偶然的复杂性,考虑到如今最小的文本笔记应用也使用了数千行代码和数兆字节的内存,这仅仅开始显示出问题的严重性。
计算机科学作为处理复杂性的方法
考虑到计算机的设计初衷是提高生产力,复杂性达到如此高的水平可能听起来有些奇怪。毕竟,早期的计算机是为了大幅加速那些否则需要几天、几周甚至几个月的计算而建造的,并且需要仔细的双重检查以尽可能避免错误。由于最初设计的复杂性增加,投资于创建计算机的成本巨大(设计现代计算机和电子芯片是我们文明中最复杂的任务之一),但使用计算机快速产生大量问题的准确结果在很大程度上可以弥补投资。
目前计算机所做的许多工作都具有高度的技术复杂性:在游戏中显示来自 3D 建模的高分辨率实时图像、执行如离散傅里叶变换或蒙特卡洛模拟等长时间计算等。许多这些操作无法由人类或甚至大量人群以相同的精度和低错误率实现。因此,我们可以认为 IT 有助于降低复杂性。
信息系统与复杂性
但与此同时,尤其是对于像我这样在软件领域工作了 30 多年的人来说,这看起来就像是计算机实际上并没有带来那些巨大的功能进步,而这些进步本应是我们对计算能力大幅提升的预期。GPU 的速度快了数百万倍,但游戏体验仅提高了几倍。个人电脑的功率强了数百倍,但语音打字仍然远未完美,文字处理基本没有变化,新功能——大多数时候——充其量是无用的,最糟糕的是变成了臃肿的软件。
正好,随着计算机容量的增加,我们要求它们做更多的事情。虽然其中一些额外的操作带来了新的价值(如机械模型的优化、模拟复杂物理模型的能力等),但很多都是非增值特性(更大的屏幕、无限的颜色细微差别),这些特性确实增加了额外的舒适度,但在业务线(LOB)软件应用中,并没有为功能性价值带来任何东西。
总结来说,偶然的复杂性几乎与计算能力的增长同步,因此,剩余的计算能力在处理内在的、以业务为导向的复杂性方面带来的性能提升非常有限。
“作为服务”的概念
幸运的是,关于信息系统的发展也有一些好消息,而“作为服务”的方法就是其中之一。 “作为服务”的方法意味着向用户提供有价值的东西,而不涉及物质部分。例如,基础设施即服务(IaaS)为您提供了内存和 CPU,而不涉及计算机的硬件部分;这由其他人处理,通常是云服务提供商。软件即服务(SaaS)为您提供可以简单通过网页浏览器调用的软件,而无需担心先决条件、安装、购买许可证等等。
如果我们将这种方法与之前提出的复杂性概念联系起来考虑,我们可以这样说,目标是通过对函数本身不提供,而只提供函数的结果,即所需的服务,来将偶然复杂性降低到几乎为零。如果周围的大部分工件都不存在;只获得了软件辅助程序的结果。例如,在 IaaS 中,整体基础设施本身并不是买家真正需要的:买家并不渴望占用空间的物理计算机、需要局部温度控制、机架等等,但必须经历这种偶然复杂性才能获得 CPU 能力、RAM 使用、存储空间或网络带宽和连接性。
“作为服务”的概念大大降低了信息系统感知的复杂性。当然,没有免费的午餐,整体复杂性仍然存在(甚至有所增加)。但这次不是模块与模块之间的分离,而是功能与功能之间的分离,在服务提供商处理的高技术复杂性和用户保留的低复杂性之间建立了一个清晰的界限。从后者到前者的财务转移可以用这样一个事实来解释:用户获得了专注于增值、面向业务复杂性的巨大优势。服务提供商如何从处理更高的技术复杂性中获得财务利益(对于一个普通用户来说可能是偶然的,但对于服务提供商来说是标准业务复杂性),这来自于他们是这方面的专家,为许多用户处理大量业务,并应用规模相关的成本节约。最终,每个人都从复杂性的清晰划分中受益,这也可以描述为责任和任务专业化的分离,正如之前所解释的那样,这与工业化是一致的。
链接到一个最小可行产品
许多使用敏捷方法工作的人都知道一张著名的图片,它展示了最小可行产品(MVP)的概念,这是 Henrik Kniberg 在 2010 年代中期创造的(blog.crisp.se/wp-content/uploads/2016/01/mvp.png):它展示了产品演化的第一行,从轮子到两个轮子相连,然后到两个轮子和车身,最后变成汽车。在这个过程中,笑脸一直皱着眉头,只有在最后一步才满意。在图片的第二行中,步骤被滑板(悲伤的笑脸)、自行车(中性的笑脸)、摩托车(相当高兴)和敞篷汽车(极度高兴的笑脸)所取代。
这已经被广泛研究,并且是对软件应用从最小可行产品(滑板)到完整项目(右侧的汽车)这一演变概念的绝佳描述。许多模仿版本并没有传达出太多的意义,因为它们遗漏了一些细节。例如,其中一些最终在两条线上结束于同一辆汽车,这完全错误,正如 Kniberg 故意在两个过程的最后展示了不同的汽车。整个故事在blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp上得到了完美的解释,我当然不会对其进行释义,而是尝试将其与之前关于“作为服务”方法的讨论联系起来。
那张著名的图片中提到的服务是什么?它有汽车吗?没有——拥有或驾驶汽车仅仅是服务本身的一个副作用,而这个服务是“从一个地方到另一个地方”。使用最小可行产品(MVP)将帮助我们尽快收集关于用户实际需求的反馈。现在,如果我们走向“作为服务”方法的极端,并将人的位移(以及可能的行李)作为唯一请求,科幻般的传送就会变得绝对完美!而且我们对可能性和价格更加合理;正如 Kniberg 所说,可能最基本的方法是向用户提供一张公交车票。
这也会是一个有价值的 MVP,但这是忘记了一个事实:MVP 并不意味着设计师没有考虑最终目标:我们提供滑板来收集反馈(例如,“稳定性很重要”),同时仍然想着我们最终想要一辆汽车,也许是因为最初表达的需求是自主旅行。
最重要的是什么——我们将会回到为什么最终汽车不一样的原因——那就是,在考虑反馈的同时,在这个例子中稳定性很重要,设计迅速演变为自行车,这辆自行车更稳定,更容易保持平衡。但这并不是收到的唯一反馈。例如,车辆没有覆盖并不是真正的问题,设计演变为自行车,然后是摩托车,没有风或雨的保护。最终,提出的汽车没有车顶:这不仅是因为它根本就没有被要求,而且因为驾驶时头发飘在风中的兴趣可能源于反馈循环。如果直接创造了汽车,也许客户就不会想到一个敞篷车顶。但要求持续的反馈已经显示出一种额外的期望特征(虽然不是需求,而只是一个“额外奖励”),否则可能无法检测到。
这就是公司在表达他们希望“取悦客户”的愿望时所谈论的内容。我们大多数工程师并不立即理解这一点,因为我们倾向于看到从初始规范中衍生出的一个优化解决方案的问题,但最好的解决方案为客户带来了他们甚至最初都没有考虑过的价值。而且你知道吗?由于所有公司通常都擅长创造预期的功能,这些意外和令人愉悦的功能将成为客户用来区分你的服务与竞争对手的服务的东西!
现在复杂性的概念应该已经清晰了,我们将提出一种如何减少复杂性的初步方法。
标准和规范的好处
本章的第一节*什么是行业?*开始讨论标准化以及它是模块化有意义所必需的。让我们想象相反的情况,一个系统被任意切割成几个更小的部分,没有考虑如何定义这些部分,它们如何相互作用,以及它们如何可以被改进的版本所替代。结果将是,模块不能在没有了解整体的情况下设计,也不能由现有的模块所替代,因为它们粘合到其余部分的方式可能已经不存在。至多,这只会使整个问题更容易解决;最坏的情况是,将一切重新组合起来的额外难度将大大超过一次性解决整个系统所带来的复杂性减少。
这就是为什么模块的切割界面及其标准化如此重要,为什么我们将用额外的例子在下一节中强调这一点。
Docker、容器和 OCI
Docker技术是讨论规范和标准的一个很好的方式,因为它的名字本身就以一个工业概念的隐喻开头,这个概念通过标准化而繁荣,即货运和集装箱。
直到 20 世纪 50 年代,货物运输根本未实现标准化,用货物装满一艘船是一项相当精细的手艺:包装大小和重量各异,有的柔软,有的坚硬。将它们绑在一起以便在运输过程中不移动的方法在每个不同的运输中都是定制的。正确填充车辆极其困难,因为几乎没有机会让所有包装都能完美地占据所有空间,同时将脆弱和轻便的包装放在顶部,将沉重和坚固的包装放在底部。如果你再考虑负载平衡、湿度或温度效应,这些效应可能会从一个包装传递到另一个包装,以及偶尔出现的最后一分钟包装,它太重以至于无法放在其他包装的顶部,迫使码头工人卸下一部分货物并重新安排一切,你开始理解当时货物运输是多么复杂的一项工作。
见证马尔科姆·麦克莱恩,他在 1956 年设计了一种基于木箱的运输系统,这种木箱可以轻易地从卡车转移到火车和船上。仅在 10 年后,即 1967 年,这个伟大的想法被广泛使用,以至于国际标准化组织(ISO)定义了三种“容器”的标准尺寸。尽管道路/铁路/海上运输活动是一个庞大、全球性的业务,但在仅仅几十年后,地球上几乎每个运营商都使用标准尺寸的金属容器,这有助于优化整个物流链,并具有以下优点:
-
装载便捷:任何调度员都可以轻松地拿到一个容器,按照自己的节奏装载它,然后联系运输公司,让他们携带容器,而不用担心被拒绝,因为他们无法处理特定的形状。
-
货物处理改进:由于金属箱具有标准尺寸和带有处理孔的角落,因此不再需要更换用于压包(并可能损坏其内容)所需的工具。现在,抓取工具只需锁定容器的四个角落并抬起它们。处理速度也得到了提高,因为不需要逐个处理不同的包装:机器将一个容器(内部携带许多不同的包装)作为一个单一单元抬起。
-
优化存储:工业容器是简单的平行六面体形状的箱子。它们的堆叠几乎不浪费空间,只是墙壁的宽度。今天,大型船只的尺寸是根据容器进行优化的。
-
可互换材料:容器已经变得如此商品化,以至于几乎不存在产权问题。容器可以轻易地修复或被另一个容器替换。容器基本上从不空载。其中一些已经环游世界多次,而它们的最初买家再也没有见过它们。
Docker 的名称和标志清晰地阐述了这项技术的哲学:术语docker指的是在船上装载货物的任务,而 Docker 的标志展示了一只鲸鱼背上的集装箱。这个联系对于运输业来说非常明显,公司希望成为应用运输的工业集装箱的等同物。
正如工业运输集装箱一样,Docker 容器提供了基于标准的优势:
-
无论容器内部是什么(Java 进程、.NET Web、Python 脚本、NodeJS API 等等),外部接口都是完全相同的,人们只需简单地输入
docker run命令,容器就可以启动并运行。 -
一旦应用被放入 Docker 镜像中,它就可以通过注册表发送到地球上的任何地方,并在任何国家以相同的方式执行。
-
独立于底层架构:Docker 容器不知道或关心它们是在 Windows 机器、Linux 服务器上还是在 Kubernetes 集群上运行。由于它们具有标准尺寸,它们可以适应任何地方。
在所有这些优秀特性的加持下,Docker 迅速成为了应用部署的事实标准。Docker 本身甚至可能成为最终标准,但存在一些不足,几年后出现了一个更高层次、更广泛的标准:开放容器倡议(OCI)创建了一个低门槛但不可否认的标准,每个容器技术(包括 Docker,以及其他虽然不太知名的技术)都必须遵守。
容器无疑使应用部署工业化并极大地改善了应用部署的方式。微服务的兴起与容器技术密切相关,因为使用旧的方法手动为每个应用设置依赖项和资源,部署大量小型应用将变得极其复杂。有些人甚至说,微服务架构的出现仅仅是因为 Docker 允许它们存在。
Docker 是技术如何规范特定软件相关功能(在这种情况下,应用部署)并产生巨大影响的一个例子,通过单一标准化方法,可以取代大量专有和手动方法。但这并不是行业里唯一发生这种情况的时候…
IAM 的另一个例子
身份和访问管理(IAM)是 IT 领域的另一个领域,标准化在过去几十年中带来了巨大的帮助,并积极改变了困难的情况。还记得每个软件应用都有自己的用户管理和密码吗?更不用说处理组和授权管理方式的不同、不兼容,等等。这样的混乱…当第一个单点登录(SSO)方法出现时,该领域的每个人都感到很高兴,中央认证服务(CAS)在可用的软件中实现了它。身份和认证提供商使该领域对初学者来说更加复杂,但避免了成千上万的糟糕设计的 IAM 系统,用在线、始终可访问的身份取而代之。
安全断言标记语言(SAML)迅速成为标准,像 Shibboleth 这样的工具帮助以正确、开源的方式扩散处理能力。最近,OpenID Connect(OIDC)、OAuth 2.0、JSON Web Token(JWT)和其他标准化方法基本上结束了关于最佳识别、认证和授权账户方式的任何讨论,考虑到需要考虑的新功能,现在几乎覆盖了该领域的所有需求。Keycloak 是一个生产就绪的、基于标准的开源应用程序,可以作为标准之间的粘合剂,这意味着我们现在有了所有真正以标准方式处理身份和访问管理(IAM)的工具。这些好处如此之大,以至于尚未采用这些方法的公司在接下来的几年里将不得不采取措施这样做,因为安全问题将使停止在专有、脆弱的实现上处理 IAM 成为强制性的。
再次,由于标准和模块化分离的方法,IAM 的功能已经变成了商品:
-
识别涉及账户及其个人所有者的身份,以及所有相关的元数据。轻量级目录访问协议(LDAP)和LDAP 数据交换格式(LDIF)是这方面的标准,但跨域身份管理系统(SCIM)也可以使用,以及如 SCIM 企业配置文件这样的扩展,例如,以纳入组织结构图。JWT 可以用来以规范化的方式携带这些数据。
-
认证是关于证明账户身份的过程。当然,会想到 OIDC,但快速身份在线(FIDO)和通用第二因素(U2F)也是与认证相关的标准,它们引入了物理设备来改善认证管理。
-
授权——一旦通过身份验证证明了身份——就是处理软件(或者,记住信息系统主要但不仅仅是关于软件)中允许人们做什么的方式。可扩展访问控制标记语言(XACML)是这方面的一个基于 XML 的标准,但还存在更近期的方法,例如开放策略代理(OPA)。
总之,IAM 是工业化的配方一旦被应用后,信息系统如何积极进化的另一个例子:将这个复杂主题划分为明确的、单独的责任,然后对每个责任应用规范和标准。
本章的最后一部分将与其他可能高度复杂且使用大量标准的系统进行类比,即我们许多人居住的城市。我说的不是智能城市,在那里软件服务于城市管理,而是随着时间的推移在组织上出现的城市。
信息系统的城市主义隐喻
为什么我要花费这么多时间和使用这么多文字来谈论那些已经成为标准并对信息系统的易用性和进化能力产生了巨大影响的技术呢?好吧,因为为应用部署和身份访问管理(IAM)所做的一切都可以应用于软件系统中的任何功能。在你系统中需要操作的每个功能可能都没有一个不可否认的、国际认可的标准,但部署一个本地认可的标准将在你自己的信息系统范围内提供完全相同的益处。
这种通过将信息系统划分为区域并标准化它们之间接口来工业化的方法是保持其长期功能健康状态的最佳方法。根据上下文,你可能会听到“业务/IT 对齐”、“企业架构”或“信息系统的城市化”。第三个表达是指一个隐喻,其中信息系统被比作一个现代城市:
-
组织遵循层级分区:大区域专门用于住宅、商业或工业。在这些区域内,人们会发现定义区域较小部分的社区。最后,街区将社区内的建筑物连接在一起。在一个精心打理的信息系统中,人们会发现同样的层级,其中包含大型业务领域区域(例如,行政),在这些区域内会出现专业方向(比如说人力资源),最后是功能块(在我们的例子中,是招聘管理)。
-
流体标准化是为了城市能够正确运行:如果消防员必须适应城市不同地区的不同管道直径,当然会出现问题。同样的情况也适用于电力、水管、排水管等等。今天这听起来可能有些疯狂,因为所有这些都已经完美标准化了几十年,但在 20 世纪初,像巴黎这样的城市有多个不同的电力公司,其中一些运营 110 伏,一些运营 220 伏,一些使用交流电(AC),一些使用直流电(CC),一些频率为 50 Hz,一些频率为 60 Hz,而且大多数使用不同的插头格式。
-
一个大城市总是在不断发展,城市东部的在建工程旨在对西区的居民生活影响最小。同样的情况也适用于信息系统,其中变化是唯一的不变因素,对某一部分的影响应尽可能小,以免影响其他应用程序。城市建筑师提供全局的视野和演变方向,但城市的日常变化是有机的,并且可能因为标准化而发生。成熟的信息系统也可以做到这一点。
很遗憾,企业架构似乎并不十分普及。这部分的缘由在于这是一个复杂的活动;但也因为缺乏知识和信息的传播,而本书旨在谦逊地提供一种补救措施。我将在接下来的章节中尝试展示,信息系统中的工业化和标准化方法可以带来很多价值,并且可以极大地减少大多数信息系统的僵化和变革难度,而这些知识和实践远不如大多数 IT 架构师所认为的那样复杂。
摘要
本章详细解释了工业化和标准化的概念,然后解释了它们如何应用于软件和计算机科学领域。正如前一章所述,如今许多信息系统都难以进化,尽管工业化是计算机科学中的一个新兴领域,但它是一种显著提高其效率的方法。
在下一章中,我们将更加注重实践,从本章中——诚然是理论性的——材料开始,并解释如何在信息系统中实施工业方法。将要介绍的最知名的方法被称为“业务/IT 对齐”。简而言之,它指出 IT 的结构必须反映信息系统旨在帮助的业务流程的结构。
第三章:实现业务对齐
在解释了全球信息系统的普遍问题和第二章节关于工业化的一般理论之后,现在是时候介绍一些经过实战检验的方法了!尽管在书的第二部分之前,我们不会接触代码或部署软件,但这一章将更加应用性,并将展示所谓的业务/IT 对齐的原则。这一原则背后的思想是,软件系统应该尽可能地反映它旨在自动化的业务领域的结构。从某种意义上说,这是应用康威定律(将在后面解释)的反向,使用它来获得期望的结果。在实践中,了解地图以在地面上统治是很重要的,因此我们将使用基于CIGREF(即Club Informatique des Grandes Entreprises Françaises)等组织推动的四层图的信息系统映射技术。
本章将涵盖以下主题:
-
商业软件和对齐原则
-
康威定律应用于应用和系统
-
介绍 CIGREF 图
-
使用四层图
-
对齐模式和反模式
在描述了方法并绘制了与TOGAF(即The Open Group Architecture Framework)框架或其他方法的相似性之后,我们将将其应用于一个示例 IT 系统,以便你完成本章后真正从中受益。最后,我们将看到最佳实践,但也会看到业务对齐的反模式。就像任何其他方法一样,使用四层方法进行业务/IT 对齐有其优点和局限性。了解它们对于尽可能有效地应用该方法以及了解如何使用它来确定在研究的信息系统中何时何地存在对齐问题是特别重要的。
技术要求
如引言所述,本章将比前两章更实用,这两章是理论性的。这意味着有一个阅读前提——既然我们将讨论分析信息系统的方法,那么至少你应该对它们有先前的分析接触。当然,现在每个人都在使用它们,但你需要的不仅仅是使用它们的经验,特别是关于它们由哪些不同部分组成的某些知识。这里没有什么花哨的,但你需要理解软件和硬件之间的区别,以及信息系统通常是为了自动化业务流程而存在的,这些流程是由旨在达到目标的人力和计算机任务组成的集合。你还需要能够识别这样一个系统的不同部分。如果我们把它们称为系统而不是简单的软件应用,这是因为它们更复杂,由多个模块组成。你需要理解这一点,并能够说出系统由哪些部分组成。
你还需要能够对这些系统部分进行分类。它们是根据功能还是根据更具体、与 IT 相关的标准进行分类,比如它们在本地服务器上的位置与云中的位置?它们是自治的还是与其他功能大量通信,如果是的话,通过哪些接口和协议?这当然对我们大多数人来说只是常识或常识,但这是你需要能够阅读这一章的内容。这将帮助你指出关键问题。例如,当谈论系统部分之间的交互时,我们是在谈论业务依赖还是与 IT 相关的具体数据流?为了更好地理解这种差异,让我们回顾一些示例。业务依赖的一个例子是订单系统依赖于客户名单。确实,我们可以用我们的订单记录公司,但不知道购买列表中产品的客户,记录订单就没有太多意义。另一方面,与 IT 相关的数据流的一个例子是订单系统访问客户数据库以提出现有记录。
商业软件和对齐原则
到这本书的这一部分,这应该已经很清楚了,但回顾一下也无妨,我们只是在讨论专业信息系统。简而言之,我们把自己放在一个软件真正意味着商业的情境中:对公司至关重要的应用程序,帮助商业公司或非营利组织生产的信息系统等。以下的所有建议在小型系统中都没有意义,如果应用到简单的软件应用程序上,将会过于复杂。
话虽如此,假设是,既然有商业,那么对它的了解就很好;我们知道商业的参与者是谁,商业的赌注和目标是什么,正在实施哪种战略(即使它没有 100%明确定义,这在很多公司都发生过),等等。最后这一点至关重要;如果没有定义战略方向,就没有必要设计信息系统。
重要提示
应该强调的是,即使定义(即使不完全精确)商业战略也是分析企业架构和信息系统映射的绝对前提。作为一名拥有多年信息系统经验的分析师,我总是在第一次会议中意识到没有公司级战略时拒绝一个项目,并且我建议每个人都这样做。如果你正在阅读这一章,思考你将如何定义信息系统并意识到没有业务定义的战略,你最好现在就停止阅读,等到这些基本信息更加清晰时再回来。在这方面,请相信我——如果你在信息系统目标愿景(至少是全球范围内)清晰之前就意识到即将到来的步骤,你将会浪费很多时间,而且弊大于利。
为什么这一切如此重要?因为你要使用目标业务的定义来设计与之一致的信息系统。技术应该始终服务于用户,所以事先了解业务将推动信息系统的设计(再次强调,不需要一个完美详细的战略,但至少有一个愿景或方向)。这就是所谓的业务/IT 对齐,或者在本书的背景下,简单地说就是对齐。我们将看到,这是你获得一个稳定、有远见的信息系统的唯一方法,不受第一章中描述的问题的影响。但在我们更深入地探讨实现业务/IT 对齐的方法之前,让我们先看看其他非业务相关但由技术驱动的方法,了解它们的应用范围以及在设计复杂信息系统时的局限性。
技术建议的丛林
如果你是一位真正的软件专业人士,并且担心你交付成果的质量,你肯定读过很多关于帮助解决该问题并提高你的软件技能的技术方法。你听说过V 循环;用于组织软件团队的敏捷方法;极限编程;测试驱动开发;行为驱动开发实践;用于改进代码结构的编程模式;用于跟踪代码质量的特定于开发的关键绩效指标;以及更多,一个完整的章节都不足以描述它们。
虽然大多数内容都值得学习和借鉴,但它们的数量本身也显示了这种方法的局限性:它们只在特定的背景下才是正确的(否则,由于数量众多,有些人会反对另一些人),而且遗憾的是,大多数撰写关于这些方法的人常常忘记定义这个范围,因为他们对解释技术本身更感兴趣。技术帮助他们克服特定障碍越多,他们就越倾向于将其作为一项基本、不可或缺的建议来提出。在极端情况下,推荐这种实践的人甚至没有意识到操作背景的小规模,并将这种实践视为普遍适用的,鼓励他人无限制地使用它。
当然,这里需要读者的批判性思维,但与此同时,读者在逻辑上应该是了解作者专业领域知识较少的人,他们可能难以发现内容的局限性。“知识越多越反动”,互联网上充斥着刚刚学会新技巧的人,他们乐于将其作为解决一切问题的方案公之于众。这种热情是可以理解的,我当然也在我的博客或一般培训活动中这样做过,但这并不意味着没有解决方案,而且,再次强调,前进的道路是提高读者的批判性思维。
以 KISS、DRY 和 WET 为例
让我们以一些你肯定听说过的实践为例:KISS(保持简单,傻瓜)和DRY(不要重复自己)。第一个指出,在创建软件实现时,简单性应该始终占主导地位。这在敏捷方法中尤其如此,因为额外的功能或用户的反馈肯定会迫使代码重写。第二个意味着代码永远不应该重复,并且相似的代码块应该被放入一个独特的函数中,这个函数可以从代码中需要相同功能的不同位置调用。
在进行任何进一步的分析之前,我们应该注意到,这两项建议似乎存在某种相似性,或者至少是非常强的联系。毕竟,如果我们减少了代码重复,事情就会变得更简单(或者至少它们可能看起来是这样,但这将是以下主题的内容)。因此,我们可以质疑这两种方法的使用,但再次强调,软件工程还不是一门行业,所以每种手艺都有自己的工具和用途。这很公平……
但分析这两种方法的真正要点是它们的适用上下文。与大多数技术最佳实践一样,它们并不盲目适用于所有情况,其使用应仔细考虑。当然,当你发现同一个类中多次出现相同的简单函数时,统一它是合理的,但如果有两个不同软件应用中的类具有略微不同的警告标签,这种情况又如何呢?如果文本不相同,也许这是因为警告是在不同的情况下,因此我们应该分析它们被调用的条件。然而,由于软件模块不相同,变量不一定相同,因此分析触发警告对话框显示的情况的相似性将会很困难。那么耦合呢?如果我们决定只保留一个代码,哪个应用程序模块应该拥有它?或者我们应该创建另一个模块来存储对话框的代码?在这种情况下,两个应用程序的生命周期现在影响这个公共库的版本,这可能会成为一个问题?有时,统一代码可能弊大于利。
这种讨论带来了许多反思,并提出了一个新的良好实践,即缩写WET(与 DRY 相反),代表写三遍一切。实际上,暴露出的犹豫意味着,为了找到正确的决定,等待并收集更多关于使用上下文实际相似性的线索是有益的,该方法创造者建议在考虑统一之前先写三次代码。这是一个合理的方法,因为它避免了 DRY 原则的“非黑即白”的方法,并开启了一个对应实际真相的灰色区域:它取决于上下文。先写代码,然后写第二次并观察相似性,最后写第三次并分析统一带来的投资回报,这确实是一个合理的方法…但这是否意味着它成为每个程序员都应该遵守的法律?当然不是——再次强调,需要批判性思维,三次可能并不适用于你。也许你需要五次,也许你将决定限制是基于时间而不是基于出现的次数(例如,等待一年的维护期)。这取决于你,我怀疑选择三次部分是因为它为 DRY 原则提供了一个相当幽默的对比。
所有这些都意味着,在软件开发中使用的许多技术、与代码相关的方 法有时被当作硬性真理或普遍适用的最佳实践,但,大多数时候,它们只是适用于特定上下文的原则,并且(必须)根据其他上下文进行调整。
内部工具箱/框架的特定情况
在我们切换到之前解释的问题的解决方案(而且不用担心,有一些解决方案)之前,一个最常观察到的例子是,在没有彻底的上下文分析的情况下应用最佳实践可能导致不良情况,那就是定制公司框架的开发。在撰写本章的过程中,我偶然发现了一篇来自 Aaron Stannard(aaronstannard.com/dry-gone-bad-bespoke-company-framework/)的精彩文章,恰好完美地反映了我 25 年来有或没有框架编程的分析,创建了一些并诅咒自己这样做,崇拜一些其他框架并意识到它们为负责的软件带来了巨大的价值等。
Aaron Stannard 在这篇文章中解释了 DRY 原则应用过于严格对开发专用框架以统一编码的团队产生的严厉后果,最终他们得到的结果与预期的相反,包括样板代码减少、扩展能力和代码质量。
一些框架为软件应用和信息系统带来了巨大的价值,你将很容易找到它们:
-
每个人都知道它们,并且乐于使用它们。
-
新人认为这个框架有助于提高他们的生产力和速度。
-
所有用户都可以用很少的句子描述框架的功能。
-
该框架依赖性非常少,可以轻松使用。
-
这非常重要,以至于开发者会做到他们不喜欢的事情,只是为了保持它:编写文档。
相反,有些代码框架会阻碍编程过程:
-
它们是由少数专家创建的,其余团队不使用它们。
-
新人往往会直接编写等效的代码,并质疑框架是否有助于他们提高生产力。
-
他们有时做很多事情,以至于没有任何一个功能是稳定的。
-
他们带来了一些其他限制(仅操作一种类型的数据库、需要脚本模块、提升权限等)。
-
他们的功能存在于专家(通常是单个人,这对公司来说很危险)的脑海中,并且培训同事使用它是一项挑战。
在这两个光谱端之间知道自己的位置困难,与框架的创造者当然会对他们的“宝贝”有偏见的事实有关。他们总是会高估使用它节省的时间,或者忘记或高估维护它的时间,向团队中其他人传授它的时间,公司把重要依赖放在一个人手中的风险,等等。如果你必须评估框架的使用,你应该将这些因素放入电子表格中,并冷静地评估短期、中期和长期的回报率。
最后,现代编程平台现在已经带来了如此多的工具,以至于框架的存在本身都值得怀疑。例如,.NET Core 框架版本 8.0 拥有如此庞大的生态系统和基类库,十年前框架被创建的所有目的都简单地消失了:
-
对象/关系映射由Entity Framework负责
-
带有对象映射的 API 请求/响应由ASP.NET Web API和集成的JSON/XML序列化处理
-
监控由可以连接到任何第三方监听器的日志堆栈负责
-
一致的页面描述是通过Blazor完成的,包括样式处理
-
移动应用的部署是通过Multi-platform App UI(MAUI)实现的,它还与 Windows 前端统一,等等
因此,我对这个关于框架的建议是尽可能等待,在创建框架和尝试避免它(大多数时候,承认吧,你想要创建框架并不是因为它对你的业务有好处,而是因为它编写起来很有趣)。如果它必须到来,它就会到来,这就是新兴代码架构(如果你还没有听说过这个概念,我们很快就会回到这个概念)发挥作用的地方。
唯一稳定的指南:与用户的业务对齐
在这次漫长的离题之后,我们回到了最初对软件中何时应用最佳实践和所谓“原则”的难度的思考。然而,我们迫切需要关于信息系统架构的指导,因为风险太高,我们也看到了如果在这方面失败,对业务的影响有多么糟糕。那么我们如何知道?是否存在一个始终适用的合理且稳定的方法?一些我们确实能够真正依赖的、在设计对公司未来至关重要的信息系统结构时能够真正依赖的定律?是的,确实存在,而且它不是一项技术规则,而是一种技术决策的方法:始终将其与信息系统的业务联系起来。这是业务/IT 对齐的根源。
在本章中,我将重复这个规则,并以许多不同的方式解释它,因为它对于正确的信息系统架构至关重要——推动信息系统设计的是业务的架构。这一点对于概念尤其如此,为一家公司创建成功的软件骨架总是从对业务的完美理解开始。客户是什么?我们销售什么产品?公司应该计算机化的主要流程是什么?大多数这些问题听起来很平凡,但它们之所以如此,仅仅是因为人类大脑能够适应背景环境。
让我们首先来探讨一个非常基础的问题:什么是客户?对于公司里的任何一个人来说,这个问题显然得出了答案,以至于很少有人会提出这个问题,如果任何员工质疑它,听起来会相当荒谬。“客户是我们与之做生意的公司”。好吧,那么个人呢?是的,有时我们也会与个人打交道;这是 B2B 和 B2C 之间的区别。如果我们质疑“做生意”这个术语呢?我们谈论的是频率和数量?当然,任何数量都可能被考虑在内,一个人从你这里买一个简单的螺栓可能被认为是一个客户,就像一个公司每月购买数千个螺栓一样。但是时间呢?你会认为二十年前从你那里购买过产品的某个人仍然是客户吗?不,当然不会。那么一年前呢?是的,当然……在这种情况下,界限在哪里?十年?五年?决策,决策,决策!
关于这个先前的例子,你可能会发现一些公司,那里的经理在营销和商业之间对客户的精确定义并不一致……那么,你期望一个愚蠢的计算机如何来决定这个问题呢?残酷的事实是,如果你想用软件和数据流来替代一些任务,你必须让它绝对清晰,才能让它工作。这就是大多数信息系统失败的地方——它们没有以完美的业务视角来设计,让一些细节留给了人类对系统的实施或使用。当人类在计算机中补偿缺失的知识时,它可能在一开始还能工作一段时间,但迟早会出现问题。在最好的情况下,系统永远不会像预期的那样高效。在最坏的情况下,带着他们补偿性知识离开公司的人可能会使系统陷入停滞。
这个例子告诉我们什么?首先,在制作软件信息系统处理业务定义时,应该非常明确。其次,这些概念往往是业务规则,这意味着它们并不完全稳定,而是取决于业务如何运作。如果有一天你的老板决定客户名单不应该包括在过去三年内没有从你那里购买过任何东西的人,而之前这个期限是五年,那么客户的定义可能会随着时间的推移而改变。如果这种情况注定会时不时地发生,那么当然,限制其对信息系统的影响是至关重要的。将这个逻辑放入统一的一行代码中,或者更好一点,放入一个参数中,在这种情况下是一个很好的举措。
相反,将客户列表及其定义存储在单个表中将导致高度耦合,因为当“客户状态”发生变化时,定义(名称、地址、联系方式等)不会改变。如果从客户列表中删除一家公司意味着您必须从数据库中删除条目,那么这可能会影响其他仍需要这些数据的功能(例如,保证管理、会计师等)。我们将在第九章中回到这个例子,并详细介绍如何正确建模的一些细节,但就目前而言,请记住,业务思维必须始终指导您的软件概念如何运作。对齐不是从无中生有或双向产生的——它是由功能驱动的,软件应该盲目遵循业务领域本体。
事实上——这将是本节最后的要点——坚持业务领域现实甚至应该更进一步,特别是要随时间坚持。这意味着您的模型应该始终适应时间,因为业务总是变化的。变化是生活中唯一不变的因素;企业无法逃避这一点,反而高度依赖于它。随着不断变化的业务规则、越来越复杂的组织以及更高的功能复杂性,信息系统必须从一开始就设计来适应变化。时间管理非常重要,下一章将完全致力于此。
关于公司数字化转型的插曲
当我们谈论业务领域的软件表示时,我们不妨进一步探讨这一点,并观察软件是如何“吞噬世界”的(《软件正在吞噬世界》,马克·安德森,2011 年)以及为什么这种积极的数字化转型与之前讨论过的业务对齐概念相关。一个架构胜过千言万语,图 3.1和图 3.2应该解释数字化转型对我们运营方式产生的主要差异。
在数字化转型之前,人工操作员是业务运营的中心,并在现实世界和业务(或其部分,如我们之前所见)的计算机化视图中进行操作:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_1.jpg
图 3.1 – 当人工操作员在业务运营中心时的架构
数字化转型带来了一种全新的方法,其中软件成为人类的主要操作工具,并在现实世界中代表人类用户进行操作。硬件和软件系统从人类界面和传感器接收命令和信号,将它们翻译,并将它们作为图形用户界面发送回人工操作员(以及具体现实,通过机械操作员或其他方式):
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_2.jpg
图 3.2 – 数字化转型的架构
这有一个特定的后果,即 IT 现在处于中心位置,与现实世界和人类进行交互。根据你的观点,计算机帮助人类避免了直接交互带来的麻烦,或者它们使我们远离了与世界的直接互动以及与潜在偏见和现实错误表征相关的所有风险。社交网络是这些负面影响的最重要表现,作为软件工程师,你应该始终意识到这种风险,并相应地设计系统,因为它们对现实世界的影响现在已经确立,该领域的从业人员应该保持道德和负责任的态度。
希望这使数字转型和人与计算机之间的交互更加清晰。现在让我们集中讨论人类如何影响软件的组织方式,并讨论一个称为 Conway 定律的东西。
Conway 定律应用于应用程序和系统
我们在本章的第一节中谈了很多关于所谓“软件定律”的局限性,所以你可能想知道为什么我现在会花几段篇幅来谈论一些乍一看可能相似的东西。没有什么能比这更不同了…Conway 定律是信息系统设计的真实、稳定的指南,因为它不提出建议,而是从多个观察中提炼出一个理论,并让一个人自己对其主题得出结论。
Melvin Conway 在 1967 年指出,“任何设计系统的组织都会产生一个结构与其沟通结构相复制的设计”。在我们的案例研究中,即信息系统,这意味着结果系统的架构将反映定义该系统的团队的结构性组织,这会意味着以下内容:
-
一个前端和后端之间有强烈分离的团队将产生一个系统,其中这两个软件功能确实是独立的
-
一个根据人们的业务领域知识分组的人将产生一个具有清晰业务对齐服务的信息系统
-
一个只有一个人的团队将产生一个非常紧密、类似单体系统的系统
-
一个部分之间没有或很少沟通的团队将创建一个系统,其中模块之间不能正确地相互操作
这个最后的例子可能看起来很极端,但遗憾的是,大多数信息系统都是这种情况,因为团队通常是以单一软件组件为出发点组成的,导致许多应用程序组成系统时,事先没有考虑到相互操作。因此,链接是在需要时以点对点的方式建立的,导致脆弱的链接和低效的系统。
自从康威的这一初始经验观察以来,同名的法则已经被验证了许多次,尽管它不能像数学法则那样被证明,但现在被认为是非常可靠的。它被认为是一个如此强大的法则,以至于系统设计者开始使用这个法则以结构化团队的方式,使最终系统呈现出期望的形状。在这种方法中,法则不仅被视为一个后果,而且被视为塑造系统所需的辅助工具。我所谈论的不仅来自我的个人经验,而且已经被正式化,命名为逆康威机动,因为许多其他软件工程师也有同样的方法。例如,马丁·福勒(Martin Fowler)提出了这个法则(martinfowler.com/bliki/ConwaysLaw.html),甚至将其与领域驱动设计(DDD)联系起来。这将在下一章中进一步解释,该章节专门用于解释与设计信息系统部分相关的语义——与 DDD 中的通用语言概念相关——的重要性。
使用逆康威机动,可以通过影响设计该系统的团队之间的沟通和结构来影响系统的最终设计。这是实现我们自本章开始就讨论的非常需要的业务对齐的绝佳方法。通过定义与业务领域并行的团队,并给他们提供相互讨论的业务相关概念,最终形成的系统将由具有明确功能责任和模块间良好交互操作的模块组成,有利于系统的长期演变。
影响系统的对齐是很好的,但大多数时候,人们会遇到一个现有的对齐,唯一可能的是理解其状态。这就是我们需要一种方法来分析现有对齐的地方,这就是专门的图表方法有用的地方。
介绍 CIGREF 图
正如我们所见,业务对齐与与功能领域相关的词汇和概念的正确表达有很大关系。对于我们更习惯于模式的人来说,存在一种更图形化的方式来可视化这种对齐,这被称为四层图。
在法国(我的祖国),它被法国大型企业信息技术俱乐部(一个大型法国软件架构俱乐部)普及,但这是非常普遍的一种思维方式,并且没有人声称拥有这个想法,至少据我所知。这个概念相当简单,是关于将信息系统不同层级分开,每个层级都使用我将要介绍的层级来工作。在图的顶部,人们会找到系统所服务的业务流程,再下一层是为此所需的业务功能。这两个层级完全是功能性的,甚至与软件无关;一些任务和功能可以通过人类实现,而不会产生任何影响。底部的两个技术层级分别是软件和硬件,前者使用后者。指定系统(或更确切地说,其最外层结构)的这种方式可以概括如下:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_3.jpg
图 3.3 – 四层图
让我们在接下来的几节中深入探讨每个层级,特别是展示每个层级的详细内容是如何表示的,因为这个方案只是象征性的,并不包含每个层级的实际内容。
重要提示
在本章的剩余部分(以及本书的后续部分),我们将经常通过它们的编号来引用这四个层级或水平,从顶部开始(层级 1、层级 2、层级 3和层级 4)。下一节将详细介绍层级 1,以此类推…
流程层级
业务流程是一系列旨在达到既定目标的任务。由于我们谈论的是 IT,其中当然至少有一些将是自动化的,但也可以有由人类激活的任务。流程用于结构化业务或任何组织的活动,业务流程建模(这是接受的名字)是关于表示这些以记录、更好地理解并提高实体的效率。
存在一种业务流程表示的标准,即 BPMN,代表 Business Process Modeling and Notation。这个标准目前是 2.0 版本。你肯定已经见过这种图表,它自己就能读:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_4.jpg
图 3.4 – BPMN 示例
在这个公司新流程的非常简短且不详细的例子中,你会发现 BPMN 标准中最常用的组件:
-
任务是带有文本的盒子,通常以动词开头,描述所表示的活动。任务内的图标可能指定它是否是自动化的、有用户输入、完全是手工的等。
-
任务之间的箭头表示流程中的信息流,以及任务处理的顺序。
-
菱形框会介入这个信息流中,以包含诸如选择或并行活动等复杂性。
-
事件由圆圈表示。一个流程总会有一个开始和一个或多个结束。它也可以有中间事件,所有这些都可以反映事件类型,如基于时间的、基于消息的等。
小贴士
关于 BPMN 的更多信息,我强烈建议研究可从www.bpmb.de/index.php/BPMNPoster获取的 BPMN 海报。
尽管 CIGREF 图的第一层主要基于 BPMN 图,因为它们是业务流程表示的标准和这一层的主要内容,但在这个层次中也可以找到一些附加信息,这些信息是相互关联的。例如,可以指定与业务领域相关的规则为DMN(决策建模符号),并将其添加到这一级,因为它们对业务流程本身有影响。顺便说一下,DMN 是 BPMN 标准中包含的一个“子规范”。
根据想要获得的内容,第一层地图可能非常粗糙,细节很少。这是一个我从一家动物遗传学公司那里得到的例子,该公司已经对其流程进行了完整的 ISO-9001 图示,只需知道哪些信息系统部分与哪些业务流程相关(出于保密原因有意模糊):
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_5.jpg
图 3.5 – 流程图的示例
由于我的这位客户在流程本身上没有问题,在整个对齐项目中,其表示保持得很简单。这里的唯一微妙之处在于,运营流程已经与支持流程和试点流程分开。
相反,以下图是我为另一位客户创建的地图中第一层众多流程之一,这次是一个 IT 问题主要来自流程定义不足(当然,当目标不明确时,很难有一个高效的 IT 系统)的组织。在以下图中,文本的可读性不是目的,因为我只想让你看整体流程图:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_6.jpg
图 3.6 – 详细流程
第一层当然是你的信息系统地图中最重要的一层,因为没有任何东西依赖于它。所有其他层都依赖于它,但流程层必须从头开始设计,纯粹基于业务领域知识。而且,正如所解释的,如果流程不清晰或设计不当,这当然会影响软件,随着时间的推移,也会影响整个信息系统的效率。
强调这一层的重要性很难过分。这并不意味着它必须用很多细节来绘制;这些细节只有在有人发现流程中的问题并需要深入了解以改进或纠正它时才是必要的。但第一层必须正确,并涵盖整个组织的范围。
功能层
CIGREF 地图的第二层表明了上述流程的任务可以使用哪些功能来实现。这仍然是一个功能层,但这次,组织不同,因为这个层的原子是关于谁可以做什么。
确实存在一些不随流程变化的函数。业务流程必须能够根据战略进行变化,但有些事情是稳定的。在我们的图 3.3示例中,欢迎新员工加入公司的一项任务是拍摄他们的照片。这可能对其他流程中的某些事情有用,但拍照这一行为是组织的一部分,可能由特定的人来完成,并需要专门的材料,如相机。因此,这是一个函数,必须在这一层进行登记。许多流程可能会指向它,但这个函数将在第二级地图的一个地方保持不变,与其稳定的属性一起。
其中一些最重要的属性与组织内部函数的组织有关。事情可能会变化,没有固定的标准,但至少有一个使用了数十年的好隐喻,并且取得了良好的效果,即创建信息系统与城市组织之间的平行关系(在法国,这种方法被称为“城市化”)。这个隐喻导致将 CIGERF 地图的第二层分解为三个深度的层次:
-
区域/区域是这一层中的较大群体。它们对应于整个系统(或在我们的隐喻中,城市)的组织方式。在一个城市中,人们会发现商业、工业和居住区通常明显分开;在功能层中也应该找到同样的情况。
-
季度/邻里是对系统本地组织的一种更细的划分,其中人们、企业,或者在我们的案例中,IT 功能可能会相互交流。
-
块/岛屿是人们相互认识并经常互动的精细划分。在相应的 IT 定义中,这些包含使用相同工具或由相同团队操作的相关函数。
我经常被问及应该使用什么样的分解方法,至少对于最高层来说。这是一个难题,因为业务能力地图(BCM)(正如这个 CIGREF 地图的第二层经常被称呼)在大多数公司中实际上是不存在的。由于 ISO 9001 认证和相关方法的标准化,许多公司对自己的流程有清晰的了解。公司也可以追踪他们的软件层,至少对于他们被收费的最大块来说是这样。但在中间,这个 BCM 经常被遗忘,我们将在稍后看到,这是系统不匹配损失的原因。
这一层被遗忘的事实本身在很多信息系统的问题中就占据了很大一部分,因为领导者可能更倾向于过程层(目标)而投入较少的努力在业务连续性管理(如何实现它们)上。然而,正如谚语所说,没有计划的愿景只是愿望,而精心构建的系统功能组织是迈向成功的一大步。
在这个与城市发展相关的隐喻中,业务能力图被切割的方式与一个大城市的组成相关联;就像城市中有工业、商业和居住区一样,在信息系统中也将会有一个全球组织,通常在以下五个区域:
-
主数据管理是信息系统最重要的数据被管理的地方(客户、产品等)。这些数据将被系统的许多部分和参与者使用。因此,它值得专门的治理(明确的责任定义、实践和工具等)并在地图的第二层拥有自己的专用区域。当对如此重要的数据缺乏明确的治理时,常常发生不同的小组会重复它们,这不仅成本高昂,而且在需要实现交换时可能会带来复杂的问题。
-
共享工具不是数据,但仍然是将在信息系统和大多数流程任务的其他许多部分中引用的常用功能。它们通常被分类在专门的区域,在那里可以找到办公自动化工具、内容管理系统、身份和授权管理等等。
-
面向外部的功能(有时称为“协作”)是关于与信息系统本身范围之外的功能进行互操作或交换的所有功能。这通常是人们会找到外联网或商业网站、与合作伙伴的联系等地方。
-
治理/试点是那些曾经用于监督系统本身的功能所在的区域。报告功能将被放置在那里,以及关键绩效指标、高层管理功能等等。
-
面向业务的职能是最后但同样重要的一个领域。这是公司组织与其核心价值和运营领域相对应的所有职能的地方。如果你是一家制造机械部件的公司,你将在这里找到所有与工程、生产、库存、销售、维护和安装相关的职能。如果你从事电子商务,将会有买卖职能、物流、网络运营、安全等。还将有一些支持核心业务的职能组,如人力资源、法律和行政职能,这些在大多数公司中都是通用的。当实体组织得很好时,这一区域的不同方向与组织结构图中的不同方向相对应是显而易见的(并且是良好一致性的证明)。相反,如果你很难判断某个特定的职能是否属于这样的方向或服务,这可能是一个缺乏一致性的迹象。
下一个图例是一个遵循五个区域原则的 BCM,主要部分按照组织结构图的方向进行分解。再次强调,这只是一个常见的模式,并不构成任何推荐。应根据个人判断调整 BCM 的分解以实现一致性。再次强调,就以下图表的文本可读性而言,文本内容并不重要,重要的是结构:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_7.jpg
图 3.7 – BCM 示例
对于这个轶事,这五个区域也出现在法国政府的信息系统 BCM 中,四个支持区域围绕在中间,而面向业务的职能位于中间,在这个背景下,由法国政府组成的各个部门将其分开。我用黑色线条突出了这些分隔,因为这正是我想展示的(文本的可读性并不是目的所在):
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_8.jpg
图 3.8 – 法国政府 BCM
我们将回到 CIGREF 第二层的重要性。正如所述,第一层表明了愿景以及公司为市场带来的价值,但第二层则是关于如何操作性地实施这一愿景,这正是无序信息系统所缺乏的地方。在我能够观察或帮助精炼的数十个信息系统中,业务连续性管理(BCM)总是最不受控制的层。这种对它的错误处理是信息系统无法令人满意的根本原因。
软件层
在 CIGREF 地图的下一两层中,我们进入了技术的领域。到目前为止,流程和功能完全是业务相关的,一个人完全可以使用它们来构建一个无计算机的信息系统。但今天我们并不是这样,这本书的分析主题也不是这个。我们将研究基于计算机的系统,至少部分是自动化的系统,而上面两层所依赖的两个技术层是关于用计算机实现功能的。它们的分离非常简单:第三层是所有“软”的东西,这意味着非物质的、虚拟的和非具体的,而第四层则将所有具体的东西分组。简单来说:如果你能触摸到它(计算机、网络电缆、设备、数据中心——即使墙壁不属于你),它将进入我们稍后将要覆盖的下一层。如果它是技术性的但不可“触摸”,例如软件应用、数据库、信息流、API 实现等,它属于 CIGREF 地图的第三层,我们马上会详细说明。
这个第三层在第一次实施时通常对公司来说相当容易:查看账单并询问人们他们使用什么软件,通常足以找出系统中软件的 80%最重要的用途。但是,即使详尽的参考不是目标,这也可能不足以两个原因。
首先,可能存在一些公司不知情的情况下购买的“隐藏”软件(这被称为影子 IT,可能是一个需要维护或强烈所有权的难题)。如果这些软件是战略性的,它们可能不会出现在地图上,而且如果使用它们的人突然离开,相关的功能在没有人的理解下崩溃,这也可能成为一个问题。如果你听说过某个公司在关键人物退休后出现软件问题,这就是我们要讨论的内容。
第二个问题是,软件不仅包括应用程序,还包括数据,而数据通常在系统中更难定位和追踪。当然,你可以通过它们的商业许可证或使用的 IP 和端口来定位数据库。但你会发现数据存在于许多其他地方,比如令人讨厌的 Excel 工作表。再次强调,谁没有听说过一份对公司来说如此重要的 Excel 工作簿,以至于每个人都听说过它?在我陪同的一家在业务/IT 对齐的公司中,有一个叫做“Serge 的 Excel 文件”,当我试图弄清楚公司文章和价格的真实来源时,每个人都向我提起过这个文件。结果发现,在这个近千人的公司中,根本没有任何产品信息管理的治理,而这个名叫 Serge 的人,在某个他急需信息的时候,承担了从商业、行政和工程收集数据并将其汇总到 Excel 工作簿中的任务,尽力追踪变化、新产品、停售日期、价格变动等。由于这不是他的主要工作,他几乎没有时间这样做,文件的内容既不完整也不免出错。但既然这是唯一可用的数据来源,每个人都迅速复制了 Serge 的文件或通过服务器链接引用它。管理者们从未考虑过这种对如此重要数据来源(也许甚至是一家商业公司的首要参考,连同客户名单)的脆弱方法。当 Serge 最终离开公司时,发生了什么?系统逐渐恶化,因为没有人负责维护这个单个人的、未记录的工作。信息变得混乱,订单开始出错,价格无法调整,因为大多数使用文件或在其上创建的连接器中的信息的人根本不知道数据从何而来,等等。
有些人可能会反对说,我所谈论的并不是第三层的一部分,而是与第二层相关,确实,主数据管理、数据治理和所有权必须在第二级详细说明。但在这个案例中,我想表明,糟糕的技术实现(这肯定属于第三层)以及对软件形式中的数据位置缺乏理解是问题的根源,这个问题直接影响到地图的第一层,并导致公司的两个主要流程——生产和销售——脱轨。
对这一第三层内容的分类实际上取决于许多因素。一些拥有强大内部 IT 和编程能力的公司倾向于由操作该技术的技术团队对应用程序和数据分组。我也见过其他人按技术对软件层进行分组,尽管他们的大部分技术是现成的,但他们的主要关注点是内部操作这些技术。在某些情况下,软件可以按编辑进行分类。还有许多其他分类方式。在下一个例子(再次为了保密原因而模糊处理)中,切割是使用功能域进行的,因为公司相当大,软件应用程序和数据的责任受到了业务方向和服务的影响(这是一个很好的实践,因为软件应该始终服务于功能):
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_9.jpg
图 3.9 – 软件层
硬件层
如前所述,硬件层列出了并组织了从具体的信息系统到的一切。毕竟,计算机化系统的重要部分是数据和虚拟功能,它们极大地加速了过程,但我们绝不能忘记,即使在遥远的云位置没有人真正关心,所有这一切都是由电子在电子芯片和电路板中流动实现的,还有电源、电缆、硬盘和屏幕。
这个层现在非常标准化并且受到控制,在我分析过的数十个信息系统中,几乎没有一个因为硬件问题暴露出这种限制。事实上,这种情况如此罕见,在大多数我制作的 CIGREF 地图中,第四层非常薄,几乎没有细节,有时甚至根本不表示。例如,如果我们看我所展示的不同层的整体图景作为样本,那么硬件层就没有任何表示:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_10.jpg
图 3.10 – 只有三层(文本的可读性并非目的)
幸运的是,公司老板们非常清楚的一件事会始终提醒我们这一层的存在,那就是他们的成本。即使机器越来越虚拟化,变得无形,财务成本仍然存在。随着时间的推移,信息系统和数据中心的环境影响最终也成为方程的一部分,使得这一层变得更加明显。
如果你必须绘制一个硬件层,你会发现很多优秀的绘图系统,它们可以区分服务器类型,可以提供专用图标和元数据,以便你可以单独列出硬件原子等。总的来说,再次强调,这是一个非常受控和标准化的层,这确实解释了为什么在谈论 IT 对齐时,我们很少需要在这个层上工作,除了引用它来完成软件成本和平衡第一、第二层预期的收益。
为了提供一个硬件层的例子,这里是一个公司在逐步外部化其 IT(橙色部分是直接由 IT 服务操作的服务器)中的这样一个层图的时间序列:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_11.jpg
图 3.11 – 硬件层
由于这个层(当然不是层本身)的表示相对较低的重要性,我们不会进一步深入描述如何使用该图来映射硬件层。分组也很明显;大多数情况下,它们基于数据中心,物理位置在顶部,然后是独立的物理服务器,接着是虚拟机等。网络也用标准符号表示,总的来说,对于这个层,图是通用的。
使用四层图
CIGREF 映射的原则现在应该已经很清晰了,因此我们可以看到如何使用这项技术来提高对齐度,从而提高信息系统的效率。正如所说,在接管一个系统时采取的第一个行动就是创建它的映射。否则,简单地没有方法能够舒适地处理这样复杂的集合。这意味着当然,为现有状态的信息系统创建映射是首先要采取的行动,而 CIGREF 映射非常适合这个目的。但关于如何做到这一点仍然有很多问题。本节包含了一些经过实战检验的建议,关于如何使用映射技术。
作为一条重要提示,不要担心你在本章结束时还没有确切地知道如何使用 CIGREF 方法。现在,我将只向你展示如何绘制它以及如何在其中发现业务/IT 对齐的问题,但本书的其余部分将展示许多 CIGREF 映射在实际中用于许多不同目的的例子。这意味着它将有望变得更加清晰,它在分析和构建信息系统方面的强大功能,你不应该因为还不确切知道它在实践中将如何被使用而感到担忧。
我们应该映射什么?
首先,一个人永远不应该详细绘制整个系统。当然,有一个覆盖整个周界的粗略地图来了解我们正在处理什么是至关重要的,但只有需要关注的系统部分应该被详细绘制。这听起来可能很显然,但在我所指导的团队中,有很多次在解释了方法几周后,我意识到他们已经详细绘制了信息系统的每一部分。因此,这个建议需要被提出。
这似乎是一种自然的反应,特别是对于系统中有严重问题的团队来说,绘制一切,因为它给人一种恢复一点控制感的感觉。遗憾的是,这不仅是在创建地图时的浪费时间,而且在人们试图根据系统的演变调整图表时也是浪费时间。这不是系统绘图的用途。地图是用来预测演变并在尽可能多的方向上推动它的。因此,它只应该在我们正在工作的系统部分进行详细绘制。提前绘制是浪费,因为我们大多数情况下在开始处理这个系统部分时还得重新绘制。同样,绘制一切也是浪费时间,而且更多,因为(希望)系统中有一些部分我们永远不会处理,仅仅是因为它们已经运行得很好了!
再次,当解释时这听起来可能很显然,但当一个人开始绘制一个信息系统时,似乎会发生某种狂热的绘制过程,我见过很多受过良好教育且有经验的人意识到他们白费力气了(我最好的例子是一个拥有数千名员工的组织,其 IT 团队为建立进入内部餐厅的徽章绘制了一个流程图;当然,这个流程图从未被使用过,甚至除了它的创作者外没有人读过)。
以下方案从视觉上解释了地图应该如何随时间演变,只有当需要时才详细绘制系统的部分:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_12.jpg
图 3.12 – 绘制演变
这个问题的另一个后果——而且更为微妙的是——那就是那些已经详细绘制的信息系统部分,在地图被用来改进它们之后,就不再需要更新了。在图 3.12 中,这就是第二步骤中已经细化的四分之一所发生的事情。在第四步骤中,它不再详细了。这听起来可能有些反直觉;的确,一旦对这部分进行了详细的工作,为什么就放弃它呢?同样的原因适用:如果这部分现在组织得很好,并且业务和 IT 之间已经达到一致,那么我们可能不会回到它这里,或者如果我们确实回到了这里,地图可能已经改变了。那么为什么还要浪费时间在上面呢?
如何开始绘制四层图
从一张空白的纸开始总是困难的;当 IT 问题影响到公司的活动时,这更加困难。为了简化开始映射活动,有时使用纸张或白板来给出系统的初步视图会更容易一些。我喜欢展示以下这个信息系统的“初稿”,因为这个系统是因为一个与之相关的轶事而被我分析:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_01_2.jpg
图 3.13 – 使用 CIGREF
这家工业公司在稳定科学分析和将结果传达给客户的数据流方面遇到了困难。我们(IT 团队和我)开始花几个小时绘制四个主要流程、不完整但足够接近的 BCM 以及涉及其中的应用程序以及数据流。在那个阶段,我们正准备评估所绘制的交换频率以确定弱点。然后,公司的总监经过,看了我们做的几秒钟,指着代表应用程序的两个便签说:“嗯,问题显然在这里”。这个人没有任何技术或 IT 背景,但看到大量数据流会进出这两个实体,他立刻理解到它们阻碍了系统的效率,特别是其演进能力。经过进一步分析,发现其中一个应用程序已经过时,第二个应用程序存在复杂性问题。因此,系统被重新设计以改进,主要步骤是为第一个应用程序集成一个新应用程序,并为第二个应用程序创建一个超集 API,以更干净的方式公开旧功能。
这显示了良好信息系统映射的力量,因为它有助于所有参与者——而不仅仅是技术熟练的人——理解他们 IT 中的情况,现在几乎在所有地方,他们的主要工作工具就是 IT。
在对信息系统进行对齐时遇到的一个普遍困难
系统中几乎总是会发生的一个问题,并且会对其映射产生影响,那就是在第一层和第三层的依赖关系之间经常出现混淆。大多数功能人员倾向于认为他们的 IT 系统实现了与设计完全一致的过程;他们多么天真啊… 软件不一定是在内部制作的,当从货架上购买时,几乎不可能完美地符合公司的流程。当然,每个新的软件项目开始时,每个人都会发誓他们会遵守编辑的逻辑,调整他们的流程以保持解决方案通用,避免昂贵的特定定制。但大多数情况下,现实是 IT 最终会像在方孔中锤圆钉一样整合软件,结果不会整洁。
当职能人员用技术术语表达自己,而没有完全理解他们所说的话对系统的影响时,这种缺乏区分的情况也会发生。来吧,我们都有过经理或大老板谈论 ESB 或 ETL,好像他们自己能直接用 XML 编写 Camel 路由的经历!这种对 IT 简单性的过度自信,一旦绘制了 BPMN 并使职能作者确信它可以直接执行,就像一个已经存在端点的 WS-BPEL 架构一样,也会带来一些有趣的关于项目持续时间的说法。
让我们举一个例子,并想象你已经得到了一个像这样的 BPMN 架构(法文标签无关紧要;只需观察图表的结构以及所有任务都是手动的事实):
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_14.jpg
图 3.14 – 手动流程
现在想象一下,由 IT 提供的实现此过程对应的数据流图,可能就像这样:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_15.jpg
图 3.15 – 软件中的流程
任何技术专长都不需要意识到,流程中的任务与软件应用程序和数据库之间创建的数据流之间没有任何关系。因此,信息系统映射的大部分工作将是关于在任务和相应的数据流之间绘制关系。最终,你可能会发现其中一些流是不完整的,需要其他流来弥补数据不足。你可能会发现将过多数据带入甚至没有权限查看的软件中的流。你甚至可能会发现不再有已知用途的数据流。
要关联这两层,您将不得不创建一个强大的业务能力映射,因为第 2 层将用作流程及其软件实现之间的间接层。这就是为什么 BCM 如此重要的原因,如果您在信息系统中工作,您会意识到这通常缺失,仅仅是因为它不如流程和软件/硬件为人所知。然而,BCM 对于减少耦合并促进系统的演进至关重要,因为它提供了一种创建依赖关系的方法,而不会因为一旦设置就难以更改的技术实现而陷入困境,或者因为公司运营方式中的流程被修改而不得不更改 IT。在这种情况下,BCM 充当一个间接层,使得第 1 层的演进成为可能,同时对第 3 层的影响最小,反之亦然。
以下架构图总结了这一点,顺便说一下,我经常告诉我的学生,如果他们只记住我关于 IT 对齐课程中的一张幻灯片,那应该是这一张:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_16.jpg
图 3.16 – 优秀的耦合
随时演进信息系统
当然,在映射现有信息系统状态之后,下一步是改进它,这需要有一个多步骤的战略来使其更加现实。再次强调,CIGREF 地图在这里提供帮助,通过清楚地说明每一步需要调整的内容,并展示如何处理依赖关系。如果通过将其插入到一个新的、改进的软件应用程序中来更改一个功能,所有依赖于这个功能的任务都需要进化以利用这一点,当然,除非有 100%的兼容性,在这种情况下,我们可以认为该功能本身没有改变。
这些步骤的最终目标是尽可能接近(且具有成本效益)一个现实且良好对齐的系统状态,这可以概括如下:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_17.jpg
图 3.17 – 理想对齐
当然,这听起来可能很理想,而且,大多数时候,我们可能永远无法实现整个系统的对齐。但局部对齐是可以实现的,如以下图例所示,这是我从一个成功对齐其最重要的支持流程(即处理新员工,因为这是一家高结构性流动的公司)的客户那里导出的。通过这种方式,所需的努力可以减少到估计的十分之一,流程的持续时间将减少到最初测量时间的三分之一。这是通过自动化一些任务实现的,但正是对齐使得这种自动化成为可能,因为在此之前,流程完全是手工的,由于 IT 结构明显缺乏(该公司从事非技术业务,在 IT 上投入有限的预算,只有在达到几乎绝望的状态后,才意识到它对其活动的重要性)。以下图表因保密原因而模糊;我们旨在展示流程的流程:
图 3.18 – 应用对齐
我们将在下一章回到信息系统随时间演化的过程。现在,只需记住,CIGREF 地图必须用于建立实际情况,但也可以用于模拟一个期望的未来情况以及中间的每一步。
服务提供商的四层图方法
你可能会对在购买软件即服务时使用 CIGREF 地图的用途感到好奇,以及它是否意味着我们在硬件层不绘制任何内容。答案回到了你想要使用你的地图的方式,因为你不是出于拥有完整东西的乐趣而映射,而是因为你的用例需要你精确地了解你的信息系统在其细节中的工作方式。这意味着,如果你的兴趣在于软件匹配功能,你绝对不需要知道服务器在哪里物理上,在这种情况下,在第四层绘制任何内容都没有用;你只需将其留空。另一方面,假设你的一个担忧是关于你数据的位置性,因为你的董事会有一个关于数据主权的限制。在这种情况下,你将在第四层中指出支持软件的数据中心所在的精确区域。这样,就很容易发现任何位于允许区域之外的东西。
这回到了一个非常关键的忠告:只映射你真正需要的。很容易被带跑偏,映射很多实际上并不关心的事情。特别是,硬件层通常很容易映射,因为自动网络探索应用可以提供帮助,而且好的系统管理员通常在每个机器上部署了代理以进行安全和软件清单。因此,人们往往会有一个非常精确的第四层图,即使他们的问题是关于功能与软件的匹配。在这种情况下,你最好用几个通用块来替换整个硬件层——通常是使用的数据中心,包括你自己的服务器室——并用相关的成本标签它们,因为这(在这种情况下)是唯一能帮助你信息的信息。
谈到服务,可以从另一个角度提出一个等效的问题,即关于为软件编辑器或集成商提供软件服务的 CIGREF 表示。我们应该如何表示它?客户使用的云是否是编辑器信息系统的一部分?是否应该以特定的方式绘制?
再次强调,地图的有用性观念应该驱动我们对这一问题的响应。想象一下手头的问题——也就是您建立地图的原因——是因为您在内部功能和客户提供的功能之间有一个耦合问题。这可能是一个安全问题,因为一方面勒索软件可以轻易传播到另一方面。这也可能来自会计团队,他们不知道哪些机器和服务应该向客户收费,哪些成本应该保留在内部。也可能来自系统管理困难,例如关闭一个被认为是内部的服务器最终影响了您的生产。在这种情况下,正确的做法是绘制当前独特的信息系统,然后绘制目标地图,该地图由两个不同的信息系统和它们之间剩余的精确交互表示(例如,从生产信息系统向内部信息系统发送使用数据,以便会计可以为不同的租户建立账单)。
这通常是您在业务能力图(CIGREF 表示的第二层)的“外部面向功能”区域使用区域的地方。在生产信息系统中,您会在这个区域找到“按租户报告数据使用情况”或“按租户发送总 API 调用次数”的功能。而在内部信息系统中,您当然会在“业务”区域/“会计”区域找到处理这些数据并计算租户账单的功能。另一个例子是在“外部面向功能”区域可以找到的“请求租户访问阻止”或“存档租户”等功能。它们通常由内部信息系统调用,以指示生产信息系统客户未支付账单,至少应该被阻止,也许以后,完全移除。
两个系统之间的另一个链接例子,当然是当内部软件-生产工作流程产生了一个经过验证的、完整的、销售给客户的软件新版本时(这是软件编辑公司的主要角色)。必须存在某种链接,因为向客户租户展示此软件的信息系统将使用这个可交付成果在某个时候更新其服务。在保持非常低的耦合的同时建立这种链接的最好方法之一是创建一个容器注册库,该库将填充来自第一个系统(当然带有正确的标签)的镜像,并由第二个信息系统通过拉取它需要的镜像来消费,以在租户中公开。
唯一剩下的问题是注册表应该放在哪里,如果需要一个非常稳定的注册表,答案是在每一侧都放置一个:一个注册表在软件编辑公司的一侧集中管理所有持续集成的生产,另一个注册表作为图像缓存在另一侧。这使得集成公司作为持续部署的一部分更容易继续创建租户,即使第一个注册表不再可访问。这种清晰的分离甚至可以用来实现一些高级规则,例如“只有带有 STABLE 标签的容器图像应该投入生产”,通过仅在第二个 Docker 注册表中缓存这些图像。
有些人可能会争论说,由于在这种情况下两个系统之间存在调用,这可能意味着它们是一个单一的系统,应该这样表示。再次强调,地图的目的不是反映世界的全部现实,而是帮助你履行信息管理职责。如果你希望实现的是良好的关注点分离(出于安全考虑,应该是这样),那么你的地图应该反映你的目标,因为它将帮助你完成实现这一目标所需的一切。
最后,有人可能会反对这种观点,声称,如今,地球上每个信息系统都存在某种形式的连接,无论是通过互联网网络,它几乎覆盖了所有本地系统。此外,当公司收购其他公司时,它们会连接它们的信息系统,有时连接得如此紧密,以至于最终成为一个单一的系统。同样,这完全取决于你的策略,因此 CIGREF 地图应该简单地与愿景保持一致。
对齐的模式和反模式
几年的信息系统咨询使我观察到,大多数问题都与系统中的几个不匹配有关,而这些不匹配本身只属于几个模式。在有限业务领域工作了一段时间后,当我开始与农业合作社、化学风险评估公司、律师协会以及其他不同领域的公司合作时,我惊讶地发现,这些模式(或者更确切地说,反模式,因为它们会引起问题)无处不在。
法国 CNRS 的研究员 Dalila Tamzalit 与我合作,对这些反模式进行分类,并记录了一种寻找它们并利用信息以更好地对齐受其影响的信息系统的方法。这导致了 2021 年国际信息系统开发会议(可在aisel.aisnet.org/isd2014/proceedings2021/managingdevops/3/)上发表的一篇文章。你将在下一节中找到一些有助于管理业务对齐的信息摘要。
对齐的悲哀现实
首先,应该知道,大多数信息系统,如第一章中解释的那样,都存在一些基本问题,这些问题限制了它们的效率。从四层图的角度来看,这些问题可以总结如下:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_19.jpg
图 3.19 – 常见问题
由于很少有任何 BCM,因此过程可能被很好地理解,但相应的实现通常是通过点对点的临时互操作性完成的,这很快就会使系统看起来像“意大利面盘”,数据流以一种不受控制的方式发生。
我们可以追求的目标
这一点已经提到,听起来可能合乎逻辑,但我们的目标并不是一个完全对齐的系统。为两个业务流程设计的一个良好系统可能就像以下这样简单,其中超过十个调整良好的数据流实现了全部的业务需求,使用相同数量的应用程序(在这个例子中,大多数已经存在,并且只是正确地连接),几乎不需要额外的硬件:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_20.jpg
图 3.20 – 良好的对齐(出于保密原因,文本已模糊)
对齐中的主要反模式
现在目标已经明确,让我们回到我们的对齐反模式,并介绍其中主要的四个(我们只解释它们是什么,以及如何对抗和减少将在本书的其余部分进行讨论):
-
纯粹的技术集成发生在过程没有在第一层设计,而是直接在软件中实现的情况下。结果是,公司战略、基于领域的规则或甚至过程的简单优化都会导致软件的改变。这是听到“我们进化不够快,因为我们被 IT 拖累了”或“由于软件限制,无法实现这个业务功能”(及其变体“在整个软件链中添加这个新的数据属性,从界面到报告,将需要六个月,并需要四个应用程序的新版本”)的根本原因。
这个反模式的象征性表示如下:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_21.jpg
图 3.21 – 反模式编号 1:纯粹的技术集成
-
应用孤岛出现在组织的两个部分在未相互沟通的情况下处理他们的 IT 需求时。结果系统在图中显示了这种结果,即两个独立的系统。可能有一些情况下,完全隔离被认为很重要(人力资源、财务、其他高度机密区域),但根据经验,总会有建立不同区域之间联系的时候。在这些情况下,这可能会成为残酷的现实,因为数据已经被完全复制,使用不同的格式,或者使用没有选择以简化互操作性的技术等。这种情况的主要风险是数据源简单地被向其他区域开放,这将导致重大的授权问题。在我所见过的最糟糕的案例中,一名实习生为了实施差旅费报销而将完整的 HR 数据提供给 ERP,这当然是一项重大的保密性违规,直到纠正之前,公司都面临着潜在的 GDPR 问题。
这种反模式的符号表示如下:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_22.jpg
图 3.22 – 反模式编号 2:孤岛
-
单体是集中了大量功能的程序。这本身可能不是问题,因为一个特定的应用程序可能实现了业务领域所使用的所有内容。问题在于这些应用程序还实现了应该共享或已经在系统其他部分存在的功能。数据冗余是信息系统中的一个巨大问题,因为它们从未从一边对应到另一边,这使得很难知道哪个来源最接近真相,从而导致不良决策或错误的计算。
这种反模式的符号表示如下:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_23.jpg
图 3.23 – 反模式编号 3:单体
-
功能多重实现是一个问题,因为不同的实现几乎不可能以兼容的方式工作。可以很容易地理解,如果一个财务预算总结在一个应用程序中以某种方式计算,而在第二个应用程序中以不同的方式计算,该应用程序应该做同样的事情,那么要采取明智的行动来管理公司是困难的。我在一家报纸公司见证的一个案例表明,根据查询的应用程序不同,读者数量会有所不同,差异如此之大,以至于在某些情况下,报纸公司没有额外的计算就无法知道他们是增加了还是减少了读者。
这种反模式的符号表示如下:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_24.jpg
图 3.24 – 反模式编号 4:功能多重实现
一些其他反模式及其提出的分类
只展示了四个最重要的反模式,整个业务/IT 对齐反模式(BITA,其建议的简称)如下:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_25.jpg
图 3.25 – 反模式分类
要使用这种分类来改进现有信息系统的对齐,每个反模式都附带一张结构化的身份证,包含以下信息:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_26.jpg
图 3.26 – 反模式的身份证
反模式出现以及——更重要的是——纠正它们的标准操作的完整解释也可以在之前引用的完整文章中找到。
你可以在 GitHub 上查看完整文章,它将展示在第一个 BITA(业务/IT 对齐反模式,简称 BITA)上可用的此类信息的数量:github.com/PacktPublishing/Enterprise-Architecture-with-.NET/blob/main/Business-IT%20Alignment%20Anti-Patterns%20A%20Thought%20from%20an%20Empirical.pdf
这个来自经验的分类使我们提出了一个结构化的方法来改进信息对齐,这可以用以下 BPMN 图来总结:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_03_28.jpg
图 3.27 – 在信息系统中检测反模式的方法
我鼓励你尝试将这种方法应用到你的学习信息系统中,并反馈意见。这对学术界以及希望对其他读者也有帮助。
摘要
这相当长的章节展示了如何使用形式化的绘图技术来更好地理解信息系统,并记录它将遵循的演变。地图是控制特定地面的先决条件,而图式胜过千言万语,这使得这一步在任何信息系统架构活动中都是必须的。
由于 IT 是一个相当特定的环境,我们需要一种专门的方式来绘制信息系统的地图,这就是 CIGREF 地图的内容。它的四层布局有助于将面向业务的方面(流程和原子功能)与技术的方面(软件和硬件)分开。
这种表示信息系统的方式也有助于可视化其一致性,通过检查第三层(软件位)是否很好地调整到它所实现的第二层(业务能力图)。业务/IT 一致性是复杂信息系统质量的最重要指标和演变能力,这是一个必须追求的特征。
下一章将扩展当前章节,考虑时间的维度。我们已经在之前的章节中简要提到了这一点,解释说 CIGREF 地图可以用来记录信息系统当前的状态,也可以记录其应逐步达到的期望未来状态(大爆炸方法永远不是一种实用的替代方案)。但是,正如您将看到的,时间出现在信息系统的许多其他方面,并且可能是一个相当难以处理的参数。
第四章:处理时间和技术债务
信息系统就像一个活生生的有机体:它总是移动和变化。然而,大多数系统都是“一次性”设计的,没有考虑到它们在时间上的适应性,而只是考虑了它们在设计时的处理业务需求的能力。许多 IT 问题都可以与时间相关联。
在上一章中,我们讨论了业务对齐以及基于业务关注点构建结构的重要性。这也必须应用于将时间作为良好信息系统的方程参数:如果业务功能是一次性/可丢弃的,其技术实现将不会比原型更复杂,快速编码并在使用后很快被丢弃。另一方面,对于将在生产中使用数十年的功能,你必须仔细打磨设计并完善实现,尽可能减少移动部件,因为一个好的架构师知道维护这样一个模块最终将比其初始开发成本高得多(例如,参见natemcmaster.com/blog/2023/06/18/less-code/关于这个问题的讨论)。代码的质量及其易于维护(因此是开发者害怕的活动——文档)将比快速交付功能以适应市场时间的重要性更大。
本章将分析信息系统时间适应性这个问题,因为大多数系统都是基于固定时间目标设计的,很少考虑到时间的演变。这就是为什么它们在时间上性能迅速下降,也是为什么当它们的构建时间过长时,结果甚至不符合表达的需求,因此出现了敏捷软件开发。将解释技术债务的概念,以及耦合的概念。希望到本章结束时,你将提高对需要概念验证(PoC)方法以及需要强大、可演进的、设计的批判性思维。
在本章中,我们将涵盖以下主题:
-
功能变化对系统时间的影响
-
敏捷方法旨在如何解决这个问题
-
技术债务的概念
-
信息系统的经验证蓝图方法
功能变化对系统时间的影响
在设计系统时,最难做到的一件事就是考虑时间。毕竟,要想象一个复杂事物在某一时刻的样子已经很困难了。考虑到时间需要额外的思考深度,这可能会使问题更加复杂。此外,随着时间的推移,系统的各个方面都会发生变化,但它也应该在功能运行时考虑到时间,就像另一个变量一样。
一些关于不当比较的有趣之处
下面是一些你可能在日常生活中听到的关于计算机行业以外的行业的句子:
-
维修工这周末更换了我的汽车引擎;现在它又可以再使用 10 年了
-
我用普通药片代替了商业品牌:它们更便宜,我没有注意到任何区别
-
由于我们开始定期维护锅炉,我们在整个冬天都没有出现任何故障
-
零件的尺寸略有变化,但我们只需在数控机床上更改参数;对于这样的小改动,不需要加工专家
现在,让我们尝试将其转移到 IT 行业,看看我们是否能在至少不露出一个苦笑或微笑的表情的情况下听到相同的表达:
-
我们在本周末更换了 ERP 系统;周一早上一切似乎运行得相当顺利
-
我使用了一些免费软件作为商业套件的替代品:更便宜,而且由于它与 100%兼容,一切工作都和以前一样
-
由于我们定期维护我们的信息系统,我们从未遇到过任何重大故障或错误
-
由于监管系统,业务人员需要调整系统,但由于这只是业务规则,他们不需要 IT 团队进行这样的小改动
这些句子听起来现实吗?如果你在信息系统方面有一点经验,你会知道它们并不现实,甚至听起来很幽默。没有什么比这些乌托邦式的句子更远离现实的了。相应的句子应该更像是以下这样:
-
管理层已经决定更换核心 ERP 系统;我们预计信息系统将有一个至少 6 个月的稳定期,并且最初的包括分析、部署和培训的项目肯定至少需要一年时间。
-
我转向开源软件以消除许可费用,但由于我必须调整大部分流程,我失去了一些功能,而且在这个技术领域专家很难找到,我不确定最终的总拥有成本是否会降低。
-
由于新的网络安全合规性规则,我们让整个 IT 团队推送更新到信息系统中的所有软件应用;我们希望大多数服务器都得到了覆盖,但我们知道员工的工作站仍然存在高水平的风险。
-
“新的 GDPR 将迫使我们必须发布一个全新的软件版本,并调整信息系统中的大部分数据流;IT 部门在接下来的 6 个月里肯定将把大部分非维护时间花在这上面。
这些版本看起来更加真实,但听起来像是对形势的绝望评估。这样的信息系统的灾难性容量是从哪里来的?正如在第一章中解释的那样,信息系统尚未实现工业化。但如果你更仔细地注意这些句子,你会意识到它们都包含时间演化的概念。这正是它们听起来很愚蠢的原因。如果时间从等式中去除,它们可能看起来还不错:
-
我们目前使用的 ERP 系统运行正常
-
我在使用免费软件,它按预期工作
-
我正在使用最新版本;目前一切似乎都很正常
-
我们已经设置了软件的初始规则(并且我们希望未来不需要更改它们)
简而言之,IT 可以工作并提供优质的服务,但大多数时候,这是时间流逝和 IT 必须进化以解决问题的时候。
软件世界的后果
上述比较可能看起来像是轶事,但它们反映了一些现实,因为变化是生活中唯一不变的因素,因此在信息系统也是如此。我听说的一个有趣的故事说,一个完全稳定的信息系统是可能的,但需要三个组件:一个人,一台计算机和一条狗。计算机做工作,人喂狗,狗保护计算机免受人的触碰。
再次强调,尽管这个笑话带有幽默的成分,但其中确实有一些真理:完美的系统之所以被认为是完美的,是因为它是稳定的(狗阻止人造成变化,从而造成混乱)。计算机可以做得完美,因为它不需要改变它被编程去做的事情。抛开幽默,时间和进化对信息系统的影响可以正式描述,并且与它们的不同类别相关联,所有这些我们都会在这里描述:
-
与信息技术中时间相关联的第一个概念——并且这个概念对于任何使用计算机的人来说都是众所周知的——就是软件升级的概念。随着时间的推移,无论在设计和发展过程中投入了多少质量努力,一款软件都必须经历定期的版本更新和至少安全补丁,以保持完全运行。一个软件应用本身就是一个复杂的系统,有时有成百万行代码。如果我们继续用机械工业来做比较(对于再次回到这个话题,我感到抱歉,这肯定来源于我在机械系统方面的学术背景),这意味着一个标准的行业级应用在复杂性上更接近商用飞机,而不是标准汽车。难怪它在其生命周期内需要升级和调整,就像客机需要重型维护一样。困难之处在于,大多数应用的设计方式并不像我们预期的那样模块化,意外的依赖性经常发生,使得应用作为一个统一的实体运行。如果你拿一款“拉法尔”战斗机来说,由于整个飞机都是按照这个限制来设计的,两个机械师可以在几小时内更换发动机。那么你的 ERP 软件呢?你能在几小时内切换授权引擎吗?当然不可能…这就是为什么大多数软件应用都有有限的预期寿命:经过多次版本升级后,整体质量总是下降,随着时间的推移,应用对业务的适应性也越来越差。当然,有些应用可以持续超过 10 年,有时甚至 20 年或更久。但如果你问用户,原因是不是因为软件完美无缺,他们是否喜欢它,你总是会得到同样的答案:这块软件之所以还在这里,仅仅是因为尝试移除它太过危险了!
-
时间对信息系统的影响的第二种类型并非来自软件部分,而是由于商业本身。正如本章开头所述,信息系统是活跃的实体,并且由于商业本身的演变而持续发展。新的战略、法规变化、大型公司重组、与被收购公司的融合、出售业务单元等等——有如此多的因素可以影响信息系统的使用,以至于它们几乎不可能长时间保持稳定,即使在非常稳定的商业领域也是如此。此外,在法律相关法规之上,许多商业规则是特定于公司的,这使得难以开发出真正能够达到“适合所有情况”状态的应用程序。即使有最好的意图保持事情简单,公司往往最终会调整他们购买的软件应用,或者通过专用连接器或定制代码将它们集成,以符合他们的业务方式,这仅仅是因为这样做成本更低(至少最初是这样),而不是重新组织相应的功能。但这是一种陷阱,也是时间再次介入游戏的地方:随着时间的推移,这种特定性将变得越来越昂贵。首先,每个新的主要版本的应用程序可能会使其失败,并且需要花费金钱来保持特定代码与新版本兼容。大多数情况下,这并没有完全预算,这意味着随着时间的推移,整体成本会不断增长,有时最终会花费比最初调整流程到软件上更多的钱。其中也有一部分心理学因素:功能专家会因为外部编辑的一些代码认为另一种方式更好而感到不高兴去调整他们的工作方式。他们对自己的工作了解多少呢?
-
信息系统中与时间相关的第三个联系既不在软件也不在功能上,而是在于软件如何适应业务功能。这可以通过集成、定制、调整应用程序参数、调整应用程序与其他软件部分交互的方式以及更多方式来实现。这里的联系稍微微妙一些,但所有这些方式基本上仍然是专家的工作。由于专家很少见,所以在软件项目的这个步骤中花费的时间往往比预期的要多。更改参数很快,但在一个复杂系统中分析所有可能的影响需要对其有很好的理解(我们在第三章中讨论了信息系统的地图需求)并且可能需要花费大量时间。这就是为什么 ERP 项目——一个众所周知的例子——在一家公司中花费了如此多的时间(尽管销售人员可能会告诉你关于它的所有事情,但在实践中,至少无法将其时间缩短到 6 个月以下)。这个后果的另一个结果是供应商锁定:随着越来越多的参数从默认值更改,随着越来越多的连接器或集成被添加到系统中,更改软件以适应其他供应商变得越来越困难。在一段时间后,应用程序已经深深嵌入到系统数据流中,定制新的应用程序将需要巨大的努力(尤其是由于文档不是这些项目的最佳资产),因此一些 IT 能力的发展停滞了。
所有这些后果都意味着使用信息系统的公司在某种程度上失去了其业务流程,因为 IT 以如此多的方式阻碍了发展,并可能阻止快速演变。当然,IT 有助于自动化流程,一旦实施,可以提供有趣的收益。但使其工作并保持其按时间工作所需的努力可能并不那么有趣(记住 Gartner 的统计数据,显示 70%的 IT 预算仅用于维护!)
最后,技术债务也是一个与时间流逝紧密相关的概念。实际上,它非常类似于熵,并且倾向于随着时间的推移而不断增长。但这个概念非常重要,所以我们将在本章稍后的单独部分对其进行分析。现在,我们将探讨敏捷实践如何帮助我们处理时间问题。
敏捷方法旨在解决时间问题的方法
敏捷与时间管理有很大关系,因此它可能有助于我们处理信息系统周围的时间问题。为了解释这一点,我们将回到敏捷是什么,然后观察它以解决我们需要驯服的时间复杂性的不同方式。
解释敏捷的隐喻
敏捷是关于考虑时间因素的。在 V 周期开发过程中,一切都被规划好,随着时间的推移,事情应该只会在流程中向前推进。敏捷方法认识到时间本身就是项目的一个因素,它无处不在:
-
由于质量不容妥协,增加资源并不能让软件项目更快完成(“五个厨师不可能在 10 分钟内而不是 50 分钟内烤好一个蛋糕”),因此,调整风险的唯一方法就是增加时间或减少功能范围(如果客户仍然希望最初请求的完整范围在项目结束时实现,这又回到了增加时间的问题)。
-
时间是组织敏捷项目时的一个主要决策:如果你使用敏捷工作,冲刺应该有多长?如果你使用看板方法,应该使用什么节奏?我们应该以多高的频率组织稳定冲刺?持续集成应该有多快才能有效?团队使用的时间节奏有多可持续?
-
填充冲刺是对可用时间的谈判,以及如何估算待办事项所需的时间,并将其加起来以填充冲刺。
我找到的最好的比喻之一,用来向我的客户或学生解释敏捷软件开发,也谈到了很多关于时间的内容。这个想法是将两种射箭的方式进行比较:通常的方式是瞄准目标,仔细考虑风力和目标距离,当一切准备就绪时,射箭并希望不会突然刮起一阵风,我们估计的角度是正确的,等等。猜猜看?如果目标足够远,在这些条件下命中靶心几乎就是一个运气的问题。这就是 V 周期的内容:在时间上仔细规划项目开发,尽可能考虑初始条件,最终启动项目,希望一切不会偏离目标……遗憾的是,总会有外部条件的变化,客户改变主意,团队生病,重要的依赖项没有按时发布,等等。
以敏捷的方式射击每一枪都能命中靶心,或者至少有相当高的概率:你必须握住手中的箭,逆风走向靶心,如果靶心移动,就纠正你的路径,最终在你足够接近时将箭射入靶心。当然,手里拿着箭走向靶心比箭射出后的飞行时间要长得多。但你确定这会比在风中射出多支箭,最终有一支射中靶心,哪怕不是正中心吗?区别在于项目的条件。如果一切都很稳定,没有外部依赖,并且你处于完全受控的环境中,那么提前规划一切可能比逐步调整要快一些。然而,绝大多数软件项目并不属于这种乌托邦式的情境。大多数项目都是在极其变化的环境中开发的,到处都是危险。
回到涌现架构的概念
在第三章中,我迅速提到了涌现代码架构,并说我将回过头来讨论这个话题。现在正是时候。既然我们讨论了敏捷开发,而且我们正处于关于时间的讨论中,让我们看看与涌现架构密切相关的东西。这个概念是关于在没有提前瞄准架构、没有模式和计划的情况下实现良好的架构,通过在软件项目的发展过程中细化架构,并在迭代开发的每一步中重构代码结构。没有提前瞄准……这让你想起了什么?这是我们之前用来解释敏捷方法达到软件项目目标的隐喻。再次强调,时间是允许我们在架构(其意义为提前结构化)和在工作之前无法了解复杂业务领域的不可能性之间达成协议的概念。这种对立及其解决方法如此重要,以至于需要一个专门的章节。
架构与敏捷方法之间的明显对立
十年前,当我开始理解敏捷软件的原则并将它们应用到我所带领的技术团队中时,我很难理解为什么一个典型的 Scrum 团队会包括开发者、测试员、产品负责人和 Scrum 大师。为什么没有架构师呢?因为那时我的名片上写着这个头衔,所以我对此感到个人受到了打击。这有点令人不安,因为在同一时间,我意识到敏捷与那时我们使用的老方法相比具有巨大的价值。
在与许多将这一概念带到法国的敏捷领导者讨论之后,我最终在 2013 年就如何将架构与敏捷方法结合起来进行了专题讲座(法语版本:www.infoq.com/fr/presentations/concilier-architecture-et-agilite/)。在揭示了众多矛盾以及“象牙塔”建筑师在短迭代中会遇到的困难之后,我最终解释了一种可能的折衷方法,即如何协调“提前看到”和“在短迭代中行动并调整愿景”的效用。像大多数模式一样,这些模式不是被发明的,而是由许多人独立发现的,这个新兴架构的概念仅仅是任何试图消除之前所述矛盾的任何工作的结果。
再次强调,时间在这里是伟大的方程式解算器:如果你将架构的时间范围设定为只有几个迭代,那么架构和短迭代并不对立。这样,目标移动很大的可能性会大大降低,架构仍然是有用的,因为它有助于结构化这些少数迭代的开发。
这解决了建筑师面临的难题,因为他们的工作仍然是必要的,即使考虑到工作是要提前思考长远,也会有很大的变化。但话又说回来,即使在敏捷方法出现之前,那些在象牙塔中的建筑师(在没有掌握现实的情况下想象很长时间,并向团队提供计划……他们不会遵循的计划)在很大程度上被视为没有意义。
它还帮助我们理解新兴架构的概念,该概念指出,如果在每个冲刺结束时正确地进行重构,代码的最终结构将完全适合功能需求……就像完美的建筑愿景(在我们的比喻中,箭头在目标中心的远射)在纯理论中会做到的那样(但除了非常小的项目外,在现实中几乎不可能做到)。
除了时间之外,语义学也有助于消除之前暴露的矛盾。架构这个词被用于两种不同的方式:
-
建筑作为项目新兴的全球形态,是关于团队产生的代码结构
-
建筑作为在应用程序中(甚至更高,在整个信息系统)构想结构的行为,是关于通过最初思考和行动在系统上尝试达到这种结构化状态
这意味着这种理论上的矛盾可以被克服。但这并不意味着没有实际影响,我会向你展示一个例子,因为它将帮助我们回到将技术方面与业务方面对齐的观念。但在那之前,我将添加一个外部分析。
著名建筑师的地位
就像任何科学学科一样,我们软件架构师通过“站在巨人的肩膀上”节省了很多时间,在我们的情况下,这涉及到反思该主题真正专家建立的艺术水平。马丁·福勒无疑是软件架构领域最好的参考资料之一。关于“黑客、编码和修复”与“前期大设计”之间的对立问题,我强烈推荐阅读马丁·福勒在www.martinfowler.com/articles/designDead.html上发表的优秀文章。标题*设计已死?*虽然挑衅性强,但隐藏的真正背景主题正是我们在这里讨论的。
马丁·福勒对架构的反对意见的回应仅仅是只应用设计来增加系统演变的能力。像往常一样,在两个极端之间没有“对或错”的答案,即极限编程(它明确承认其极端性)和前期大设计(它通常不承认甚至不承认其极端性,并迅速产生“象牙塔架构师”)。
这就是架构师的工作变成一门艺术的地方,因为他们需要运用良好的技能,在不强加不可移动的限制的同时,通过一些前期设计获得微妙的平衡,但仍提供真正有助于开发者长期快速生产功能的健康指导(而不是像马丁·福勒文章中描述的那样,被软件熵所阻碍)。
由于,在软件开发中,变化是唯一的不变因素,因此提前编写因功能变化而可能过时的内容是没有用的,但这并不意味着架构师的工作被取消了:相反,这是关于简化未来的演变。架构不是关于 UML 或代码框架,而是关于系统应该如何构建的指导方针:它的固定点是什么,它围绕哪些关节转动;我们应该在哪里关注质量,在哪里可以承担可丢弃的代码,因为业务发展如此迅速,投资于稳定性是没有意义的?有时,大量的架构努力正是为了适应一个需要频繁变更的重要模块。例如,使用业务规则管理系统(我们将在第五章中更详细地讨论这一点)。
同样的情况也适用于编码模式:仅仅因为代码能够正确运行,并且持续不断地重构你的代码,自然会形成代码中的模式,即使你事先并不知道这些模式(我告诉你了,工艺并没有消亡!)。如果你还没有在编码活动中亲身体验过这一点,那么一个很好的证据是,当你阅读大量的代码(尽管很少有人喜欢这样做,尽管大多数伟大的作家在成为作家之前都是饥渴的读者)或者当你跟随一群学生时,你会意识到这些模式经常被重新发现。这正是模式的定义,因为它们是普遍的,无论它们是如何被发现的,只要你正确地组织你的代码,上下文就会让你最终使用正确的模式来解决这个具体问题。
提前思考函数合约
在之前,我谈到了一个实际例子来阐述我们之前讨论过的长期架构和新兴架构之间的对立,让专家(马丁·福勒)发言。我提到的例子来自我的个人专业经验,是为实现功能流程的几个应用程序之间的复杂互操作场景而创建的图表。在项目开始时分析业务需求(或者更确切地说,是它们的初始表达,因为它们随着项目的发展而变化),我创建了以下流图:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_04_1.jpg
图 4.1 – 数据流示例
根据第三章,你应该通常能够认识到函数和软件实现之间的双重性,特别是关于依赖关系应该指向第 2 层而不是直接指向第 3 层的基本建议,这将导致点对点互操作,从而创建软件解决方案的“硬耦合”(如果你不知道这个表达式的意思,我们很快就会回到这个话题)。
即使使用机器符号,底层实际上还是关于软件服务器,因此是第 3 层。中间区域也是软件层的一部分,因为它包含将 API 调用转换为专有调用的连接器,如果需要的话。顶层的编排层显示了由 API 合约表达的功能任务是如何组合起来创建细粒度过程的。它基本上可以被认为是 CIGREF 图中的第 2 层,带有一些第 1 层的触感。
我展示这个架构的原因有两个。首先,它说明了当架构走得太远时会发生什么,以及新兴架构如何有助于节省时间:我是在我还是一个年轻的建筑师时画的,当时并不完全了解新兴架构的概念,所以它走得太远了。当然,有一个项目愿景是有帮助的,但最终,几乎所有的连接器和数据流都没有按照我预想的方式工作,时间在这里被浪费了。
其次,这个架构提供了关于 CIGREF 地图第 2 层和第 3 层之间交互的更多细节,表明它们是关于 API 合同(我们不是在谈论带有代码的 API 实现,而是在谈论合同,即用精确的技术术语表达的功能能力的列表)。这尤其有趣,因为尽管技术实现并不是(完全)预期的,而且自从原始蓝图以来,流编排已经改变了很多次,但结果却是 API 合同在 很多年里都没有变化。
我无法告诉你,在项目几年后意识到这一点给我带来了多大的满足感。当思考代码时,我的最初愿景是失败的,其中只有一小部分得到了实施。当思考编排时,由于流程和业务规则的变化,API 的粘合方式已经改变。但与业务知识人士设计的 API 合同在几年后仍然存在,基本上没有变化,并且它们与业务的极端一致性使得所有这些变化在代码或定制中产生了非常有限的影响。
简而言之,代码架构应该限制在几次迭代之内。但业务一致性架构是一项值得在项目初期就进行的投资,因为它的价值不会衰减。
这就是为什么先合同后 API 设计(再次强调,我说的不是 API 实现,而是仅仅定义合同)如此重要的原因:可以通过纯粹的功能知识建立合同,将每个技术方面都排除在外,从而确保定义业务模块及其依赖关系的基础非常稳定,这将作为软件实现后续的强大基础。
我们将在第八章和第九章回到“先合同后思考”这一概念,但在此,请记住时间范围对架构在两个意义上都有影响。对于技术架构,时间范围应该是有限的,以便“箭头能够达到目标。”但对于功能架构,时间范围没有限制,因为在这种情况下,我们所做的是了解目标在哪里。这是即使仅仅想到要击中它也是必须满足的前提!
技术债务的概念
技术债务可能是过去十年与 IT 管理最相关的讨论概念之一。作为本书的基本目标之一,软件质量是我们必须清楚地描述的内容。
技术债务的一般定义
如果你正在阅读这本书,你肯定对软件质量和如何正确构建事物感兴趣,因此你肯定对技术债务的概念有很好的理解,或者至少已经接触过它。尽管如此,我仍会给出一个快速的定义,以便你可以尝试将其形式化。
技术债务是什么?
技术债务是你允许进入项目的意外复杂性及其内在复杂性的增加。
让我们稍微分解一下。当你开发一个软件项目时,它总是旨在在给定的业务领域范围内产生一个功能。有一个内在的、确定的、稳定的复杂性,来自于你试图解决的领域:在信封上打印地址比优化国际机场的航班要简单得多。
或者不是吗?作为一个旁注,请务必始终确切地知道我们在谈论什么,当业务需求被表达出来时,如果你不确定,不要犹豫,提出关于可以做什么和将要做什么的保留意见。用一句话简单地解释功能需求并不一定意味着目标本身很简单,而且可能隐藏着很多内容。在这个例子中,你应该立即产生一种反射,询问地址应该如何打印,如果支持不同的信封格式,国际地址是什么,如果有一些规范和标准需要遵守,数据将如何提供,等等…
这种第一种复杂性通常被称为内在复杂性,因为它伴随着你想要解决的功能业务领域,而且无法逃避。除非你做得少于客户期望,否则你无法减少这种复杂性。这并不意味着你应该立即接受所有这些复杂性:记住敏捷方法将项目切割成小块,一次处理一块(“你是怎么吃大象的?一口一口吃”)。如果你的客户希望你处理完整的业务领域功能和复杂性,你只需添加所需数量的冲刺以达到所需的内在复杂性水平。这只会花费更长的时间,因此成本更高。
现在,让我们来谈谈第二种复杂性:意外复杂性。为此,让我们以第一个表达的功能需求为例,即打印信封上的地址。为了使内容简短,假设我们只需要在标准 A5 格式信封上打印四行标准地址,地址数据以我们想要的任何格式提供,并且硬件部分(用于信封的特殊打印机)由其他人负责。作为开发者,我们如何实现这个请求的功能?
想到的最简单的方法之一是使用 Office Word 应用程序的融合功能,从集成助手消耗 XML 数据,并保存文件供客户未来使用。
但实现一个函数的方法(方式!)不止一种,你可能会发现自己正在使用一个从头开始创建的 Java 应用程序来读取任何格式的地址、创建 PDF 文档并将其发送到打印机。这并不复杂,但已经有比第一个技术解决方案更多的移动部件…而且它们的维护是你的责任,而不是办公室编辑的责任!你将不得不处理 Java 运行时的版本兼容性问题。PDF 生成也可能有点棘手。也许开发者会在代码中留下一些 TODO,表明一些边缘情况需要在将来解决。最终,尽管不是极其复杂,但这个解决方案在技术复杂度上比我们最初提出的方案要复杂,尽管它在功能上达到了相同的目标。
我们所说的 delta 就是所谓的偶然复杂性,因为这是可以避免的复杂性——与功能性复杂性相反。有时人们会将它与技术复杂性混淆,但这并不是正确的说法。实现一个函数总是需要某种技术复杂性:具体的执行不能从无中来,并且必须有一些软件来执行函数。偶然复杂性,正如其名称所暗示的,是建立在实现功能性需求所需的最小必要努力之上的技术复杂度。因此,它被视为一个意外,因为事情本可以不这样做,而且它之所以存在,是因为外部的不愿原因。
技术债务的原因及其与时间的关系
这些不想要的原因是什么?嗯,它们如此之多,以至于很难一一列举:懒惰、没有足够的时间达到适当的质量、缺乏培训、我们都有的倾向,即使用一个众所周知的技术而不是一个更适合但需要首先学习的技术(“当你只有锤子时,每个问题看起来都像钉子”)、缺乏技术监控,结果是我们根本不知道还有更好的做事方式,还有更多。
在这些原因之上,还有一个更深层次的原因,即大多数技术专家实际上在内心深处热爱复杂性。我经常将开发者比作气体(没有恶意:我自己也是,几十年来都是,而且仍然会陷入这个陷阱):他们总是会占据你给他们的所有空间。
这与热力学有另一个相似之处,因为我正在将熵作为 技术债务 的隐喻。
让我给你一个(并非完全不切实际)的例子。组建一个由软件专家组成的百万美元团队,要求他们创建一个计算两个整数之和的函数,你几乎可以肯定,没有人会提议简单地使用Int32.Add。他们会在假设你知道自己在做什么的情况下工作:既然你组建了如此庞大的团队和预算,你肯定有更高的目标,即创建一个高性能的函数,以几乎无限制的大小添加整数,在所有条件下都能产生可预测的结果。
这是因为开发者是工程师,很少是商人。如果他们是,团队中第一个联系的人会告诉你,你不需要其他雇佣人员,他们会自己承担整个工作,只需要五十万美元。接下来,他们会组装一个复杂的机器,它只是调用Int32.Add,让你等待几个月来隐藏它的极端简单性,然后之后将最终产品交付给你。
一个关键发现是始终给你的开发团队提供一些边界约束;否则,他们可能会添加意外的复杂性,有时甚至数量很大…而且总是很痛苦地知道你最好的客户业务流程被一个添加的“以防万一我们将来需要它”的过度热情的开发者引入的函数中的错误所阻塞。
技术债务的第一个原因对开发者来说非常关键,但等等——我们还得谈谈功能人员!
解释精确需求时的懒惰?与开发者的沟通不足?过度依赖他们来理解技术复杂性(之前,我谈到了一句业务需求的危险:它往往隐藏了请求者本身对需求的理解不足)?无法测试结果并调整功能需求?这个清单可以继续下去,还可以挑剔我们心爱的产品负责人。并不是因为他们自己不是技术人员,功能人员就不能造成意外的复杂性!
所有这些关于应该做什么的功能定义的变化,对技术实现产生了巨大的影响:我们改变代码的方式,就像更换汽车上的轮胎一样!函数之间存在联系,代码的整体复杂性在几行之后就会超出人类大脑的处理能力。因此,如果指示总是变化,结果无疑将是质量低劣的代码,为了节省时间而牺牲质量,充满了“临时”的解决方案(谁在骗谁?我们都知道它们会一直存在,直到应用程序的生命周期结束),为假设的同事留下TODO指示,以便神奇地出现并重构那些愚蠢的不完整代码成为美妙优雅的新版本,等等。我甚至没有提到那些将永远膨胀应用程序的无效代码,仅仅因为复杂性——以及缺乏文档——使得删除它并产生副作用的风险如此之大…
技术债务与时间的关系
为什么,为什么,为什么今天的软件行业中会有这么多垃圾代码?嗯…又是时间。技术债务是另一个与时间密切相关概念。尝试对上述症状进行根本原因分析,经过几个连续的“为什么”,你几乎总是会得到同一个答案:“时间不足。”缺乏初级培训?我们没有时间。缺乏产品所有者的可用性?他们没有时间。缺乏文档?我们没有时间…
时间以另一种方式出现在技术债务中:如前所述,技术债务,就像熵一样,总是增长的。而且,就像熵一样,可能会有一些特殊的地方,局部上的无序正在减少,但这总是通过消耗能量并在其他地方增加无序来实现的,这使得整个系统的熵增长。
技术债务是为什么在超过十年后仍然可以正常运行的应用软件如此之少的原因。旁观者可能会认为这是因为软件是一个快速变化的领域,但当你仔细想想,变化并不那么快。Java 是 90 年代的事情,.NET 在十年后出现,2010 年代见证了 JavaScript 在它本不应被用于的地方的使用,2020 年代标志着尝试一些新语言,但没有一种语言现在标志着它的时代——变化的速度并不那么快…那么,我们为什么改变软件这么快呢?简单地说,是因为它们充满了技术债务,我们不能再以高昂的成本来维护它们了!
这又是一个技术债务暴露时间的例子:随着时间的推移和技术债务的增长,它对项目造成的开发速度减慢的时间成本也在增加。这就是为什么我们称之为技术债务:就像金融债务一样,只要你还保留一些借来的资本,就必须支付利息。借入的资本越高(你的技术债务深度,这与你在开发过程中多次走捷径的次数有关),利息就越高(向你的应用程序添加功能所需额外的时间)。
既然我们在谈论债务水平与消耗软件开发时间的线性关系,这意味着存在一个比率,就像在金融贷款中一样。现在是一个分析这个比率更详细的好时机。
债务或高利贷?
我们中的许多人至少在一生中贷过款,用来买房子。与我们可以从中获得的优点相比,支付所获得金额的一小部分是合理的:贷款结束后拥有房子,不再支付租金,等等。根据经济环境(以及个人偏好在选择中也起着很大作用),可能有些情况下租房而不是买房更好,但从长远来看,积累一些资本总是胜出的。
然而,这只有在利率足够小的情况下才成立!如果你必须以每年 10%、20%甚至 50%的利率借钱,你会感到多么不舒服?在这种情况下,当然,没有人会借钱,因为 2 年后,贷款的成本就会和本金一样多:在这种情况下,你最好推迟 2 年购买,用现金支付。
除了有些情况下你不能这样做。当然,你可以租房子住而不是买房子。但是,当你需要钱吃饭或因为生活中发生的困难事件需要临时住所时怎么办?如果没有监管,银行可以随心所欲地提高利率,在某些情况下,你可能会被迫贷款,因为你的生活依赖于它。在这种情况下,当你情况改善时,你将不得不偿还这笔贷款,但利率如此之高,以至于它会吞噬你所有的储蓄,你最终会陷入另一个贷款的恶性循环。这是为了避免几个世纪以来,政府和甚至进行金融操作的个体行为者通过所谓的高利贷利率受到限制的情况。
如果你不了解这个金融术语,高利贷指的是以如此高的利率贷款,以至于实际上无法偿还本金。社会进步是为什么现在在大多数国家都是非法的,那里的最高利率是固定的。例如,在撰写本文时,法国的高利贷利率为 5.33%,这意味着银行不允许以高于这个价值的利率贷款。
现在,让我们回到技术债务,并评估我们借款的比率。这并不难找到,因为 Gartner 关于信息系统维护成本的研究已经被引用过两次:令人震惊的是,占 IT 预算的 70%!好吧,这并不包括技术债务的 70%利率,因为你还应该计算 IT 系统为公司带来的好处以及不这样做带来的成本。我将让你自己计算,因为这会根据你所在组织的具体情况而有所不同。但总的来说,你可能会得到一个数字,这个数字你无论如何都不会容忍从银行获得的金融贷款,而且这个数字将远远高于高利贷利率。
那么,我们为什么要容忍这种情况呢?这种情况的原因已经在前文中提到;现在是时候提出一些解决方案,以消除过度的技术债务。这就是我们在下一节将要做的。
技术债务的平衡方法
人们经常谈论与技术债务作斗争或压制它。这种说法并不准确,因为它意味着技术债务(以及因此产生的意外复杂性)应该降至零。这听起来像是一个很难实现的目标,因为完美需要花费很多钱:事实上,你的目标不应该是不再有任何技术债务,而应该是控制它,就像你不应该试图找到一个 0%的利率贷款(你永远找不到)而是找到一个合适的利率,这样可以让你的项目更具成本效益,平衡利率、贷款金额和期限等等。
因此,一点技术债务是可以接受的。如果你必须按时推出这个功能,以便在年度研讨会上向所有客户展示,谁会在意你当时没有为这次活动设置日志记录呢?真正重要的是,你已经将工单放入开发工具中,并且产品负责人同意在将功能投入生产之前,在即将到来的冲刺中完成它。如果他们违背承诺,试图推迟这个“技术”工单,并对它大吵大闹,提醒他们后果,发送一封电子邮件解释这将如何影响未来,要求他们书面同意承担责任,将其升级给大老板……无论需要做什么,都要确保这个功能回到正轨!否则,技术债务开始增长的责任将是你。
当然,最好的方法始终是简单地不让技术债务溜走。当然,说起来可能比做起来容易,但知道问题可能如何产生已经是一个很大的进步。记住,“技术债务”这个概念在 2000 年代并不为人所知或正式化;现在,甚至非技术经理在 IT 或软件开发领域工作也可能听说过它。这已经是一个很大的进步,并让你能够向他们做出有根据的论点,解释减少延迟、缺乏培训或文档和质量的时机将导致几个月后开发缓慢。再次强调,如果你是技术负责人或 CTO,控制技术债务是你的首要且最重要的职责之一。
但您可能处于一个软件应用程序的技术债务已经很高的境地——要么是因为您过去让它滑落了(真傻啊),要么是因为您负责的软件本身就已经处于糟糕的状态。首先,要清楚地表明——如果利益相关者并不完全了解——情况很糟糕:您不会想到糟糕的软件团队可以编造多少借口来掩盖他们无法交付的能力,而且您确实需要有能力改善这种情况。如果您接受了这份工作但并未迅速发出关于不稳定状况的警告,那么每个人都会认为软件没问题。而且您以后也无法对其状态发出警告,因为您是一个技术专家,逻辑上每个人都会认为您应该在此之前就看到它,尤其是如果它像您描述的那样一团糟。您甚至可能会发现一些不负责任的先前所有者发誓说,在将软件转交给您的团队之前,软件完全没问题!
在这种情况下,建立应用程序中技术债务的地图(使用 CIGREF 四层图,不要忘记技术债务甚至可能来自设计不良的过程或定义不明确的功能,以及不正确的治理)。可能有些地方一点技术债务是可以接受的。而有些地方则大部分维护时间和预算都被消耗,必须紧急处理。当评估纠正这些风险时,许多参与最初混乱的人会告诉您,影响将会非常高,试图纠正软件将不会奏效,甚至可能已经尝试过并失败了。在这种情况下,做出您最好的估计,并要求经理们做出决定并承担责任。当宣布重写相关功能将花费 10 万美元,并带来 20%的影响风险,额外增加 20 万美元时,在座的每个人肯定会皱眉…但如果您也解释说,这款软件在过去十年中每年已为公司造成 4 万美元的损失,因此已经浪费了 40 万美元,决策者会迅速进行计算并让您尝试。
这意味着您可能通过消除技术债务(在金融贷款的比喻中)来偿还部分资本,即使向经理们解释这种做法的好处通常很困难。毕竟,业务影响不是立即可见的,也没有客户抱怨软件不工作。所以,再次强调,您真的必须通过评估技术债务给您带来的时间成本,同时可以为客户的愉悦完成哪些功能,以及偿还债务并使应用程序达到良好质量水平所需的时间来构建一个强有力的论点,同时不要忘记影响分析——在“汽车行驶时更换引擎”总是存在风险。
大爆炸的诱惑
大爆炸方法怎么样?你知道的——把所有东西都扔掉,重新启动一个全新的、干净的产品。所有工程师的梦想… 如果你仔细想想,这并不是正确的做法。如果你无法阻止它发生,那么这是最好的结果。 让我来解释一下:大爆炸方法,尽管它可能很有吸引力,但永远不是正确的选择。如果你的软件中存在技术债务问题,那是因为你的开发过程是错误的。所以,如果你开始另一个应用程序,希望它比前一个更好,但又不修复这个过程,你只会浪费几年时间,最终达到相同的状态。如果你知道过程中哪里出了问题,并且已经纠正了它,应用程序将会改进,所以再扔掉它也没有用了。
这是否意味着大爆炸永远不会发生?不,当然不是。即使这不是一个好主意,人们仍然会尝试去做… 并失败。但是一张干净的纸是如此吸引人,以至于即使在其他方面营销能力较差,应用程序所有者也会不遗余力地争取利益相关者的同意和预算。他们会通过承诺提高性能、提供更好的上市时间、未来功能的易于改进以及许多其他质量来做到这一点,以至于参与者最终会 wonder 为什么之前没有提出这个建议。而且,他们还是会失败。这不是我说的话,而是从这些类型的项目中拥有经验的人都会找到的经验之谈。
在任何规则中都有一些例外,确实有一些大爆炸项目是成功的。我注意到在那些大爆炸并非团队意图而是通过旧项目因自身重量而崩溃的事实中。在我居住的法国,我们有很多这样的大型政府项目,失败如此严重,以至于从项目中没有任何东西可以挽救,新的软件公司不得不从头开始。例如,“卢沃伊”项目(管理士兵的薪水),该项目浪费了数百万欧元。回到我之前提到的这些事件中的共同责任,这个项目中技术问题很多,但功能特性描述严重不足,几乎没有缩减项目规模,这导致了这场工业灾难。
耦合的不同类型
应用程序之间的耦合是我们将要讨论的关于时间的最后一个概念… 就像技术债务是必须存在的东西(技术复杂性,以实现功能复杂性)的略微过多一样,耦合是指从功能角度来看比所需更强的依赖关系。
让我们考虑一个例子:你想要向你的客户之一发送一份带有电子签名的合同。当然,合同模块将依赖于电子签名应用程序,以及提供你所需要关于该客户信息的模块(即他们的财务或法律联系电子邮件地址)。但是,有依赖关系,还有依赖关系…
假设第一个依赖关系设计得非常好,你只需通过在信息系统调用一个 API 来发送一份合同进行签名,然后它会处理所有事情。你甚至不需要知道哪家公司将实际完成这项工作,也不需要知道这如何具有法律约束力:你只需调用 API,如果它返回 OK(技术上称为HTTP 200),你就没问题了。这种依赖关系是一种低耦合的依赖关系:实现中可能会有变化,你的公司可能更倾向于选择另一个电子签名供应商,或者根据调用 API 的人将文档以不同的方式路由进行签署:你不必关心,因为你只需使用POST命令调用类似mycompany.com/document-sign这样的东西。这就是你所知道的一切;幕后发生的一切都不关你的事。你仍然依赖于函数的完成来签署你的合同,但这种依赖性非常灵活,你可能永远不需要改变调用函数的方式;你所承受的耦合度很低。
现在,让我们来看第二个依赖关系,并想象一下另一端的情况:你需要获取客户的财务或法律联系人的电子邮件地址,为此,你必须知道客户 ID。遗憾的是,这并不是你在服务内部使用的同一标识。因此,首先,你必须调用负责客户参考的服务来了解确切的使用标识。当你有了这些信息后,你必须深入到一个通过其 IP 号共享的文件夹中,并沿着文件夹结构向下走,从消费者被记录的年份开始(看起来像是你得到的标识符的前两个数字,但你并不确定),然后是一个名为Contact的文件夹,最后是一个包含联系类型的文件夹,即FIN代表财务和JUR代表法律。在那里,你最终会找到一个 Word 文档,你必须跳过一些无用的信息,直到你最终到达第 2 页,在那里你找到了你一直在寻找的电子邮件地址。
这听起来有些牵强,但这是我咨询生涯中遇到的一个真实世界的例子(诚然,尽管如此,这是我 15 年来在客户系统工作中见过的最差的信息系统之一)。而且我们还没有完成!一些客户有多个标识符;当他们在数据库中删除并重新输入时,他们的标识符被恢复了…但他们的数据在对应续签年份的文件夹中,而不是初始创建年份的文件夹中。在某个时候,联系文件夹被重命名为CONTACTS,这破坏了信息恢复自动化的几次尝试,并且联系类型的代码也发生了变化。最后,文件夹中的 Word 文档格式发生了变化,电子邮件地址不再位于同一位置,让人们怀疑新位置是否包含正确的数据,或者是否是关于新的电子邮件信息。所有这些无用的复杂性使得对电子邮件地址的依赖变得非常紧密(这又是我所见过的最糟糕的例子)。
当然,低耦合通常更好,但就像技术债务一样,有一些耦合是可以接受的,重要的是要控制它。可能有些地方,极端紧密的耦合并不是一个令人担忧的问题。例如,紧密耦合到一个预算结构通常不是什么大问题,因为这些结构是监管强加的,并且它们的变化频率以十年计算。所以,在这种情况下,你必须彻底审查你的流程和软件应用,这并不是什么大问题。另一方面,如果你使用的是供应商的商业依赖,而该供应商在你意识到你用他们的工具做了很多业务时提高他们的许可价格(你肯定知道这样的编辑器),你将希望有低耦合。在这种情况下,向你的 CEO/CFO 展示你通过一些参数更改和一个小型的迁移过程就能切换供应商,这将使你成为他们喜爱的 CTO 合作伙伴,因为他们将带着极其有力的论据和一条容易逃生的后路回到与供应商的谈判中。
最后,耦合也与时间有关。有很多种耦合方式,但有一种耦合是按时间顺序衡量的。如果你要在模块 A 中执行任务,需要来自模块 B 的信息,那么这种依赖是同步的(你肯定会发现它通过同步调用实现,例如 HTTP GET调用)。如果你的模块 A 在调用模块 B 中的函数后可以自由继续,那么这种依赖是异步的(你肯定会看到它,例如,一个返回回调 URL 的POST API,你可以在之后调用它来查看工作是否完成——或者更好的是,你可以注册一个 webhook,一旦工作完成就会通知你;这将发送另一个你可以联系以获取外部任务结果的 URL)。在第十七章中,我们将回到这种方法,并特别解释编排和协奏之间的区别以及何时使用每种方法——就像往常一样,没有对错之分,正确的技术取决于确切的功能需求和其上下文。理想情况下,在良好的信息系统中,两种方法都应使用,每种方法在其首选解决方案的上下文中使用。
到目前为止,你应该已经清楚地理解了技术债务及其对信息系统演变的影响。随着时间的推移和技术债务的累积,IT 组件在演变和有时仅仅是功能上会因技术债务而减速。我们能做些什么呢?嗯,这正是下一节的主题。
信息系统的经验证蓝图方法
在本节的最后,我想解释一个方法,我过去几年中一直在使用它来创建信息系统演变的蓝图,并与几家工业客户一起完善了它。里面没有什么特别之处,也没有特别创新,因为这仅仅是将常识应用于达到功能目标…但它足够正式,以至于我可以想象你在描述所使用的步骤时能找到价值。
完整的方法相当复杂,需要一本专门的书籍来详细说明,所以我将专注于本章的精确主题,即处理时间和技术债务。我将使用的例子将涉及从一个充满技术债务的单体软件应用中提取依赖项,但遗憾的是,它被用作客户公司业务的基石(是的,这是一个最坏的情况)。为了在限制对日常业务的影响的同时提取这个依赖项,不得不创建一个多年的计划蓝图。以下章节将解释如何完成这项工作,重点在于方法而不是具体行动,以维护客户的机密性。
所有这一切都始于…映射!
如第三章所述,我们必须从对问题的正式映射开始,CIGREF 地图建立在对研究边界的周围。由于问题在功能和软件层,过程完全没有表示,关于硬件基础设施的第四层只是略过,因为我们只需要相关机器的全球成本。结果是以下结构,其中你可以看到软件层中左上角的大白方块(这是我们研究的单体主题):
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_04_2.jpg
图 4.2 – 地图中精确度的演变
这是一件非常重要的事情,我想再次强调:只有与学习相关的部分被绘制在地图上。还记得上一章中的这个图表吗?
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_04_3.jpg
图 4.3 – 具有减少内容的真实世界地图示例
这是因为,在 20 个左右软件应用中,它只是该公司整个信息系统的一小部分。至于业务能力地图,它更为详尽,但这仅仅是因为我们需要整个边界线用于另一个项目。正如你所见,只有少数这些功能与正在研究的软件应用相关(沿着层 2 和层 3 之间的线条)。
回到巨人的肩膀上,我将简单地回顾一下马丁·福勒关于类所提出的观点,这完全适用于函数和软件应用映射:马丁建议不要在 UML 图中绘制所有类,只绘制重要的类。然后他继续解释,图表的主要问题在于绘制它们的人试图使它们全面。图表应该帮助我们理解简洁明了的信息,而代码应该是全面信息的来源。
寻找原子操作
由于一次提取单体并为其新应用进行更改是不可能的(记住,“没有大爆炸……永远”),我们必须设计一种方法,逐步从应用中提取功能并将它们迁移,一步一步地,并尽量减少对新的、现代实现的影响。但顺序是怎样的?这就是地图将帮助的地方,它显示了模块及其依赖关系。从现在开始,我将使用任意架构来更好地解释方法,即使它们与这个项目发生的事情有所偏差。让我们想象一下,我们需要“拯救”的三个重要特性基于以下依赖关系的五个软件模块(注意,有功能实现——层 2 和层 3 之间的链接,以及技术耦合——层 3 内部的链接):
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_04_4.jpg
图 4.4 – 函数及其实现的简单示例
一旦我们完成这个,我们就可以使用提供的信息来绘制两种时间顺序的方法(在这个部分中,时间关系最为明显)。第一种方法可能是,尽可能快地将第一个功能推出(市场时间策略):
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_04_5.jpg
图 4.5 – 以市场时间优先级的场景
在这种情况下,重写具有功能(或功能)1 和 2 的引擎的责任最终被推迟,但至少功能 3 的新实现可以快速可用。这种方法的缺点是,由于它将技术债务推迟到以后,开发过程将保持缓慢,直到项目结束时功能将更难发布。
这需要另一种方法,其中首先解决技术债务。这种方法的优点是未来功能将快速流动。然而,这种替代方法的缺点是,我们将在一段时间后才能看到结果(正如之前解释的,这是你最好有一个强大的商业案例来说服利益相关者资助这项投资的地方)。这种第二种方法可以在以下图表中看到:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_04_6.jpg
图 4.6 – 以 TCO 优先级的场景
为了总结每种方法的优缺点,最好是将两个图表叠加,这提出了主要差异,以下用字母 A 和 B 标注:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_04_7.jpg
图 4.7 – 两种场景之间的图形差异
标记为A的 delta 显示了初始市场时间的差异。这在许多业务中都是一个重要的标准,因为客户需要真正相信你在前进。无论你与客户或内部用户的关系多么可靠——顺便说一句——很难让他们在没有展示你几个月来所做的工作的情况下离开,商业所有者知道这一点。在第一种场景中,功能 3 更早地进入市场,并可能参与整个项目的财务投资。在第二种场景中,第一个发布的功能不仅来得更晚,而且与之前的不同,这可能会根据用户最重视的内容产生很大差异。
标记为B的 delta 显示了在项目上花费的总时间差异:虽然场景 2 比场景 1 更慢地显示出初步结果,但它解决了项目开始时的更多技术债务,这将使剩余时间的发展更容易和更快。这是应该考虑的事情,因为开发团队的成本很高。根据项目的复杂性,这个 delta 可能变得非常重要(注意,在前面的图表中,刻度是完全任意的,并不代表任何有代表性的事物,因为它取决于你的项目)。
现在我们已经确定了两个基本场景,我们将深入探讨一些更现实的内容。
根据业务准则优先处理行动
这是一个常用的技巧,但提出两个极端的替代方案通常有助于让利益相关者选择一个中间的方法。有很大可能性,决策不会在之前解释的两个极端方法之间,而是在中间某个妥协点。但话又说回来,你如何调整光标?你可以使用哪些准则来进行这种调整?
对于任何有商业头脑的软件架构师来说,一些标准准则会立即浮现在脑海中,即总收入/营业额和盈利率。通常,我会让第三个准则是一个模糊的东西,利益相关者会根据对公司战略的重要性来评估它(他们对这一点比任何 IT 人员都了解得更多)。在项目这个阶段重要的是,这些准则应该快速评估,以“规划扑克”模式进行,并且数量应该有限。我为每个准则使用一到三颗星的水平,整个准则不超过三个。这通常足以找出最佳场景。
回到更现实的话题,这里有一个例子,是我为另一家我咨询过的公司创建的决策表(一些条目已被删除或重命名,因为它们可能会透露太多信息):
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_04_8.jpg
图 4.8 – 通过所选准则优先处理项目
有趣的是,在这种情况下,你会注意到,准则并不是之前提出的“标准”准则。这里,我们有以下内容:
-
改善的流程数量(在地图的第一层)
-
对整体生产力的预估(这是一个工业生产公司)
-
对增长预估的影响(它们在一个快速整合的市场上运营,小公司被大公司收购,因此快速增长对繁荣至关重要)
此外,还增加了一个列到传统的项目估算权重和延迟估算中,即与项目相关的冲击和风险(这是一个合理的方法,并且应该适用于任何此类项目)。为了强调这样一个项目的易于阐述比完整性和对方法的精确尊重更为重要,以下是表格在直接在白板上分析 2 小时后的初始样子。
您可以在 GitHub 上找到这张白板图片:github.com/PacktPublishing/Enterprise-Architecture-with-.NET/blob/main/Whiteboard%20image.jpeg
最后,关于时间、语义和对齐的几点话
为什么会有一个关于软件开发中时间重要性的完整章节?你可能想知道这个问题,我希望我已经说服了你,不是时间的的重要性,这在任何项目中都是不言而喻的,而是许多软件开发和信息系统设计中的问题应该通过时间管理的棱镜来观察。
经常发生的情况是,我们把它视为理所当然,因为它在我们无法采取任何行动的情况下悄然流逝,我们在设计活动中忘记了时间,没有考虑到未来的视角。有多少信息系统因为它们被设计来解决今天的问题,而没有考虑到业务将如何在未来发展而诞生不良?技术演变,当然,我们可以处理它们,有时我们做得比应该的更多,通过为下一个框架、未来的技术等做准备。但这不是问题!重要的是业务领域功能:它们也在变化,信息系统必须与业务保持一致,而不是与可能在几年后成为过时的技术演变保持一致。
当我们倾向于将概念视为稳定时,时间也常常被遗忘,仅仅是因为我们没有使用足够广泛的时间范围来解释它们。许多概念在表面上看似极其稳定的同时,实际上正在演变。在第三章中,我解释了“客户”这一概念是一个业务规则而不是一个稳定的实体:对于商业而言,客户可以是过去 12 个月内购买过东西的任何人,而对于营销而言,可以是过去 24 个月内购买过东西的任何人。也许维护部门会根据谁获得了运行保证来列出客户名单。这些规则,就像任何业务规则一样,会随着时间的推移而改变。在某个时候,也许大老板会厌倦商业和营销没有传达相同的客户数量和演变速度,并强迫他们采用一个共同的定义;也许保证期限会改变,这将影响客户维护名单。谁知道呢?有一点是肯定的:如果你没有考虑时间,你会遇到麻烦。
一个小故事——我希望你能从中发现时间的重要性——来结束这一章:我碰巧为一个组织提供建议,该组织通过与其他组织的合并,不得不更换他们的标志。由于这些组织在选举后都受到政治变化的影响,那里的人们希望使将来调整标志变得更加容易,因为这项操作非常耗时且无聊:标志必须从信息系统中产生文档的每个应用程序的每个消息模板中更改,而我们谈论的是数千个模板和数月的工作来调整标志。最初的方法是在每个地方传播服务器共享路径:这样,当文件被修改时,更改标志将自动在所有地方发生。幸运的是,我们有时间稍微思考一下,并决定将方法切换到通过内容协商公开不同格式的标志资源的 URL,并使用额外的 URL 查询参数来指示要发送标志的参考时间。这样,大多数只是创建文档的应用程序不必关心传递此参数,并将获得最新的标志位图资源;但对于少数法律约束的应用程序,它们必须能够在过去某个时间点与精确的像素完美形式融合信函,仍然可以这样做,而不会在旧信函上出现新标志,这会引起麻烦。如今,这个组织已经配备了一个具有存档功能的内容管理系统,现在以更好的方式解决了这个问题。技术进化以新的方式处理了功能需求。再次强调,这只是时间问题!
关于时间和技术债务在信息系统中的重要性,这里有一个最后的注意事项:可能存在比技术债务更糟糕的事情,我们称之为“功能债务”或更精确地说是“语义债务”。这将是第九章的主题。但就目前而言,我们将以一个非常理论性的内容结束这本书的理论部分:一个完美的信息系统!
摘要
在本章中,我们展示了时间如何影响信息系统分析中的每一件事,因为它是一个在使用过程中不断演变的活生生的实体。时间在我们的日常生活中如此普遍,以至于在构建信息系统时常常被遗忘,但它却是设计它的关键,使其能够经受时间的考验,并具有较低的 TCO。
敏捷方法是最早处理这种时间相关现实的方法之一,它彻底改变了软件的创建和处理方式。同样的方法可以应用于整个系统,即作为一个协同工作的应用程序集合,这意味着技术债务可以全局处理并保持受控。这种技术债务的概念命名并不准确,正如未来章节将展示的,语义学非常重要,因此我建议时刻牢记这一点,并在可能的情况下调整命名,正如本章所提出的。
本书剩余部分包含许多食谱,以帮助您减少这种技术债务或至少将其保持在可接受的水平。但在下一章中,我们将尝试想象一个完美的信息系统,几乎没有任何债务或耦合。
第五章:一个乌托邦式的完美 IT 系统
记得在第四章中关于由计算机、人类和狗组成的完美信息系统笑话吗?其中有些真理,因为人类一直在变化(他们的思想、他们的做事方式、他们在商业中遵循的规则、他们想购买的东西等等),而计算机则乐于重复、稳定、定义明确的任务。当然,这两者并不兼容,因此需要狗来防止人类把计算机工作搞得一团糟。
但谁首先创造了计算机?当然是人类。所以,这个笑话并不是说人类应该完全不接触计算机,而是创造它们,然后让它们在不做任何改变的情况下完成工作。当然,这只有在第一次尝试通过某种奇迹变得完美的情况下才可能实现。
在本章中,我们将涵盖以下主题:
-
理想系统的概念
-
理想系统中的数据管理
-
理想系统中的规则管理
-
理想系统中的流程管理
-
我们能接近这个乌托邦式系统有多近?
本章将描述一个类似于 100%理想信息系统的东西,它能够适应不断变化的商业变化,同时在速度、鲁棒性和能源效率方面仍然表现出色。这样的系统是由多米尼克·沃基耶在他的基础书籍《可持续信息系统 - 使用 SOA 重铸 SI》中想象的。构成理想信息系统的三个主要实体是主数据管理(MDM)、业务规则管理系统(BRMS)和业务流程建模(我们将在稍后详细解释)。接下来的几节将逐一解释它们,本章将以分析如何在现实中创建这个乌托邦式系统以及什么会阻止它为结尾。
理想 IT 系统的概念
在我们详细说明这样一个理想系统的不同部分之前,我将进一步解释这样一个模型的有用性。当我们对想要达到的目标有了更清晰的认识时,我们将详细说明概念的不同部分,并尝试使它们更加具体。
理想结构的用途
“理想”或“乌托邦”是工程师们通常有一种奇怪关系的术语。尽管他们的大部分思考都是在假设的情境中进行的,以在理论上取得进步,但他们知道实践总是与理论有很大距离,并且根据他们在理论与实践之间的掌握程度,工程师们很容易陷入许多陷阱:
-
仅停留在理论层面可能会让他们的思考从未应用于现实世界,最终导致时间的浪费以及无法真正了解他们工作的价值。
-
缺乏足够的理论基础来在他们的领域取得进步;仅仅停留在工作的实践中可能会帮助完善某些领域,但很少带来强大、改变领域的革命性想法。
-
最糟糕的是:在两者之间切换,但从未真正建立连接,使实践从理论中受益并从实践中证实理论。这种将两者结合在一起的能力,在我看来,是构成最佳工程结构的关键,无论是公司还是甚至国家,只要它们在最高水平上教授和组织这种能力。
正因如此,在这本书中有一个位置,否则它非常偏向于实践,并从许多现实世界的信息系统中获得了丰富的经验回报,但也有一些理论和理想化的思考。
设计的起源
我们将要描述的理想系统的结构,最初是在多米尼克·沃基耶的《可持续信息系统 - 使用 SOA 重整信息系统》中提出的,他是法国最著名的架构师之一,也是 PRAXEME 方法的倡导者。我有幸在 10 多年前接受了培训,其解释非常清晰,我立即购买了他的书籍,这些书籍很快成为我在信息系统工业化咨询工作中的参考之一。
这个想法背后的原则是,了解许多信息系统,可以描述一种元系统,它将包含所有必要的功能,只需几个通用的模块,这些模块可以定制以实现与业务相关的方面。这三个模块中的每一个都将非常通用,与任何特定的业务都没有关系,这也解释了为什么整个系统中内容如此之少。这三个领域如下:
-
存储数据并使其可用(简而言之,这正是数据库所做的事情)
-
执行业务规则以做出决策(这将涉及使用数据的软件应用程序)
-
通过协调许多小任务来实现复杂过程(尽管非常近似,但这正是使应用程序协同工作的关键)
多米尼克·沃基耶确实能够将任何业务功能指认为这些三个不同技术功能的组合。在与许多小型和大型公司的工业信息系统合作后,他认为这三个模块结合起来将能够处理任何给定业务领域的所有可想象的特征。尽管现在这种结构还没有被广泛使用,但它可能只是对未来几十年将要工业化和良好控制的工业信息系统的一个非常期待的预览。至少,从我的谦逊观点来看,这是这样一个具有前瞻性结构的最佳候选人。
使用愿景来定义目标
我们为什么要讨论这样一个如此未来派且我们没有任何证据证明其能够在实践中应用的架构呢?这会不会导致过度思考或过度设计?如果你自己问自己这些问题,你完全有理由这样做,而且这一章节并不是要推动你为你的下一个信息系统架构使用这个工具。
就像任何模式或架构工具一样,它主要存在是为了给你一些想法,如果其中的一些部分适合你的系统,那么它帮助你前进并加速思考将会是非常好的。
但是,拥有一个“理想”或“乌托邦”的愿景也可以帮助你给你的实现提供一个全局的导向。记住敏捷开发的隐喻,我们不是射箭,而是带着箭走向目标,当达到目标时用手将箭射出?好吧,为了做到这一点,我们同意我们需要知道目标是什么样的,以便找到并达到它。有时候,目标形状可能很难想象。当然,一个以业务为导向的思维方式会给你最好的想法,你应该保持功能性的关注。但如果业务想法相当模糊呢?也许一个“柔软”的、可适应的信息系统会允许你在事情变得稍微清晰一些的时候开始工作。开始构建系统甚至可以帮助企业主更好地思考他们想要什么。而且如果你有一个可以非常快速适应他们构建业务想法的理想系统,那么这可能就是你需要的项目类型。
当然,完全通用的成本(在性能上,以及初始设置的时间上)将会更高,但如果你的情况是这样的,通过简单地定制信息系统的参数来适应不断变化的企业目标的能力可能会在很大程度上克服这些缺点。而且一旦企业愿景确定,就没有什么可以阻止你重新实现一些现在稳定的部分,用特定的、优化的代码来替换,如果需要的话。
既然愿景已经清晰,我们将深入探讨构成这个理想 IT 系统的三个部分,即数据管理模块、规则管理模块,以及最后的流程管理模块。
理想系统中的数据管理
MDM 是理想系统的第一部分。在讨论数据是如何处理和使用的之前,解释数据将采取什么形式以及如何管理它是合乎逻辑的。这就是为什么我们将在讨论 BRMS 和 BPM 之前解释一个乌托邦系统中的 MDM 概念。
数据是信息系统的血液
数据是任何信息系统中最重要的组成部分。有些人甚至可能认为它们完全是关于数据的,因为“数据”这个术语在过去十年中被炒作得如此之高。
虽然很难想象一个没有至少一个数据库来存储数据的信息系统,因为这意味着没有任何关于任何商业事件的知识留存。可以想象一些完全短暂且未存储的信息片段,但由这些特定案例组成的整个系统听起来几乎无法理解。
这意味着数据应该首先得到关注,这正是理想通用信息系统第一个模块的主题。这个特性被称为 MDM。在这个情况下,“主”一词指的是系统中最重要的数据,那些被系统中的大多数参与者使用的数据。但在这种乌托邦式的系统特定情况下,每一份数据都会被视为如此,并放置在一个单独的模块中,该模块负责“MDM”这一职责。
数据作为 21 世纪的石油——真的吗?
在我们更深入探讨这样一个模块可能是什么样子之前,关于数据的重要性有一个小的补充说明:你可能听说过关于数据的“21 世纪石油”这个表达。这是为了强调数据已经成为商业组织如此重要的一部分,它可以与 20 世纪带来巨大工业进步的石油相提并论。
许多工业公司使用数据来跟踪他们的机器,优化生产,并将其与销售和股票联系起来,简而言之,通过“数字孪生”虚拟化工业流程以更好地控制它。但对于某些行业来说,数据甚至可能是原材料本身,如今许多数字原生公司纯粹通过数据收集、精炼和转售来赚钱,尤其是针对广告活动。
石油开采,自 20 世纪以来就已经发生,极大地加速了几乎所有领域的工业生产(重工业、铁路、化肥、农业机械等),同时也催生了众多公司自身进行石油的勘探和生产。这种比较是公平的,尽管在保持一些重要差异的同时需要谨慎。
首先,数据的类似物——石油裂化精炼塔——尚未发明,或者至少尚未以标准形式出现。当然,一些商业智能工具和大数据方法在某些情况下可以提供帮助,但数据项目失败率(据估计在 70%到 80%之间,取决于研究)表明,我们距离石油精炼厂的工业化还有很长的路要走。到目前为止,数据对于大多数公司来说仍然部分地处于未开发状态,就像 19 世纪末土地所有者对土地的态度一样:污染。当然,数据中存在价值,但它完全未被开发,且没有标准的方法来提取这种价值。
我们也不要忘记,数据仍然基于实际的石油:数据中心消耗了大量的能源(很快,地球上 8%的能源将被用于数字用途,这超过了航空旅行,并将很快达到汽车运输)。尽管一些数据中心所有者假装使用可再生能源(但只是购买补偿活动,这并不会减少全球石油消耗),但大多数数据中心仍然严重依赖石油作为能源。此外,服务器的生产、网络硬件的使用以及它们的运营功能都是能源的大消费者,最终也是石油的大消费者。
因此,我们必须牢记,数据确实可能成为下一个世纪的石油,但我们目前正处于这种状况。本章中描述的数据管理方法可以帮助你达到这一目标,特别是通过使用治理来提高数据质量,这仍然是阻碍数据利用的首要原因。
一个真正的“无所不知”的系统
我不会在本章中过多地详细阐述,因为 MDM 将在本书后面的专门章节中讨论,并且将结合一个真实案例进行解释,这有望使一切变得更容易。现在,我只会提出将 MDM 与“无所不知”的系统进行比较。确实,一个制作精良的 MDM 不仅会知道实体当前的状态,还会知道每个实体的整个故事,并记住它们的不同状态和修改,从摇篮(创建)到坟墓(存档或删除)。正因为如此,持久性是 MDM 模块的主要责任。
然而,还有一些其他的责任:
-
研究的便捷性:这与坚持一致,因为如果不提供数据,那么保留数据就没有太多意义。这个特定的责任可能在其实施过程中被委托出去,通常当 MDM 应用程序使用索引引擎时(全文搜索是一个复杂的问题,它证明了将责任分解成几个更原子的责任并委托给专门的模块是合理的)。
-
报告/商业智能的来源:就像数据搜索一样,报告是 MDM 模块可以委托给其他实现的重要责任,例如数据湖。
-
处理实体多个版本的能力:正如之前所解释的,一个真正的 MDM 应该知道数据实体的每一个版本,而不仅仅是它的“最新”状态(引号用来强调这个“最新”概念在许多系统中都是一个痛点,尤其是在分布式系统中,因为它依赖于数据的一致性,以及因此事务、乐观/悲观锁等等)。
-
管理属性值验证的能力:尽管这可能与 BRMS 共享责任,我们将在本章后面讨论 BRMS,但 MDM 本身可以执行少量验证以确保其基本一致性。这不应与数据正确性混淆,数据正确性取决于其目的(单一数据可以适用于商业用途但不适用于另一个用途),并且可以根据数据版本以及业务规则的变化而很好地变化。
-
业务规则:一些入门级业务规则,仅使用 MDM 系统内部的数据,也可以添加到 MDM 中,尽管如前所述,它们大多数都与验证有关。
-
历史处理:除了存储数据的连续版本外,MDM 还必须能够根据目标时间轻松检索这些版本,浏览历史记录,找出谁对数据进行了哪种类型的更改,等等。
如果这还不清楚,请不要担心——本书后面提供的关于一个 MDM 存储个人数据的例子将帮助您理解所有这些责任是如何被使用和实施的。
与 CQRS 的关系
如果你对数据架构感兴趣,你可能已经将存储数据版本化修改和搜索其不同状态之间的关注点分离识别为命令和查询责任分离(CQRS)的原则。我们确实在谈论相同的原则,即使 CQRS 远远超出了仅仅分离这两个责任,并提出了如何使它们一起工作的技术方法。
CQRS 是一个太大的主题,无法在本书中与其他所有主题一起处理,但它非常适合 MDM 模块的责任分离,我强烈建议你在创建 MDM 实现时使用这种方法。本书接下来几部分将要涵盖的例子将使用 CQRS 方法,尽管这是一个非常有限和简化的实现。再次强调,这是一个版本选择,因为添加完整的 Kafka 引擎会使我们难以专注于应用程序本身,并且会使我们远离本书的主题。
数据质量的需求
回到石油的比喻,里面含有沙子是一个真正的问题,因为石油必须被足够净化才能进入工厂。否则,不仅输出产品的质量不会很好,而且这还可能损坏这个极其昂贵的工业设备,即精炼塔。这就是为什么轻质原油是最好类型的石油之一,也是为什么来自沥青砂的天然气之所以如此昂贵进行精炼(除了对我们环境的灾难性影响)的原因。
在信息技术领域,数据与清洁数据之间有着强烈的等价性,因为清洁的数据是一个伟大的产品,它将允许对商业活动进行精确的报告,对可能出现的问题进行快速洞察,并总体上更好地控制公司。与行业内任何人讨论这个问题,每个人都会同意数据质量的重要性……然而,据估计,数据科学家的工作中超过一半的时间是用来清理数据的。我见过一些公司,这个比例危险地增加到了大约 80%。我说“危险”,有多个原因:
-
首先,为这样的低智商任务支付高薪是一种金钱的浪费
-
这通常也是一种浪费时间的行为,因为优秀的数据科学家通常会在接下来的 6 个月内离开工作,如果他们不得不花更多的时间在清理和组装数据上而不是“让它说话”
-
最后,它揭示了管道中的问题,因为发送这些脏数据的 IT 系统设计不正确,这意味着这些报告问题可能不是唯一的
为什么每个人都同意数据清洁的重要性,但情况仍然如此糟糕?好吧,恰好数据管理通常不是关于技术部分(这相当简单,因为我们现在有这么多工具)而是关于组织部分:谁负责哪些数据?谁有权利收集和更新它?哪个团队决定如何切割数据以及哪些数据进入这个或那个服务责任?所有这些问题都是所谓的数据治理的一部分,尽管它非常重要,但许多公司并没有处理这个问题,尽管他们拥有所有存储数据的工具。再次,这是与往常一样的根本原因:技术从未解决过组织问题,但编辑们非常擅长让你相信这一点……而且,作为一个公司老板,你非常希望它是真的!但是,不,你将不得不做一些不有趣的工作,比如数据分类,找到数据所有者和数据管理员,实施定期的会议来跟进决定的数据流程,等等。现在创建你的 MDM 模块正是做这件事的正确时机,因为它的成功很大程度上取决于这些行动。
设计一个通用的“参考”
几乎总是会有一个内部标识符来指代存储组织主要实体的软件应用程序,例如产品。大多数时候,人们会根据技术名称来称呼它——例如,“产品数据库”或“产品 Excel 文件”——但这是一种坏习惯,因为它将功能概念与技术、软件概念耦合在一起。这不仅可能分别发展(“产品文件”变成了“产品数据库”),而且它们应该尽可能多地这样做,因此应该保持分离。
从这个角度来看,“产品列表”已经有所改进,但我的最爱(因为它与我母语法语中的一个等效词非常接近)是将其称为“产品参照物”。这个名字承载着这样一个重要概念:它假定数据内部具有参考地位,同时也可以将其视为空间中的参照物,这回到了信息系统地图的概念,就像我们会有一个地理地图,其中包含参照物内的坐标。
重要提示
命名可能会更糟糕,正如我在第三章中提到的轶事,我的一个客户将文章的参照物简单地称为“Serge 的文件”。这导致了很多混淆,因为它隐藏了保持此文件最新状态的极端重要性,而这甚至不是 Serge 的正式工作!
大多数情况下,所谓的“参照物”都是针对单一类型的实体(产品、销售、客户等)。本章所描述的理想化系统的想法是,对于任何类型的实体都使用单一款软件。在本书的其余部分,我们不会深入探讨这个理想化系统,而将保持一种更标准的方法,即针对特定的业务领域实现一个参照物。这也使我们能够为每种情况采用最佳技术(“最好的面包”方法),而不是“一刀切”的方法,尽管在理论上很有趣,但在现实世界中却极其复杂。
MDM 的实施选择
无论喜欢与否,Excel 仍然是你可以拥有的最简单的 MDM(主数据管理),在许多小型公司中,使用 Excel 进行良好的组织流程已经可以走得很远,前提是你已经消除了良好组织的重要缺陷(例如,一个集中式文件,不同用户有权限而不是到处都是副本,严格的数据质量组织等)。
当然,将需要一定程度的复杂性,这将导致专门的实现。这听起来很疯狂,但针对 MDM 的软件应用非常少。微软基于 SQL Server 提供了一项名为 Master Data Services 的解决方案,但它的使用听起来非常有限,至少在我的知识领域内是这样。在与微软讨论后,该产品确实已被放弃,其继任者是名为Profisee的合作伙伴产品,它与Microsoft Purview合作以提供数据治理功能(learn.microsoft.com/en-us/azure/architecture/reference-architectures/data/profisee-master-data-management-purview)。Semarchy (www.semarchy.com)听起来是集成 MDM 的一种新颖且有趣的方法,但我还没有尝试足够多,无法推荐它。
我看到一些公司使用无头 CMS 系统,如 Strapi、Sanity、Cockpit、Prisma 等,来创建可以作为入门级 MDM 的后端。但这通常缺乏良好的数据版本控制系统,并且治理的实施仍然是集成商的工作。总的来说,这是一个非常基于技术的方案,如前所述,MDM 远不止是简单地存储数据。
这就是现成方法的概况,诚然这并不走得很远。我至今所见到的每一个 MDM 的正确实现都是一个专门的开发。一些商业应用程序,如 ERP,有时对产品或客户有良好的参考,但大多数都缺乏业务上的对齐。
小贴士
可能听起来逻辑上 ERP 可能不与业务对齐,因为它试图适用于许多业务领域。尽管如此,当你看到前五的 ERP 系统为顾客和供应商提出两个不同的领域,忽略了大多数现有公司可能的数据重复问题时,这表明问题不仅仅是商业的多样性,还有这些 ERP 公司的非常方法,他们认为建模业务是 100%客户的责任,而它应该是基于书面、向前兼容的标准共享责任。但这将违反供应商锁定,这不符合他们的利益,这就是为什么唯一的前进方式是客户选择一个接受业务实体表示标准的实现,包括在(罕见的)它们不存在的情况下开发这些标准。
剩余的——并且代表了运行中的大多数 MDM 系统——是专门的应用程序与数据库(有时是索引引擎,甚至是一个数据湖,如前所述)相结合。它们是由内部 IT 或外部软件公司为特定的业务用途定制的开发。如果你从全球的角度来看,许多公司的需求非常相似,那么这将是时间和金钱的巨大浪费。然而,由于软件行业缺乏使用的业务标准,目前这只是一个现状。
现在是理想系统的第二部分:在讨论数据之后,我们需要讨论业务规则。
理想系统中的规则管理
业务规则是应用于数据上的谓词,以帮助实施对业务必要的现实世界决策。例如,你可能声明,只要客户没有检查他们的银行坐标,就不能向他们发送产品。这是一个业务规则,因为它可以不带有任何软件实现的迹象来表述:这可以通过工厂中的人手动检查,通过电话与会计确认规则是否得到遵守,然后再将包裹发送给客户。
规则作为信息系统中的神经系统
如果数据是信息系统的血液,那么业务规则就是其神经和肌肉网络:它们利用血液以特定方式实现某些活动。在我们的比喻中,一个业务规则可能是“如果你感觉手指有灼热感,手臂必须缩回。”这个规则在我们的脊髓反射系统中得到实现,它使用手指中的传感器通过神经发送信息/数据,导致相关手臂的肌肉无法被控制。
在 IT 领域,业务规则的实现主要是所谓的应用程序。这通常是业务规则包含的地方。在我们的例子中,ERP 系统中必须有一些代码,它会警告包装准备系统客户尚未通过已验证的支付方式条件。包装系统的反应可能是这个订单将不会处理,或者可能是它将提前处理,但实际的物理包装将在发货前保留。实际的实现是每个模块的责任,但业务规则保持不变:“只要我们没有验证我们可以收到付款,就不会有客户交付订单”,
业务规则还包含你将在信息系统中执行的所有计算,从最复杂的到最平凡的。让我回到这一点,因为关于责任正确划分的某些事情需要说:任何客户端(GUI 或调用 API 的应用程序)都不应该处理业务规则,这些规则始终必须在服务器端实现。这肯定是你所知道的事情,听起来也很合理:毕竟,显然这些重要的功能推理必须集中化,以确保它们以相同的方式应用于所有地方。但看看任何客户端几分钟,你会发现有很多在本地做出的功能决策,有时甚至是最合理的。例如,考虑从毛额和增值税率计算净额。当然,增值税率在应用程序的生命周期中不会经常变化,计算方式本身也非常稳定,因此应该没有大问题,可以信任客户端进行这种计算,而且我们通过避免往返服务器而获得更好的性能。
好吧,但你总是可以想象业务规则会发生变化的方式。正如已经想象的那样,增值税率可能会变化。此外,如果我们谈论多行订单的净额计算,你必须在一个实体中处理多个增值税率。问题归结为风险管理:如果你知道你永远不会遇到这些特定情况,那没问题。如果你怀疑它们将来出现的可能性,你应该首先向你的产品所有者询问。如果他们对此不确定,你将不得不比较不同方法的开销:
-
您可以在所有客户端中硬编码业务规则并希望它不会演变。如果它们不多,并且您可以轻松升级它们,那就没问题。
-
您可以通过将利率作为您使用的应用程序的参数(当它们支持时)来为可能的变化做准备;这样在调整时已经更好、更容易,而且无需巨大的前期成本。
-
如果您预计在未来几年内业务规则将发生变化,并且您知道调整软件将会很复杂,那么您可能应该将规则放在服务器端、集中式的应用程序中。当然,它可能会稍微慢一些,但客户端缓存可以帮助,并且您将具备未来适应性。
-
如果您处于一个将发生大量业务变化的情况,并且许多业务规则需要处理,那么您将达到一个投资于一个专门用于管理您的业务规则的集中式软件是有意义的点。
BRMS 作为处理业务规则的专用工具
现在,让我们集中关注最后一个假设,即您需要所谓的 BRMS。再次强调,这并不是您在每一个信息系统中都需要的功能模块,最简单的系统也不需要这样的安装努力。但请注意,大多数信息系统的起点都很简单,但会变得更加复杂。真正的困难在于,一旦您进入生产阶段,用 BRMS 定制替换现有规则将会更加困难,因为如果它们没有在 API 合同下统一,所有调用都必须更改。这就是为什么这个乌托邦式的信息系统是有意义的:它展示了如果您对您的信息系统认真负责,并且不想对其打任何赌,您应该做什么(并且不会隐藏投资的规模)。如果您从一开始就知道它将会增长,您的活动将会变得工业化,那么您应该从一些一开始可能看起来过于复杂和昂贵的工具开始,但它们将在未来带来巨大的回报。如果您对此不确信,只需阅读所有那些因为未投资于他们的信息系统而失败的初创公司的经验回报,尽管他们的想法得到了客户的认可,他们的定价合理,并且他们拥有市场:这是初创公司失败最常见的原因之一,也是大公司(后者通常不提供任何反馈)。
业务规则的常见实现
你会如何实施一个 BRMS?可能会让你微笑,但同样,Excel 通常是 BRMS 最简单的实现方式,大多数时候人们并不知道这一点。一个会计肯定会告诉你(如果你问的话),他们没有使用任何 BRMS 来完成工作。但与此同时,他们会吹嘘一个手工制作的 Excel 工作簿,其中包含了他们在日常工作中以及每月结算期间所需的所有计算(以及相应的业务规则,无论是监管还是公司层面的)。如果不是 BRMS,那又是什么呢?事实上,由于“真正的”专用 BRMS 相对较少使用,对于大多数小型公司来说,业务规则实际上都包含在 Excel 文件中。
在大型公司中,有更多的业务线应用,例如 ERP,或者当公司有内部软件开发能力时,甚至定制软件。在这种情况下,集成到应用中的业务规则比例上升,但这并不一定是最好的。为什么许多以业务为导向的人尽管存在缺点,却仍然喜欢 Excel 电子表格?简单来说,因为他们可以完全控制它,并且不需要依赖 IT 部门进行更改、添加功能等。在某个应用中开始实施业务规则,耦合问题立即显现:
-
是否可以在不重新编译、测试和重新部署应用的情况下更改规则?
-
如果规则可以定制,是否可以通过功能操作员来定制,或者你需要 IT 部门来实施更改(或者最好是在最佳情况下,对他们进行培训)?
-
我们如何处理必须在特定日期和时间更改的规则(例如,许多与会计相关的或业务范围内的报告规则在午夜 1 月 1 日)?
-
是否可以有两个业务规则的同时版本,或者我们是否必须停止软件在年底,以确保在新的一年规则更改之前没有任何操作(不要笑:这种情况经常发生……)?
尽管存在这些潜在问题,但在专用业务应用中实施业务规则已经是一个很大的进步,尤其是如果这是在服务器端完成的,最好是作为实现一个良好文档化的、以合同为先的 API。这种解决方案比 Excel 电子表格更不容易出错,因为规则的不同实现可能会在整个组织中传播,导致混乱。它还倾向于提供业务规则的第一级治理,因为不同的服务负责“他们”的软件应用,以及其中实施(或定制)的业务规则。
然而,这些实现并不能代表我们乌托邦式系统中的 BRMS,即一个包含公司所有业务规则的独特系统。而且,如果你想走上一条未来证明的信息系统之路,你可能会更倾向于使用像 Drools(一个开源包,由 Red Hat 的 JBoss Rules 实现)或 IBM Operational Decision Manager 这样的软件。这些软件通常会提供以下功能:
-
业务规则的执行,这提供了所谓的决策引擎(DMN代表决策模型和符号,是 BPMN 的伴随标准)。如果你想了解更多关于 BRMS 这个重要部分的信息,我推荐 Drools 提供的优秀文档页面,可在
www.drools.org/learn/dmn.html找到。 -
这些业务规则的存储定义,听起来很显然,但当你加上存储整个业务规则版本历史的要求时,情况就不同了。
-
决策表的使用、事件和动作之间的链接,以及在最复杂的情况下,将混合许多不同规则以找到额外结论的推理引擎(一个过于简化的例子是“所有采购都需要发票”和“发票必须在标题中包含买方的地址”,到“所有采购都需要买方的地址”)。
-
其中一些提供了图形编辑器,让非技术人员能够自行调整规则。尽管编辑器声称任何人都可以使用,但它们仍然相当技术性,需要一些培训。
-
与此同时,沙盒对于人们在将业务规则调整投入生产之前进行测试非常有用。
-
根据所需的性能和调用变化的程度,缓存机制可以是一个很好的 BRMS 补充。
小贴士
Open Policy Agent 是一个专注于授权规则的 BRMS。我们将在第十二章中展示其用法。
最后,在讨论数据和业务规则之后,我们将添加第三个也是最后一个模块,以完成我们的理想 IT 系统。
理想系统中的流程管理
我们理想中的系统可以处理数据,并使用业务规则对其做出决策,但是什么让它运转呢?在第四章中,我们强调了一切都是时间的问题,但没有任何东西具有将数据和规则置于受控、基于时间的运动中的角色。这将是由流程引擎来扮演的角色。
流程作为信息系统的大脑
回到我们的人体比喻,你可能已经猜到,业务流程管理(BPM)作为协调包含业务规则的应用程序动作的模块,可以与大脑的控制塔相联系。正如我们的大脑协调每个肌肉的动作,同时接收来自我们五感的信号,以实现诸如接球、键盘打字或穿衣等复杂的结果,流程描述了信息系统每个部分必须完成的复杂任务,以实现全局目标,而 BPM 引擎协调这些任务,有效地调用负责的模块(或者更确切地说,API,这些 API 本身由技术模块实现;还记得我们在第三章中解释的重要观点吗)。
如果理想的信息系统由一个 MDM 和一个 BRMS 组成,包含每个业务领域的所有数据和所有业务规则,那么 BPM 就是使一切协同工作的模块,通过互操作所有功能。
BPM 的实现
在谈论技术实现(这现在应该是一种本能)之前,我们必须尝试找到一个我们特征的可接受标准。幸运的是,一个无可争议的标准已经存在,我们之前已经讨论过:BPMN。所以,如果你在寻找一种执行流程的演变方式,其中引擎可以完全与设计解耦,你必须使用 BPMN。我已经在一个特定的编辑器的 GUI 上测试了编辑 BPMN 2.0,并在另一个编辑器的执行引擎上执行,它运行良好,这表明了该标准的成熟度。
当然,并非所有信息系统都需要我们讨论的这种复杂性,但它们在某个时候都会使用一个中央协调系统,即使大部分可以手动操作,流程可能甚至没有在 BPMN 中设计,而只是信息系统参与者头脑中知道(再次强调,这并不意味着一切都是基于计算机的)。而且你知道吗?当我们根本不需要任何复杂性时,我们会找到我们老朋友 Excel!
小贴士
到目前为止,我知道你一定认为我是一个绝对的 Excel 粉丝。然而,我并不是,我也亲眼见证了由于使用 Excel 处理共享数据、没有控制其在组织中的传播以及更多问题而导致的巨大功能问题。我完全清楚它如何通过让几乎每个人都能拥有信息系统的一小部分信息来阻止人们共同工作。但与此同时,我也不赞成忘记电子表格为 IT 和业务专家带来的所有贡献。而且,现在大多数信息系统如此不成熟也不是我的错!所以,如果你必须使用一个粗糙的工具来完成工作……那就这样吧!Excel 将长期成为信息系统的“瑞士军刀”。拥有一个完整的工具箱会更好,但如果你只能拥有一个工具,那就拿这把刀而不是拔钳或一些其他奇特且高度专业化的工具。回到软件上,现在对于 MDM、BRMS 和 BPM,比 Excel 电子表格更好的替代方案已经存在。然而,如果你意识到 Excel 的缺点并控制它们,其多功能性和易用性将使你在信息系统的许多部分中加快速度。特别是,尽管 MDM 和 BPM 在 Excel 之外要好得多,但我经常通过在 Excel 中放入复杂的计算并将电子表格插入 API 中,建立了一些“低质量的 BRMS”,并取得了巨大的商业成功。是的,这有点像是一种黑客行为,但只要它隐藏在 API 后面,这种临时、快速和简陋的实施就没有问题……而且业务用户非常喜欢它!
我们能有多接近这个乌托邦式的系统?
到目前为止,我们已经描述了理想信息系统的内容,这是由 Dominique Vauquier 在他的开创性著作中描述的。这种 MDM + BRMS + BPM 的组装也可以在其他一些来源中找到,例如,SHIFT 项目在其向更环保的 IT 行业数字化转型的方法中描述的“可持续信息系统”,或者敏捷链管理系统(ACMS)方法。
当然,这是一个理想化的愿景,所以问题不是将其用作实际信息系统都应该基于的蓝图,而是在设计新的信息系统或改进现有信息系统时,我们能够接近它有多近。
优先考虑上下文
各种上下文元素可能会促进本章讨论的方法的使用:
-
全新的信息系统:如果你足够幸运,可以从一个新创建的组织从头开始,那么遗产就不会阻碍你,这是一个可以使理想信息系统得以实现的巨大因素。
-
复杂程度:当然,这还远远不够,因为正如之前所述,只有当它得到商业的证明时,这样的理想系统才有其存在的空间。如果你的信息系统必须高度可进化,因为商业规则和市场经常变化,数据结构不明确,并且必须快速适应流程,那么这又是另一个可以促进之前所述架构采用的因素。
-
投资批准:再次强调,这个因素也不够,因为商业可能需要这样一个高度复杂的信息系统,但投资者可能没有意识到这一点。对于许多首席执行官来说,IT 只是一个成本中心。但如果你理解信息系统已经成为大多数工业活动的支柱,那么你又有了一个积极的因素。
-
实施能力:一切看似顺利,但不要低估实施这样一个信息系统所面临的困难。这种方法与大多数专业人士所知的方法大相径庭,以至于你很难找到能够部署它的人。你将不得不说服他们,培训他们,并让他们适应你的方式,而现有的反馈和经验回报非常有限。
诚然,这些因素有很多,而且你很可能现在没有它们。对于像我这样年龄的人来说,甚至有可能我们永远看不到这种完美的协调,使得这种乌托邦式的结构成为最常用的一个。但它确实存在——其优势是显而易见的,而且行业变化的加速和数字化转型将毫无疑问地使其成为一个越来越被观察到的选项。技术监控也显示,在过去 3 到 5 年中,MDM 的主题已经取得进展,一些先进的公司已经实施了它们,并伴随着一个内部开发者平台,带来了非常有趣的结果。简而言之,我们正处于趋势的初期,但波浪增长的可能性很高。
接近目标,敏捷的方式
这样的理想信息系统目前还不是主流,并且在未来也不会成为标准,这并不意味着它们没有兴趣。这就像一个在敏捷团队工作的人,他不想知道最终软件的愿景是什么,声称他们只对当前冲刺中将要完成的事情感兴趣!
同时也存在一个理想——一个愿景——来提供方向,指引我们的旅程。还记得箭的隐喻吗?我们必须知道目标的样子才能定位并达到它;否则,我们只是在漫无目的地四处走动,希望得到最好的结果。总之,请不要因为这一概念不是立即实用的就放弃它:了解它可以帮助你在未来在信息系统不同的方向之间做出选择,因为你将感觉更接近这个长期、理想的方向,这将为你带来更大的价值。可能性是,你永远无法达到理想状态,但希望,所采取的方向将为你的信息系统带来更多的价值和灵活性。如今信息系统的情况如此糟糕,拥有一些高于平均水平的东西在所有工业流程数字化时代已经是一个巨大的优势。
摘要
在本章中,我们展示了可能是一个理想的信息系统。我们讨论了在软件工业部署中可能出现的所有问题,特别是关于它们的演变,因此展示不仅限于点解决方案,还展示了如何全局性地解决这些问题。当然,这一方法的大部分仍然是乌托邦式的,但这一练习有趣之处在于它表明,如果构建得非常好,只有三个模块就可以满足任何商业需求。数据、业务规则和业务流程之间的分离是即使在您工作的信息系统远非理想的情况下也应该记住的事情。
本章相当概念化,但这本书的第一部分就此结束,这部分旨在涵盖大量的理论背景。本书的下一部分将涵盖更多关于架构的实用方法,为了完全阐述理论与实践(这是本书的主要前提),第三部分将提供源代码,展示如何实现我们讨论过的良好架构的概念和方法。特别是,这里解释的三个主要模块将在一个具有有限功能范围的示例应用程序中展示,但具有所有成长为完整信息系统的必要复杂性:
-
MDM 将基于 NoSQL 数据库,后端服务器使用 ASP.NET,并公开 API 设计的合同优先
-
将使用一个独立的 BRMS 进行授权管理
-
将采用 BPM 方法来展示如何使流程可调整,使用低代码实现
总结来说,在接下来的七个章节中,我们将通过使用适用于所需用例各部分的示例,运用经过实战检验的方法来设计示例应用程序。在接下来的五个章节中,我们将分模块实现这个示例应用程序。最后,最后三个章节将讨论部署和调整这个示例应用程序,就像它在生产环境中一样。随着时间的推移,需要做出调整以保持其与功能业务需求的一致性。
进一步阅读
一个可持续的信息系统 - 使用 SOA 对信息系统进行渐进式翻新,作者:皮埃尔·博内/让-米歇尔·德塔弗尼耶 - 多米尼克·瓦奎尔 - 赫尔墨斯 / 拉瓦锡 - 2007 年 11 月 - ISBN-13: 978-2746218291
第二部分:架构框架和方法
在第一部分,它相当理论化,因此即使没有技术背景的读者也能理解之后,本书的第二部分第二部分变得更加实用,并解释了如何将某些原则应用于信息系统的架构层面,以使其更加完善。这就是我们将讨论如何设计与业务良好对齐的低耦合服务的方法。将涵盖通用软件原则,但也会涉及更广泛的方法,例如授权管理和业务规则的外部化。在业务/IT 对齐的信息系统中管理数据的正确方法也将被详细阐述。
本部分包括以下章节:
-
第六章,SOLID 原则,从代码到系统
-
第七章,C4 和其他方法
-
第八章,面向服务的架构和 API
-
第九章,探索领域驱动设计和语义
-
第十章,主数据管理
-
第十一章,业务流程和低代码
-
第十二章,业务规则的外部化
-
第十三章,授权的外部化
第六章:SOLID 原则,从代码到系统
从本章开始,我们将从理论部分转向,尽管我们还没有开始编码(这将在第十三章开始),但我们将开始将理论应用于设计由几个应用程序组成的小型信息系统。我们将分解不同的功能,展示它们如何帮助产生业务流程结果,并创建这些功能背后的软件。为此,我们将设计不同的组件和涉及服务的 API 合同,并思考数据应该如何设计和治理。在第十三章中,我们将使用所有这些设计阶段来实际实现样本信息系统。
当然,这个信息系统将在范围和复杂性上有所缩减,但这个练习已被设计成包括应该做出的大多数重要决策。你会发现严格的责任分离,过程和功能之间有良好的分离,通过 API 解耦服务,标准化合同,采用最佳实践方法,将软件堆栈适应所需的功能,软件和硬件之间的独立性,以及许多其他原则。
在本章中,我们将通过思考它应该公开的功能来开始设计我们的演示系统。为此,我们将使用SOLID 原则,将它们扩展到信息系统。SOLID 是由软件开发五个基本原则的首字母组成的缩写,这些原则如下:
-
单一责任指出一个模块应该只做一件事
-
开/闭区分了对进化的开放和对修改的封闭
-
Liskov 的原则解释了替换应该如何工作
-
接口分离随后讨论了合同应该如何与业务功能紧密对齐
-
最后,依赖倒置处理了耦合以及它应该如何进行,这与大多数情况下看似自然的方式相反
这些原则,通常应用于软件应用,实际上适用于每个软件系统,并且是设计它们不同模块的绝佳方式。因此,我们将使用它们来设计我们的样本信息系统。但首先,我们需要描述这个系统的业务需求。
描述样本信息系统需求
在进行任何分析之前,我们将想象系统所有者希望从系统中得到什么。当然,正如我们解释的那样,时间是信息系统设计中非常重要的约束条件,其生命周期很长,我们将模拟我们最初并不了解所有需求的事实。特别是,本书的最后一章将模拟信息系统的虚构公司出现新的需求,并解释系统将如何适应这些需求。这一点尤为重要,因为本书的主要目标是展示系统应该如何创建或适应,以便其随时间演变的简单性。
为了使练习尽可能真实,同时保持简单以便包含在一本书中,我们将想象公司、信息系统的用户、他们的业务、他们操作的数据等等。这就是我们将在本节中要做的事情。
公司及其业务
我们将称这家公司为DemoEditor,它将是一家与个人作者签订书籍写作合同并随后销售这些书籍的编辑公司。我们将想象这是一家相当小的公司(少于 50 人),并且其当前的信息系统极其简化,主要由一个标准的 Office 365 组织提供电子邮件功能、基本的 SharePoint 文档管理、一个外部的网站,以及大量通过 Excel 工作簿实现的内部功能。
尽管目前这种状况还算舒适,因为信息系统尚未因为长期积累的点对点互操作、遗留软件应用的退化等问题而变成一团乱麻,但它仍然显示出效率低下的迹象。Excel 工作簿的多个副本使得员工难以清晰地看到作者池和书籍写作的状态。此外,由于写作过程不统一,公司总监抱怨无法找到关于全球进度或书籍交付延误的清晰统计数据。
业务主要是寻找适合市场的主题,选择合适的作者,跟进书籍的写作,以及组织合适的销售流程。
信息系统的用户和参与者
50 人当中,大多数是图书编辑。接下来是销售团队,一点行政人员,以及总监。在这个简单的例子中,我们将考虑所有书籍的印刷和发行都外包给另一家公司,而 DemoEditor 只专注于编辑过程。
图书编辑的工作是寻找作者,寻找书籍主题,并将合适的作者与合适的书籍匹配。然后,他们跟进写作过程,确保质量达标。
然后,这取决于销售团队,他们的工作是寻找间接客户,这意味着图书馆或书店组织,因为 DemoEditor 并不直接向读者销售。这意味着商人实际上是通过数量而不是单位来销售书籍的,尽管在我们演示软件系统中我们不会过多地处理这部分,但在实际情况中这会很重要。
最后,总监需要通过销售团队和编辑提供的报告和统计数据来监控数字。公司的顺利运营在很大程度上取决于书籍的截止日期,正如写作质量、主题与作者的匹配以及读者和书店的期望一样。这意味着总监必须衡量所有这些指标,信息系统当然也预期提供这些信息。要求编辑或销售人员每周填写 Excel 表格是没有意义的,因为这会让他们失去实际工作的宝贵时间。
数据操作
如你所想,DemoEditor 的信息系统将不得不处理有关作者、书籍和销售的数据,以及从这些原始数据中提取的一些附加统计数据。作者将通过他们的身份、一些联系信息、可能还有支付版税的银行坐标以及他们的技能信息来识别。书籍将使用业务范围内的参考编号、标题、摘要以及其他关于内容的信息进行注册。销售基本上是向书店销售的书籍数量,以及相关的日期和可能的销售条件。
报告数据将是一切可以由总监用于销售、作者和书籍的业务智能的数据:每个类别销售了多少本书,销售的时间趋势如何,作者在销售放缓和新鲜感不再起作用之前可以就给定书籍完成多少版次,谁是他们的最佳销售员,哪个书店退回的书籍最少或重新订购得最快,等等。报告数据无疑是与时间相关的,这不仅是因为报告随时间演变,而且还因为报告应该显示业务在时间和地理上的动态。
信息系统对公司的重要性
DemoEditor 是一家小型公司,这意味着员工可以“填补空白”并做一点任何事情。虽然这在某些情况下是一个优势,意味着他们敏捷且适应性良好,但它也意味着他们不太倾向于以工业化和可重复的方式做事。电子表格可能会被复制并在公司不同版本中传播,而不是在网络上保持一个独特的参考版本。此外,数据销售被分散到不同的销售人员手中,因为他们往往相互竞争,因此很难统一数量折扣(价格是固定的)以及客户名单。
由于商业管道并不非常正式,一些潜在客户在没有销售人员真正能够提供关于花了多长时间和多少努力才能将潜在客户转化为客户的统计数据的情况下,就变成了客户。总监在不知道如果他们雇佣更多的销售人员公司是否会卖出更多产品时,确实很难。为合适的书籍选择作者也是一个问题。一般来说,编辑对市场有很好的把握,并且非常清楚哪些主题应该被撰写。但是,合格的作者池相当有限,而且作者们大多因他们已经写过的书而知名。大多数时候,编辑不知道这些专家还了解哪些其他技术,而且有时候,在花费了大量时间寻找作者之后,一本书被签约给了一个新作者,结果编辑几周后才发现,实际上有一位已经为 DemoEditor 写过几本书的优秀作者实际上具备新项目的正确技能。对于 DemoEditor 来说,一个更新、共享和高效的知识库对于作者能力至关重要。
负责改进系统的人看到的状况
经理要求你过来帮助处理信息系统。公司里的每个人都清楚 IT 可以更加高效并帮助他们更好地工作,但他们说,他们并不是 IT 方面的专家。由于没有内部 IT 人员,他们尽力而为,但他们意识到小型公司的“自己动手”精神只能走这么远,他们不得不找人来提供一些结构。经理也担心在完成这项工作之前增加公司规模;否则,这可能会带来更多问题而不是增长。
随着业务流程的进行和预算不可扩展,你被分配的任务是“在汽车行驶时更换轮胎”。IT 系统可能会有一些短暂的停顿,但不会持续很长时间。数据必须被清理,但作者和书籍的数据库必须在过程中保持可用,因为它们是公司大多数员工日常工具。经理并不太关心报告不可用或甚至被破坏,因为目前它并不很有用,而且大多数数字本来就不准确。
在接下来的章节中,我们将置身于一个被要求执行这项基础任务并设计更新后的 IT 系统不同组件的工程师的角色,并决定其服务应该如何运行以及应该设计哪些业务领域。之后,我们将实施所有这些,并逐步转型信息系统。但到目前为止,我们必须将前几章学到的理论转化为将指导我们前进的原则。SOLID 原则是一套非常适合的原则。
SOLID 原则及其在任意规模系统中的应用
SOLID 原则是适用于软件应用的重要原则,但它们也恰好非常适合软件系统的一般应用,因此我们可以使用它们来构建我们的项目。我们将逐一解释这五个原则,以及它们如何应用于 DemoEditor 要求的转型和其新信息系统的设计。由于这是一本关于信息系统的书,而不是关于软件开发的书,尽管我们最终会构建一些实现,但我将不会从编码的角度描述这些原则,而只是简要地介绍它们的主要思想和,更详细地,它们在系统设计中的转化。
单一职责原则
这个原则指出,一个类,或者在我们的情况下,一个信息系统模块,应该只做一件事,并且只做一件事。这个定义相当宽泛,但可以通过指出一个实体应该只有一个业务理由来改变来稍微缩小范围。如果同一个类在作者管理和书籍管理发生变化时应该升级,那么这与单一职责原则有关,并且这个类应该被分解成至少两个更小的类。
这个原则显然很容易翻译到适用于信息系统的场景中,它直接应用于模块,无论是服务、组件还是整个系统的其他部分(我们将在下一章回到粒度管理的话题)。系统中的每个实体都应该只做一件事,并且只做一件事。如果从组成系统的软件应用的角度来看,这意味着每个应用都应该负责系统的一个业务领域。由于我们管理作者、书籍、销售等等,我们确实应该为这些找到各自的应用。这个业务领域的概念现在还不够精确,但同样,我们将在未来的章节中回到这个话题,即第九章,详细阐述领域驱动设计方法和领域以及边界上下文的概念。
目前,我们只需同意一个业务领域需要其自己的应用程序。如果你在考虑微服务,是的,我们将遵循这条路线,但请耐心等待,因为这个“微”的限定条件并不总是必要的,我们更愿意谈论“服务”(在第八章中有更清晰的定义)。
这个首要原则听起来可能非常简单(它的表述确实如此),但其含义可能非常深远。为了给出我们将在示例应用程序中必须处理的复杂性的一个例子,让我们展开一个服务依赖于另一个服务的案例,比如“书籍作者”的关系。正如之前所说,作者和书籍管理是两个不同的责任。但我们应该如何处理两者之间的关系?是另一个服务吗?无论如何,当有人从服务中读取书籍实体时,应该如何检索和显示书籍的作者?我们可以反过来问相同的问题:如果有人调用特定作者的 API,我们应该如何显示这位作者参与编写的书籍列表?
深入探讨这个最后场景有助于更好地理解责任的概念。想象有两个独立的服务,每个服务都有自己的数据库,因为它们应该独立。现在,一个用户使用GET请求调用特定书籍的 API,比如说https://demoeditor.org/api/books/123456。模块确实负责发送书籍标题、ISBN/EAN 号码和书籍的一些其他属性。那么关于作者的信息呢?这就是责任原则帮助划清界限的地方。编辑们会告诉你,大多数时候,当他们获取书籍信息时,他们需要知道作者,但只需要他们的标识符和一些主要数据,比如他们的首字母和姓氏。这是书籍服务的责任。如果你再次询问你的产品所有者(编辑们,因为他们将是使用信息系统的人),并且他们需要更多数据,他们会转向/api/authors服务来获取,当然使用/api/books服务提供的初始答案中的标识符。因此,这些数据是第二个服务唯一的责任。
每个了解良好数据库设计原则的读者可能已经感到窒息了,考虑到这种方法需要数据重复。确实,由于/api/authors负责作者的全部数据,包括当然的首字母和姓氏,这意味着如果/api/books负责在请求时提供给定书籍的标识符、首字母和姓氏,那么非重复规则就被打破了!而且这正是责任概念有趣且应该深入挖掘的地方。我们考虑以下责任分配如何?
/api/authors 服务负责提供关于作者的始终是最新的数据,包括他们的名和姓。这意味着它是作者的真实参考来源:任何需要关于作者最新、最佳信息的人都应该转向这个服务,该服务将负责及时提供这些信息。由于它是这些数据的参考,该服务当然会提供带有价值日期的数据,因为数据可能会随时间变化。例如,作者结婚后可能会更改他们的姓氏;作者服务应该跟踪这一点,因为它负责作者及其数据完整性。
/api/books 服务负责为书籍提供相同的服务,这意味着在书名、标识符等方面有相同的服务水平。但当谈到一本书的作者时,这涉及到另一个参考服务的关系,因此它所负责的服务数据仅仅是指出在另一个服务中的正确实体。这提出了两个有趣的问题。
第一个问题是功能性的:链接应该是简单地指向一个特定的作者,还是应该指向作者在特定时间点的值?这需要回答一些业务规则:如果作者在第一版和第二版之间结婚,书籍上出现的作者名字应该改变吗?如果是这样,如何调整注册的版权?对于书籍原始版本的简单重印,情况是否相同?
第二个问题是更技术性的:如果书籍服务存储了作者服务的链接,而后者在需要时不可用,会发生什么?如果“通常”的数据(名和姓)已经在书籍服务中存储,那么没有问题,因为它现在与第二个服务是独立的。但这又回到了功能问题:如果作者结婚后书籍上的名字不应该改变,那么没问题,甚至更好,因为本地副本将防止在具有检索“旧”数据的日期时难以访问作者服务。如果名字应该演变,可能最好是暂时退回到旧名字,而不是仅仅提供机器可读的标识符;只有编辑者才会知道……
我希望这个例子,尽管复杂,已经向你展示了我们所说的“责任”一词的含义。诚然,这很复杂,但请记住,我们谈论的一切都与商业复杂性相关,并非偶然。确实,谈论价值日期和参考数据管理中历史的重要性可能听起来过于复杂,因为这在当前的信息系统中并不常见。但这确实是一个真实的问题,而且这种对实际功能现实的缺乏反思也是一个问题,因为它阻止了对所有业务规则的反思!这并不意味着基于这种反思构建的软件将考虑到所有这些复杂性。在真正的敏捷方式中,你肯定会从非常简单的东西开始。但这个对功能复杂性的深入理解确保了软件在未来将易于演化,而且你不会在某个实施点上卡住,因为软件与业务不匹配。
开放/封闭原则
开放/封闭原则在其表达方式上本身就包含了一个悖论,这使得它一开始看起来很奇怪:一个模块怎么可能同时是开放的和封闭的?理解这个原则对于创建将不断演化的系统非常重要,因为它指出了什么应该保持封闭,什么应该开放以改变,以便这种演化尽可能顺利地进行。
当应用于面向对象编程时,开放/封闭原则指出,一个类应该对扩展开放,但对修改封闭。封装和私有成员用于防止程序中的任何实例直接修改另一个实例的状态;否则,在执行程序时跟踪发生的事情将非常困难。如果没有办法跟踪哪个类修改了另一个类的状态,那么调试也会变得复杂。这就是为什么一个类保持其成员私有,并且只开放一些公共函数,以允许对其状态的某些更改,这种方式由其自己的代码控制,遵循其自己的规则。这是原则的封闭部分。
但通常情况下,一个类不会被标记为final以允许另一个类从它继承并专门化标记为virtual的功能。继承的类还可以添加一些数据成员,除了访问标记为protected(或者当然,public)的从继承类之外。再次强调,类控制着什么可以被覆盖,什么不能,但至少它对另一个类扩展其行为是开放的。这是原则的开放部分。
将此原则应用于信息系统并不意味着在反思上发生重大变化,因为服务替换了类,扩展或保护的技术仅从实际观点来看有所不同。如果我们继续以服务是 REST API 的例子为例,我们可以在类的成员和 API 合同实现持久化的数据之间建立平行关系:只有服务可以修改这些数据,因为只有实现可以访问持久化使用的(数据库或其他)。当然,一些 API 方法可能允许从 API 客户端传递一些特定的修改,但 API 实现控制这一点,并应用业务规则以确保修改按其意愿进行(或者,顺便说一句,可能被拒绝)。这是原则在信息系统模块中的应用的封闭部分,它在与类应用中的相似性方面非常明显。
在 API 上实现开放原则的部分更为复杂,因为 API 的行为可以通过多种方式扩展:
-
实现这一目标的一种方法是通过创建一个将扩展初始 API 合同的 API。OpenAPI 语法中存在实现多态的机制,也可以以这种方式聚合类型,使得新的类型包含初始类型及其所有内容。
-
另一种方法是创建一个 API,它替换了旧实现的展示,但仍然依赖于所有标准数据,并在此基础上提供自己的数据。如果做得仔细,扩展后的 API 甚至可能与初始 API 合同完全兼容,因为它只添加了新的数据(如果它只是传递初始数据而不改变任何行为,甚至符合我们很快将看到的 Liskov 替换原则)。
-
第三种选择是使用 API 网关来公开更新的合同,并实现来自原始 API 和来自另一个服务的附加数据的混合,该服务专门用于其存储和处理。这种方法在继承原则上更接近。
这三种方法可以概括如下:
图 6.1 – API 的开放/封闭方法
Liskov 替换原则
Display接口将实现一个print函数,该函数在白色纸张上打印黑色文本(通过打印机、屏幕或其他任何东西;这里不重要)。现在假设有一个名为ColorDisplay的类专门化Display类,并再次提出一个新的函数签名print,但接受一个名为color的参数,允许用户指定他们想要的任何颜色。无参数的print函数应该如何表现?继承的类肯定会指向其新的改进后的print函数。在这种情况下,应该将什么默认颜色传递给这个函数?如果你回答“当然是黑色”,你就知道 Liskov 替换原则是关于不让类的用户感到惊讶,并确保他们知道行为是预期的。
对于模块化信息系统中的服务也是如此。再次,我们将使用 API,因为这是在这种系统中分解模块的标准方式。当你得到一个 API 合约时,它声明了可以调用哪些方法,使用哪些动词,以及通过哪个 URL;它还声明了你可以发送或接收的属性的准确名称。但你完全可以尊重 API 合约的字面意思而不尊重其精神,这正是 Liskov 替换原则所涉及的。
让我们将之前面向对象编程的示例翻译成 API 服务,并想象一下,有人可以使用/api/print的 1.0 版本来处理将被发送到设备上的黑色文本。如果使用/api/print API 的 1.1 版本,并且支持/api/print?color=[HEX-VALUE],我们肯定会期望将我们的旧客户端指向新的 API 会导致黑色文本的产生。在 API 合约与其实现之间的流中,这可以描述如下:
图 6.2 – Liskov 替换原则
接口分离原则
Rectangle类实现了IShape接口。这意味着Rectangle必须实现getSurface和getPerimeter方法,还必须实现一些方法,如drawShape等。如果程序中唯一有趣的事情是计算形状的数学特性,那么正确的方法是将IShape拆分为IGeometricalShape和IDrawableShape(即分离接口),以便类只实现它们需要的接口。
对于 API 合约也是如此。回到我们的图书服务,最好将两个合约分开,一个用于图书特性,另一个用于图书销售特性,即使相同的实现会公开两个接口,一个位于/api/books之后,另一个位于/api/books/sales之后,而不是通过仅使用一个合约来强制实现所有函数。
虽然总是可能(并且在 API 中比在类中更可接受)以NotImplemented消息响应,但将接口分为两个也使得引入版本更容易。如果销售接口在 1.0 版本中没有正确定义,并且需要进行重大的、向后兼容的重写,那么将能够在不进行任何更改的情况下继续暴露书籍的特性(可能被大多数客户端使用)。
注意
向后兼容性是 API 新版本的质量,其中所有在先前版本中使用的调用在新版本上工作结果完全相同。
接口隔离甚至使得暴露三个 API 合约成为可能(并且很容易),即/api/books、/api/books/sales和/api/books/sales/v2:
图 6.3 – 接口隔离
虽然在解释中这一点相当明显,但遗憾的是,这个原则在现有的 API 中经常被遗忘。这尤其是因为编辑倾向于在从实现生成 OpenAPI 合约的框架中提供函数,而不是遵循合约优先的方法(虽然这确实更复杂,但这是唯一能够产生正确、与业务对齐的 API 的方法)。由于合约是从整个源代码自动生成的,生成器不会区分不同的方法和资源,产生一个单一、不可分割的合约,该合约不遵守接口隔离原则。
依赖倒置方法
依赖倒置方法之所以被称为如此,是因为它与传统关于依赖的思考方式相反。想象一下,我们有一个报告模块和另一个提供用于报告目的的数据的模块。自然地,我们倾向于认为从功能角度来看,报告模块应该在技术上依赖于数据模块。这是一个罕见的情况,其中将技术设计直接与功能概念对齐并不足够好。如果我们想确保低耦合,我们必须再迈出一步,向两个服务都将依赖的共同接口添加一些间接性:
-
就像
Data类会实现IData接口一样,/api/data服务应该实现数据 OpenAPI 文件中定义的 API 合约。 -
就像
Reporting类会使用IData源(而不是直接使用Data实现)来防止硬耦合一样,/api/reporting服务不会直接调用/api/data服务,而是调用配置提供的 URL,前提是它实现了数据 OpenAPI 合约。在面向对象编程中,这通常通过注入来完成。在服务中,根据编排机制,这可以通过 API 网关、入口暴露或甚至(更复杂的)服务网格来实现。
下面的图已经用于第三章,但它在这里特别相关,因为它直观地展示了信息系统中的依赖倒置原则:不是软件模块之间的依赖,而是两个模块共同工作,指向同一个业务合同定义(纯粹是功能性的),一个来实现它,另一个来消费它:
图 6.4 – 四层耦合图
让我们继续到下一节,我们将分析 SOLID 原则。
对 SOLID 原则的批判性分析
虽然我及时意识到(当然像许多人一样)SOLID 原则几乎适用于信息系统,就像适用于面向对象的类和接口一样,但有些部分值得讨论。确实,对于任何其他原则,严格的、不考虑周全的应用可能会导致问题。即使原则在特定情境中适用得很好,它们也可能有强烈的副作用,最终使它们比有帮助更有害。必要的谨慎方法导致了大量的争论,你必须小心处理这些原则。
职责分离的局限性
第一个(也许是最重要的)原则,即职责分离原则,有时很难应用——在某些情况下可能甚至不可能——应用到服务或应用的高级模块中。对于类来说,一旦编译完成,总是有可能在不影响整个应用的情况下分解一个类。服务和模块并不提供这种易于组合的特性,因为它们附带额外的约束,如暴露、端点、编码接口、文档、集成子系统等。所有这些都对分解产生了影响,这也是为什么那些不谨慎对待微服务并在系统中过度分解它们的人最终花费更多时间在它们的演变上,而不是如果他们坚持使用单体架构的话。我不会再次谈论粒度,因为这个问题已经在之前的章节中讨论过了,但这是显然需要深思的问题,可能会限制职责分离原则的适用范围,或者至少是其深度。
另一个困难与你能将责任分离得多深无关,而是与某些责任有时多么复杂有关,即使在相对较高的层面上也是如此。例如,这是“微前端”架构中的一个巨大困难,我并不是在谈论与前端组件“微”相关的那个问题。简单的事实是,视觉组件不仅呈现功能,还具有视觉影响,这在使它们独立方面是一个巨大的困难。例如,在jonhilton.net/good-blazor-components/中暴露了这一点:通过将 Blazor 组件的内容委托给另一个子组件(在文章的例子中,使用ChildContext属性包含一个Card实例),你确实将显示视觉组件内部部分的责任外部化了。但这并不意味着它不会产生影响,因为子组件的渲染必然会影响其上方的组件。事实上,容器要么为子组件固定一个大小并承担其显示的责任,要么以纯响应式设计的方式让子组件调整其显示。在这种情况下,它自己的大小将受到其子组件的影响,这也破坏了通过责任分离原则本应实现的独立性。
对于这个问题,智力解决方案是考虑每个组件对其内容的负责,但不负责其显示,而这是浏览器中显示引擎的责任。这可能并不令人非常满意,因为它回到了整个前端的单一点执行,这是我们试图使其模块化和易于演进的目标,但至少在有限的范围内实现了组件之间的低耦合。这是在设计信息系统和模块化应用程序时必须做出的许多妥协之一。
关于单体架构的争论
本章可能是讨论关于“回归单体架构”这一长期争论的好地方,这是对在软件架构社区讨论中观察到的微服务缺点的一种反应。总的来说,微服务方法被认为是有害的,有些人建议回归单体应用程序。这遗憾的是又是一个当今极端辩论的例子,因为这两种方法(以及它们之间巨大的范围)都有价值,这取决于你的需求。
没有哪个严肃的人曾经假装微服务是适合所有架构的最佳解决方案。事实上,从一开始,大多数解释这种方法的文章就坚持认为,它们只适用于某些特定的环境(高流量、频繁修改应用程序、负责不同模块的团队之间有明确的界限等等)。然而,有些人没有考虑到这一点,现在抱怨微服务并不适合他们的案例。更糟糕的是,在一种二元且不太深思熟虑的反应中,他们摒弃了微服务的整个原则,并宣传所谓的“回归单体”。在这种情况下,任何理智的工程师都会简单地发现一个众所周知的摆动运动,其中一种极端追逐另一种极端。在这种情况下,解决方案既不在其中任何一个,也不在两者之间不断平衡,而只是在它们之间达到一个美好的平衡。
在软件应用程序的情况下,我们称什么为单体?一个由单个进程组成的程序?不,否则这意味着即使是像cd或exit这样的最小应用程序也应该被视为这样。这个术语正是用来描述那些负责太多业务功能、变得过于沉重以至于无法良好演变或适应其设计用途的单个应用程序。
关于所谓微服务架构的死亡和回归单体的文章中讨论的情况,根本就没有涉及到正确的话题,那就是服务的粒度。当然,微服务的非常小的粒度并没有满足他们的信息系统需求。但这仅仅意味着粒度太细。回到最粗的粒度(单体)只是显示了对于问题的理解深度不足。工程师处理这个问题的方式是寻找服务正确的粒度。正如codeopinion.com/biggest-scam-in-software-dev-best-practices/中所述,选择不仅仅是在“亚马逊在做,所以我们也做”和“我们不是亚马逊,所以我们不应该做”之间——对业务和上下文的分析知识才会告诉你你应该如何在这两者之间定位自己。
恰好存在一种称为领域驱动设计的设计方法,这是找到信息系统服务正确粒度的完美方法。它也与业务/IT 对齐的原则密切相关,因为它说明了业务领域如何被切割成内部紧密耦合、外部低耦合的模块。我们在我们的演示应用程序设计中所做的一切,都将是对这种正确粒度研究的说明,基于业务功能需求。
小心无意中的耦合
最后关于职责分离的一些建议(这个首要原则无疑引发了众多讨论!):一旦你根据业务能力图明确划分了职责,职责分离的工作并未完成,因为在功能的技术转型过程中,很容易重新创建耦合。
我将给出一个此类问题的简单例子,因为它非常典型地代表了我在描述的陷阱。想象一下,我们创建了一个演示应用程序,其中包含一个用于书籍的 API,一个用于书籍销售的 API(可能位于相同的 URL 下,但在合同层面上仍然是独立的),以及一个用于作者的 API。公司总监可能在某个时候想要一份关于作者的地域起源如何影响其销售本地化的报告(也许布列塔尼的作者在其地区销售会更好,因为他们的联系网络更密集?或者可能没有影响?无论如何,这可能值得分析)。这是微服务中常见的问题,微服务本应拥有自己的持久性。在这种情况下,我们如何建立数据之间的联系,就像我们在添加单个数据库时创建join操作一样?一个标准的方法是添加一个收集数据结构,它从所有微服务中收集、索引和汇总数据,并提议一个具有自己 API 合同的专用/api/reporting服务。
当然,这个服务对其源存在一定程度的耦合,但这可以通过保持本地缓存或混合订阅数据变化的方法与较低频率的直接收集相结合来降低耦合级别,以确保没有信号丢失,并且数据在可控的频率下重置。此外,它还提供了原子数据服务中没有的有趣功能,例如索引、动态聚合能力等。GraphQL是一个很好的协议来公开此类服务,并且它们与命令查询职责分离架构集成得非常好。
然而,如果原子服务不仅为这个报告服务提供数据,而且开始从它那里消费原子数据,那么可能会出现意外的耦合。这可能会很快发生,因为索引引擎的性能提升对开发者来说非常有吸引力。问题是这导致了循环功能依赖,这本身就是一个相当大的问题,而且耦合程度更高,因为一旦这种依赖关系建立,原子数据服务就会突然与每个其他服务耦合。当然,耦合可能仍然很低,但无论如何,已经建立了一个联系,这在大多数情况下——我所见到的——都是进入系统的一个真正的痛点。
如果你真的需要在原子服务中进行快速的聚合读取,你必须通过在专门的持久性上添加索引等方式内部处理。如果你看到这个漂亮的索引引擎就在服务墙的另一边,这可能会听起来很复杂,但这是为了保持你的系统不受耦合影响并且易于随时间进化而必须付出的代价。再次强调,这是一个妥协,如果架构师认为功能耦合不是问题,你可以通过这样做来节省时间。但这是一个有意识的(并且有文档记录的)妥协。
图 6.5 – 降低耦合强度
在回到演示之前总结
在本章中,我们学习了可以应用于信息系统(而不仅仅是面向对象编程,因为它们最初的目标是)的 SOLID 原则;单一职责原则和接口隔离原则已被用来开始定义我们演示应用程序所需的不同的 API 合约。开放/封闭原则将帮助保持这个 API 语法的可进化性,并且其进化必须遵循 Liskov 替换原则,以便系统能够令人满意地进化。最后,依赖倒置已被证明是合同优先 API 的核心原则,以及能够将软件实现与面向业务的功能对齐,这是我们在这本书中寻求的主要目标。
在下一章中,我们将进一步设计我们的演示应用程序,通过定义其不同的组件。这将帮助我们描绘出我们想要在这本书中创建的第一版的大致轮廓,同时也能更清晰地定义应用程序的不同部分,使用一种适应分解深度的方法。
第七章:C4 和其他方法
在上一章中,我们提供了一个关于我们将要构建的示例信息系统(以及应用程序软件模块)的商业目标的初步想法(作为一个实际练习来遵循本书中概述的原则)。谈到原则,在介绍演示系统时,我们还解释了面向对象编程中最重要的原则如何应用于我们系统的设计。
现在已经确定了高级原则,是时候深入理解我们的示例信息系统了,我们将利用这一点来展示专业人士使用的一些不同方法。正如我们将看到的,存在许多架构方法和软件表示,它们在许多方面都有重叠。事实上,其中一些非常接近,以至于它们实际上是作者标记他们个人方法的一种方式,而不是为架构工具带来额外的价值。这与 JavaScript 生态系统中的情况相同,那里有太多的框架。此外,每天都会变得更糟,因为每天都有新的团队认为他们可以通过提出另一个解决方案来解决问题。同样的事情也发生在架构方法上,我们最近被清洁架构、六边形架构、洋葱架构等等所淹没。
由于选择一种方法而不是另一种方法实际上并不重要,我们只会快速展示这些方法,以便您可以选择哪种方法更适合您的思维方式,或者简单地从许多方法中混合一些有趣的片段,因为这些只是一些众所周知的最优实践的新组合。在我们的示例信息系统的情况下,C4 方法听起来很有趣,可以提供一个如何设计它的初步方法,因此我将重点关注这种方法,以提供更多细节。C4 架构框架背后的理念是,从上下文级别到容器级别,然后是组件级别,最后是代码级别,关注 IT 系统。我们将随着在示例系统中应用它们来解释这四个级别。
就像在上一章中提到的SOLID 原则一样,我的目标不是深入解释这些方法,因为如果已经在免费和高质量的资源中提供了更详细的解释,那么写作就没有价值了。我的意图是提取这些方法如何有助于达到商业/IT 一致性,或者至少帮助表示与这个主题相关的问题。
在本章中,我们将学习以下主题:
-
C4 方法
-
清洁架构
-
六边形架构
-
洋葱架构
C4 方法
C4 方法涉及设计一个系统(或应用程序),具有四个不同粒度级别,从应用程序所处的上下文级别开始,到将展示应用程序不同过程的容器级别,再到展示组成过程的各个部分的组件层,最后到代码级别,在那里我们将找到用于创建应用程序的类和接口。以下是该方法的示意图:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_07_1.jpg
图 7.1 – C4 方法中的级别
每个级别都详细说明了上一级的内容,并提供了更详细的应用视图。事实上,低级别的单元总是必须与上层的一个给定单元相关联,这也帮助正确地分担责任并保持低耦合度。
在接下来的章节中,我们将为我们的示例系统和软件模块绘制四个级别,以展示方法;同时,我们将开始设计我们的演示,比上一章允许的业务需求更深入。请注意,以下图示不一定是书中最后创建的系统所对应的精确图示;我真的很想展示完整和真实的设计过程,因此我会在设计样本应用程序的同时绘制这些图示。在我对最终结果有一个清晰和完整的看法之后再绘制这些图示——在我看来——不会那么具有教育意义,而且在某种程度上,我会感觉像是在作弊。
上下文级别
在上下文级别上,我们的系统将由之前详细说明的三个用户配置文件使用,即编辑者、销售人员以及 DemoEditor 的总监。我们将考虑用户列表已经存在于信息系统中的 Active Directory 或类似系统中。对于内容管理系统,我们将考虑它已经存在用于二进制文档管理。最后,我们还将考虑在整个系统中也提供了一个负责电子邮件发送的模块。它用于向系统中的参与者发送电子邮件,无论是验证某些数据还是通知他们某些事情。当然,作者也会在上下文中表示,即使他们没有直接使用内部信息系统。以下是上下文级别的示意图:
图 7.2 – 上下文级别图
我们可以对箭头的内容进行更详细的说明,但到目前为止,这个图示是自解释的,不同角色使用的功能在上一章中已经解释过了。我们在这个图示中最感兴趣的中心是Demo Editor System框,这是我们将在 C4 架构的下一级别详细说明的。
容器级别
在容器级别,我们展示了系统在构建块方面的使用情况。大多数情况下,这些块是分离的过程。在我们的案例中,由于我们决定拥有完全独立的服务,我们将拥有与 API 展示服务器一样多的容器。我稍微提前透露一下本书的技术章节,但我们将以 Docker 容器的形式部署系统,因此 C4 架构中的“容器”与 Docker 或其他类似技术中使用的部署单元的“容器”之间有一个完全匹配。
在下面的图中,虚线矩形内的框代表与 Demo Editor 系统对应的更高级别框的详细信息。我们仍然可以表示上下文级别的其他部分,但最佳实践是不详细说明它们,因为它们超出了我们的研究范围。我们可以详细说明流向它们的流(就像我们对用户目录箭头所做的那样,指向单页应用程序),但这不是强制的,如果我们还不想精确地描述这些数据流,我们也可以保留从整个系统指向的箭头(这就是邮件发送者和 CMS 子系统所做的那样)。
图 7.3 – 容器级别
顺便说一下,在这个反思层面,我们开始考虑数据流的内容可能是什么,在这个特定的情况下,我意识到,与其使用 CMS,电子文档管理可能更为合适。如果我们考虑这是一个外部系统,而不是我们正在设计的系统的一部分,那么实际上我们可能没有能力去改变它。然而,由于它与它有交互,我们可能能够影响与其通信所使用的协议,这正是我们的分析所关注的。从实际的角度来看,我在写作的时候还不知道(记住,我希望尽可能现实,我在写章节之前还没有完成练习),是否会有一个 Alfresco 容器、一个 Azure 存储,或者一个 SharePoint 365 网站来实现这个子系统。我所知道的是,我需要传递二进制文档并检索它们,这意味着像内容管理互操作性服务(CMIS)这样的流肯定会被指示(CMIS 是电子文档的互操作性标准)。
目前,我们还没有表达每个框内部的内容。我们只知道每个框都将作为软件应用拥有自己的生命周期,并代表一个过程和一个 Docker 容器(确实是一个好的实践,即每个容器只包含一个进程,以便所有事物都能很好地对齐)。如果我们稍微深入到开发细节中,那么为每个这些容器设置一个持续集成管道,以及逻辑上为每个容器设置一个 git 仓库,将是正确的。但我正在预测这本书的“动手”部分。现在,让我们深入到一个框中,即 书籍存储库,并展示它将如何使用 C4 方法中的第三级,即组件图来组成。
组件级别
在 组件级别,我们展示了可执行容器将使用哪些不同的模块和库来完成其业务需求。由于我们的书籍库将公开两个合同 API,我们需要一个 Web 服务器来支持控制器,一个客户端来调用持久化系统,可能还会与存储库模式一起使用,一个缓存组件来本地保存一些数据,等等。这就是以下图表所展示的内容,对应于 C4 架构的第三级:
图 7.4 – 组件级别
一个组件可能对应于不同平台上的不同工件名称。在 .NET 中,组件可能是程序集,而在 Java 中,它们将是 JAR 存档。无论如何,这种组件分解在基本上所有编程平台中都存在,即使它们以不同的形式出现。例如,在某些平台上,组件将对应于单独的文件,而在其他平台上,它们将更加抽象,例如通过使用命名空间。无论如何,都存在将代码级别内容分组为连贯组的方法,同时仍然比更高层级的容器具有更低的粒度,这些容器对应于由多个组件组成的整个过程。
代码级别
正如 C4 方法(可在 c4model.com/ 获取)的参考文档所述,只有在它们能增加价值的情况下才应使用图表。这也是我在介绍 IDbRepository 模式将被使用,也许还会使用 UnitOfWork 模式时强调的,但类组织结构尚不明确,因此现在尝试绘制图表是没有意义的。
此外,即使是为了教学目的,绘制这样的图表也不会带来太多价值,因为 C4 方法的第四级实际上与UML或统一建模语言中的类图相同。在接下来的章节中,如果我们需要,我们将使用这些详细图表,一旦我们进入实际制作系统的阶段。与此同时,我们已经完成了用于解释我们想要创建的示例系统的 C4 方法,现在我们将简要讨论其他软件架构设计方法,使用它们更好地表达我们正在分析的环境。
清洁架构、六边形架构和其他框架
在软件架构的背景下,接下来可能对你来说是个惊喜——选择哪种架构方法并不那么重要,只要你有一个方法(或者几个方法一起使用,顺便说一句,如果你能控制它们之间的交互并知道何时选择一个而不是另一个)。当然,这并不意味着在设计信息系统架构时你不需要应用某种方法。我在这个领域多年经验后的观点是,你应该找到并应用你自己的方法。
此外,如果你尝试了许多不同的现有方法,如六边形架构、清洁架构、洋葱架构以及其他以领域为中心的方法,你很快就会意识到,它们提供的不过是将相同的基本原则以不同的视觉方式呈现,这些基本原则与我们在这本书开头讨论的业务/IT 对齐以及从 SOLID 方法中提取的原则高度相关,即以下内容:
-
商业模式(代表功能概念的数据结构和转换成代码的业务规则**)应该是架构的核心。这样,它不依赖于任何东西,这允许轻松更改、快速自动化测试、缺乏外部版本控制约束,以及许多其他优势。最重要的是,它使团队能够专注于最重要的事情——业务对齐。
-
围绕这个业务核心的一切都应该使用某种间接方法来引入低耦合,使系统的任何模块的演进都更容易。一切皆依赖于业务核心模块,而它本身不依赖于任何其他东西。此外,如果需要,这些依赖关系很容易修改。
技术架构模式
在我们深入探讨方法细节之前,还有最后一件事——在这里,我将只讨论可以应用于整个信息系统设计的架构方法。有许多方法涉及单个应用程序的技术架构,它应该如何结构化,其源代码和信息流应该如何组织,应该存在哪些技术层,甚至在某些情况下,如何命名事物的建议。
例如,N 层架构(也称为基于层的架构)将描述每一组源代码应该如何调用另一组,从 GUI 到服务再到应用程序再到数据库。MVC、MVP 和 MVVM 架构稍微不那么线性,描述了每一组源代码将只与其中的一些进行通信,以简化应用程序的演变和维护(通常侧重于它们的视觉部分)。
这些架构理论上可以在整个信息系统的层面上使用,但这并不意味着更多,因为它们并没有说很多关于应用程序应该如何协同工作。N 层架构建议软件块应该通过应用层相互通信,但由于它没有强制执行,许多旧应用程序在数据库级别进行互操作,这在大多数情况下都是灾难性的耦合,也是当今绝大多数遗留系统无法演变的主要原因之一。MVC 应该适用于比仅仅视觉界面更高的层面,但尽管如此,它并没有达到整个信息系统的层面。这可能只是一个语义问题,但我个人倾向于将这些方法归类为模式而不是架构,因为它们来自经验而不是纯粹的反思(这里绝对没有价值判断——我们需要实验和智力方法来完整)。
由于这本书是关于整个信息系统架构的,我专注于适用于这种级别的架构方法的目的。恰好领域中心的方法通常对此作出回应,因为它们传达了业务领域的概念,如果你想要得到与业务一致的结果,这必须是第一个(也是唯一的)分解信息系统的途径。所有技术方法都可以在软件和硬件层上工作,但我们需要的是在业务能力映射层上工作的方法。
洋葱架构
洋葱架构将软件单元描绘为几个同心圆,中心正是业务领域模型!围绕它,你会找到服务(持久化等)然后是展示层(GUI、外部接口、测试等):
图 7.5 – 洋葱架构
在洋葱架构中,一条主要规则是所有依赖都应该从外侧流向内侧层。这样,核心业务模型就不依赖于任何东西,这使得它容易进化,而外侧圈可以改变,前提是它们不影响业务(这通常比反过来更容易 – 想象一下,如果你的整个业务模型基于专有关系型数据库过程语言,并试图切换到另一个数据库引擎)。
虽然这种方法不坚持间接层的级别,但依赖反转原则(见前一章的解释)以及刚刚提到的规则使得接口对于实现持久化等功能是必要的。当然,对于更高一层也是如此。
六边形架构
六边形架构代表一个以六边形形状的软件单元,因此得名,其中六边形的左侧包含消耗内部(如 GUI 或自动化测试)的接口,右侧包含适配器以处理功能依赖(如持久化、通知服务等),而中间部分,正如你可能猜到的,是核心,包含业务实体和业务规则!
图 7.6 – 六边形架构
六边形架构强调端口和适配器来组织应用程序不同部分之间的通信,但这也是洋葱架构的一条规则,即使它没有以相同的方式展示。这有助于我们记住,接口和合同应该始终应用于依赖,以防止它们演变成硬耦合,这使得系统难以进化。
就“六边形”形状而言,这只是纯粹的杂乱!我的意图是画出业务域的三个“客户端”来强调方法名称与给定的技术约束或技术优势之间绝对没有关系。实际上,方法中解释说,六边形形状只是用来保留足够的空间来显示围绕中心形状的形状。这与圆圈(如之前提到的其他方法所用的)、椭圆形或正方形有何不同?如果六边形形状在概念上没有带来任何丰富性,那么我个人认为,它不应该出现在方法名称中。
这可能听起来像是对方法的无用抱怨,但这只是为了明确,它们的真正价值在于它们共同拥有的东西,而不是它们的差异。真正重要的是核心域的中心性,与业务对齐,以及依赖的控制,但所有这些已经在 SOLID 原则中存在。
清洁架构
清洁架构基本上是将之前提出的两种方法结合起来,同时提出一些额外的规则。从图形上看,它非常类似于洋葱架构,以同心圆的形式呈现,业务实体位于中间,围绕核心组织了多层,按照依赖关系应用的方向从内向外发展。
在这种方法中,你将找到之前在另外两种方法中几乎已经解释过的所有内容,包括领域模型必须摆脱任何技术依赖性,以解决其在系统中的极端重要性,并带来各种其他好处,如可测试性。
依赖反转也是推荐的,因为它与关注点分离相似。词汇略有不同,但通过在互联网上快速搜索,你会看到很多关于三种架构之间极端相似性的博客文章。清洁架构可能比其他架构更“指导”,但在写作时,不可能找到证明其中一种比另一种更好,甚至在结果上广泛不同的文档分析。
我个人的观点是,这三种方法(洋葱、六边形和清洁架构)在它们的工作方式和推荐方面非常接近,你可以使用其中一种而不影响产生的架构质量。
能够带来质量的是对这些方法存在的原因的深刻理解,特别是创建干净分离的模块并保持对依赖关系的控制的能力。没有对它们之间关系的严格规则来切割关注点和责任,最终会导致像意大利面一样的系统。
相反,遵循严格的原则,其中业务领域(最重要的部分)没有任何依赖,并且所有技术都是围绕它接口的,中间有一个间接层,这才是真正重要的。而且三种方法都有这个共同点。其余的都是纯粹细节,如果你考虑,例如,六边形形状并没有给方法带来任何东西,就像之前解释的那样,那么存在多种方法确实给人一种印象,即其中一些方法的存在仅仅是因为它们的作者想要有自己的方法。
摘要
在本章中,我们使用了 C4 方法从四个角度详细介绍了我们的演示应用程序,即它所使用的上下文、它所使用的容器以及它由哪些组件和代码组成。遵循此方法绘制的图表帮助我们解释了我们将要创建的示例信息系统的内容。我们还了解到,没有必要创建一个完全覆盖研究领域的图表,但只有这些图表具有价值。
此外,在过去几十年中涌现了许多架构方法。尽管它们在软件应用程序的设计中,以及信息系统设计中显然具有价值,但它们在某种意义上非常相似,即最近的方法大多是领域中心的;因此,它们的价值基本上可以总结为我们已经知道的两个原则,即 SOLID 原则和业务对齐原则,即首先考虑业务功能模型,并应用一种方法来减少对依赖的耦合。这两者都使得系统的演化变得更加容易。我毫不怀疑这些方法可能对软件组织具有额外的价值,并且它们确实可以帮助你超越纯粹技术导向的架构模式。然而,就整个系统而言,我认为在第章开头探讨的 C4 方法应该首先以自上而下的方式应用,将领域中心的方法作为组织 C4 方法的第三级(组件)的一种方式。
在第八章中,我们将开始更精确地定义我们的演示系统所基于的 API,因为这些实体无疑是实现自由发展的应用程序最重要的技术方面。在接下来的章节中,我们将首先解释服务导向的概念及其与 API 的关系。在第九章中,我们将采用基于语义的方法来定义我们的示例应用程序的业务领域,并将它们转化为实际的 API 合约。我们已经对业务领域进行了很多讨论——在接下来的两章中,我们将定义这些领域,以及它们在我们演示系统中的交互。
第八章:服务导向和 API
现在我们已经解释了许多原则和几种方法,我们将继续进入一些技术性更强的章节,这些章节将展示更多应用到我们的演示应用程序中的示例。在本章中,我们将从 IT 的角度解释“服务”的概念,并尝试将服务置于 IT 历史中,以便您更好地理解它们的目的以及它们为行业带来了什么。当然,仍然存在一些不足,但面向 Web 的架构和一般意义上的 Web 服务,当它们被正确设计时(遗憾的是,这远非普遍现象),为软件行业带来了巨大的价值。
在对服务的历史进行了考察之后,我们将详细阐述良好服务导向架构的特征(我之所以不使用“服务导向架构”这个表达,是有确切原因的,您很快就会明白),并解释它们当前的演变,即 REST API,如何对许多软件系统有益。
最后,我们将展示上一章中提到的架构模式如何应用于演示应用程序的服务定义。为此,我们当然会使用 REST API,因为它们是现代 IT 系统架构方法的核心理念。我们将回到在第二章中广泛讨论的标准概念,并解释哪些标准可以用于我们的演示系统。最后,我们将解释当没有标准存在或适用时我们可以做什么,这将成为下一章的过渡点。
在本章中,我们将涵盖以下内容:
-
查看服务导向的历史
-
服务的特征
-
应用到我们的演示系统中
查看服务导向的历史
首先,让我们从一点历史开始。这可能是因为我是一名老程序员,过去 37 年中一直在编程,其中 25 年在工业环境中,但我认为了解我们从哪里来总是很有趣,因为这解释了今天许多技术被创造的原因,以及它们仍然缺少什么。这样,我们不仅能够预见某些技术和软件实体的不足,还可以避免不充分利用它们的风险,正如它们的创造者所期望的。回顾技术的历史还有另一个优点:在沿着这条道路漫步时,你可能会偶然发现一个古老但仍然有趣的技术,它可能比新出现的技术更适合你的环境。这种情况并不经常发生,但当它发生时,如果你可以用一个经过实战考验的、比目前普遍使用的工具更简单的技术来解决你的 IT 问题,这可以在维护时间和性能上为你提供巨大的提升。例如,基于文件的互操作性可能对每天使用 Web API 的人来说显得可笑,但在特定情况下,异步操作更好、安全性不是问题、进程的独立性是一个优势、避免部署 Web 服务器可以节省时间的情况下,它们可以是一个完美的解决方案。
因此,让我们从互操作性技术开始这段旅程,并且特别开始于为什么我们需要它们。
长期期待的可重用性
使软件实体的两个部分相互互操作是一个几乎与编程本身一样古老的概念,因为它与可重用性相关。为了使一个常用函数不需要被输入两次,需要有一种方法将其从其他不同的代码部分中分离出来,并且以某种方式使其可以被这些代码片段调用。这可以很容易地如图图 8.1所示进行图解:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_01.jpg
图 8.1 – 重复使用通用代码
代码重复是一个问题(尽管“不要重复自己”原则可能有其自身的不足,而且每个编程选择总是需要妥协),因此将一些代码放在公共部分通常是一种有价值的方向。有众多方法可以组织这一点,而且可重用性就像 IT 领域的格拉尔一样,多年来一直被追求,甚至几十年。
程序和避免额外打纸卡
首次尝试实现可重用性来自一个你们大多数人甚至都不太可能了解的时代,那时程序以穿孔纸卡的形式存在,这些纸卡上打有孔洞,为计算机提供指令。这实际上源于雅各德织机机制,在这些纸卡被用来控制半自动化机器中的布线,以在最终布料上形成特定的图案和编织图案。通过重复使用相同的卡片,可重复性已经实现,但在给定卡片上重复图案是通过多次以完全相同的方式打孔来完成的,这导致了一个漫长的手动过程,并涉及到出错的风险。此外,每个应用程序,由按精确顺序排列的带有穿孔纸卡的盒子组成,必须包含每一条单独的指令。由于卡片上可以容纳的指令数量是固定的,因此卡片在另一个堆栈中再次使用实际上是不可能的。即使可能,提取正确的卡片,使用它,然后将其放回堆栈的正确位置,对于涉及的两个应用程序来说都太危险了,尤其是关于复制纸卡,即使这是一个手动操作。
然后出现了使用例程并将代码发送回程序中给定地址以使其重复部分指令的想法。与臭名昭著但仍然值得尊敬的GOTO指令相关的概念诞生了,它为后续的程序节省了许多指令。但有一个问题:例程只能在单个程序内部使用。诚然,它有助于减少它们的大小。然而,当创建一个新的程序时,仍然需要输入相同的代码——我们谈论的是一个复制粘贴根本不存在的时代。因此,需要更好的东西。这就是我们将在接下来的章节中要探讨的内容。
库和程序间共享指令的能力
重复使用的下一个发展阶段是.dll文件的概念,或者说是.jar存档中的 Java 模块,它们仍然是重复使用的基石,当像微软的基类库这样的库成为所有.NET 程序员的证据时,通用性就不远了。对于这个最初在微软严格控制下开始,并逐渐发展到开源可用性的特定框架,历史是一份祝福,因为许多库只是一次性实现。另一方面,Java 最初是一个更开放的平台,但在 Oracle 收购 Sun 之后,它逐渐变得更加封闭。尽管事情开始有所统一(以牺牲更慢的进化为代价),Java 生态系统中仍然存在许多做同样事情的库。我记得在我使用.NET 五年后的第一次 Java 专业开发中感到震惊,因为没有一个 XML 解析库,而是有许多,每个库在某一件事上都是最好的:Xerces 擅长流分析;Xalan 被认为是加载 XML DOM 最快;有些其他库在处理 DTD 模式验证方面做得更好;等等。对于习惯于只有System.Xml的人来说,这真是一个巨大的惊喜和巨大的失望,因为它使得学习曲线突然比平台和语言的亲近性预期的陡峭得多。
无论如何,库无疑是编程平台中重复使用最普遍的方法,并且几乎存在于所有现代语言中,无论是 JavaScript、Python、C 还是 C++等等。尽管库也有它们的困难,不仅在于版本控制和向前兼容性领域,还在于复制文件时的便捷性,有时这会导致代码库中出现多个副本(这与重复使用的初始目标相悖),但它们仍然是必要时首选的方法。当然,就互操作性而言,它们有明显的不足,因为它们只能在它们自己的执行平台上使用:尽管可能存在桥梁,但 Java 库只能由 Java 程序使用,.NET 程序集只能由另一个.NET 程序集调用,等等。这是一个长期困扰 IT 工程师的问题,也是许多解决方案出现的地方。
尝试实现通用互操作性
解决上述限制的主要方法是创建包含编译后代码的库,这样机器代码就可以从任何调用者处使用,无论创建调用程序的编程语言是什么,只要它也以机器可读代码编译。那里的困难主要在于技术层面:调用这样的库并不像使用函数名和属性那样简单。此外,由于编译平台的原因,仍然存在限制。确实没有在 Linux 操作系统内执行 Windows 库或反之亦然的方法。
微软实际上通过引入由操作系统控制的组件的概念,在这个方向上尝试走得更远。这些可重用单元不是直接作为文件提供,而是作为操作系统本身所知的实体:它们的实际形式仍然是文件,具有.dll扩展名,但一旦注册,Windows 就会使函数对任何程序可用,即使它没有访问原始文件。除了作为应用程序定制的存储库之外,注册表还存储了组件对象模型(COM)所需的信息;实际上,它甚至从 Windows 3.11 开始就主要为了这个用途而开始其职业生涯。与 COM 一起,微软的一项名为动态数据交换(Dynamic Data Exchange)的技术允许应用程序组件相互插入。这就是你今天在 Word 文档中打开 Excel 工作表时看到菜单适应的原因。
在 COM 之后,出现了 COM+等扩展,然后是分布式 COM(DCOM),这是试图突破本地计算机的边界并引入组件的远程执行。这些并没有像这个组件领域最新创新——ActiveX——那样成功。ActiveX 是一种基于 COM 的技术,旨在使将图形组件集成到应用程序中变得更容易,而不仅仅是函数。甚至可以通过在浏览器中交付它们来将这些组件嵌入到 Web 应用程序中。在浏览器安全没有像今天这样扩展的时候,它提供了许多有趣的功能,但现在这项技术已经过时。
存在其他用于分布式组件的技术,例如企业 JavaBeans 和所有使用 CORBA 的平台,但它们与 DCOM 相同的局限性,并且没有展现出最初承诺的低耦合水平。版本控制留给了平台维护者,没有能力与表示层建立关系,以及其他不足之处使得这些技术如今纯粹是遗留技术,未来有限。
事实上,以组件形式实现的互操作性可能过于谦逊,以至于无法真正达到 ASCII、Unicode、HTTP 等广泛使用的技术如持久技术状态。组件最初是在单台机器的范围内开始的,并且从未找到一种方法走出这个范围。为了迈出下一步,需要一个完全通用的方法,这涉及到将互操作性和重用带到整个计算机网络中——而且为了值得这样做,这必须在所有网络中最大的网络,即互联网中。
尝试使用 Web 标准实现通用性
我们互操作历史的下一个里程碑涉及 Web 服务,在术语的一般接受意义上,意味着通过 Web 标准提供服务。由于 HTTP、TCP/IP、Unicode 和 XML 等基础已经可用,并提供了这样一个通用互操作技术所需的大部分基础,因此网络是进行这一步的明显方式。
第一次尝试实现 Web 服务(或者我们可以称之为互联网上的可重用函数)使用了诸如简单对象访问协议(SOAP)和Web 服务描述语言(WSDL)等技术,由于这些标准在领域内是独一无二的,它们简单地预占了web service这个术语,这成为了 SOAP 和 WSDL 兼容的表述所接受的行话。SOAP 旨在标准化通过 HTTP 请求和响应的 XML 内容,使它们看起来像函数调用,包括一个信封、具有类型的属性、可能的元数据等等。WSDL 是用来表达相关合同的标准,简而言之,是应该在 SOAP 消息中使用的语法。还有额外的标准,例如通用描述、发现和集成(UDDI),例如,但这些未能实现其目标,并迅速衰落。许多所谓的*WS-*标准也是如此——WS-Authentication、WS-Routing 以及添加到 Web 服务语法的其他语法,旨在允许补充功能。
这些解决方案在 2000 年代在行业中获得了很大的动力,并在 2010 年代初达到了顶峰。实际上,它们产生了一个整个架构,被称为面向服务的架构(SOA)。SOA 本应是一个通用术语,但它已经与特定的架构和软件相关联。此外,软件制造商大量投资于SOA 工具,使公司相信,中央中间件是他们实现互操作性的全部所需,而了解互操作性的专家知道,这仅仅是交易的一部分,语义和功能互操作性实际上比技术互操作性更关键、更复杂,而技术互操作性通常是过程中的最后一公里。
当然,这导致了对 SOA 的大量批评,2010 年代中期,许多文章宣布了 SOA 的死亡及其未能实现目标。与此同时,在行业中它的传播仍然非常广泛,许多技术因为 SOA 而得到了新生。
中间件中的步骤
特别是中间件应用在 SOA 架构中被推高,即使它们不是基于 SOAP 和 WSDL,也能适应它。企业应用集成(EAI)是一个古老的梦想,并假设集中的适配器使得系统中的许多应用程序能够相互通信,EAI 平台将每个消息从一种格式转换为目标格式。当然,其集中化的方面是一个单点故障(SPoF),相当大的缺点。如果你再加上每次任何应用程序更改其版本时都需要更新的 EAI 砖块,那么这些定制系统从未达到成熟也就不足为奇了。
提取、转换、加载(ETL)是一组数据处理工具,但它们可以被归类为中间件应用,尤其是由于在常见的信息系统中的应用程序之间有很多互操作性流实际上是纯粹的数据传输,而不是业务功能调用。当然,这是一种粗略的中间件,但数据质量比工具的复杂性更重要,一个良好的 ETL 可以在结构化信息流方面走得很远。然而,ETL 并不完全适应数字化转型,而且很容易失去对它们的控制。我咨询的一家公司有一个非常混乱的 ETL 作业系统,每晚有超过一千个作业启动,整个系统需要一个专门的工具来精确地编排这些作业,以便在早上结束时有干净的数据。随着新作业的不断添加,在低活动期间时间开始变得稀缺,在添加更多服务器功率和并行化可以并行化的内容之后,整个系统只有在办公室开门后才能完成工作。这当然成为一个重要的问题,必须通过激进的决定来解决。更糟糕的是,作业之间相互依赖且脆弱,以至于没有一个夜晚所有作业都通过,在操作期间需要运行一些纠正作业,或者对于风险最高的作业,简单地等待下一个夜晚,希望得到干净的数据。出于信息目的(并且希望它也能起到警告的作用,因为作业的数量使得图表几乎无法阅读),以下图表显示了作业的按时间顺序执行的图形。这个图表旨在概述复杂的作业;文本可读性不是目的。
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_2.jpg
图 8.2 – 复杂作业编排
**消息导向中间件(MOMs)**已经存在了一段时间,但得益于 SOA 和 AMQP、ActiveMQ、MSMQ 和 RabbitMQ 的引入,它们在消息传递的可靠性方面获得了市场关注度。即使在今天不再使用 SOAP Web 服务的架构中,MOM 仍然有助于确保系统内特别重要消息的全面传输功能。一些 MOM 支持者认为所有消息都应该通过中间件传递,防止应用程序直接相互通信,但这会影响性能,我们将展示消息的功能标准允许移除中介层。
就中介层而言,消息导向中间件(MOMs)从霍普和沃尔夫(www.enterpriseintegrationpatterns.com/)定义的标准消息操作方式中受益良多,这种方式被称为企业集成模式(EIP)。EIP 定义了一些处理软件消息的标准模块,例如多路复用、基于内容的路由器、丰富化等。通过组合这些基本的消息转换或路由模块,MOM 能够处理几乎所有可能的功能场景。Apache Camel 是参考开源的 EIP 实现,并被广泛应用于许多中间件中。术语砖块特别适用于这些模式,因为它们可以用实际的、具体的乐高™砖块来解释:我经常使用这些来直观地解释软件系统架构的概念,特别是如何通过引入具有可组合动作的中介层,以最小的冲击使系统架构演变,每个动作都由简单的技术乐高™砖块组装处理,如图8**.3所示。3*:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_3.jpg
Figure 8.3 – 使用乐高™砖块模拟的企业集成模式
企业服务总线(ESB)是 MOM 和 SOA 的自然演变,与互联网的原则相冲突。ESB 将我们讨论过的所有技术集成到一个不再有集中的系统中:网络(在 TCP/UDP 中)是唯一保持中心化的东西,其适应交付的能力被用来提高对节点故障的鲁棒性。同时,使用存储和转发模式来确保消息几乎永远不会丢失,因为它们被持久化,并且只有在下一个目的地确认它们在其控制下已持久化后才会被删除。ESBs 几乎拥有实现互联网规模系统互操作完整功能目标所需的一切。但仍然,它们失败了,或者至少没有像预期的那样成功,因为这是解决 IT 行业如此重要问题的理想解决方案。事实上,ESBs 添加了所有必要的功能,但这正是它们的厄运。由于它们可以做到一切,它们庞大、复杂的机器需要大量的专业知识来运行和维护。
最新的演变——REST API
然后出现了 REST,这是一种创建基于 Web 的 API 的更轻量级的方式,这再次彻底改变了生态系统。**表示性状态转移(REST)**在 2000 年之前就已经被定义,但在 2010 年代初才真正成名。到了 2020 年代,尽管遗留软件的部分很大,SOAP Web 服务仍在被利用,但没有任何新项目是基于这些旧技术开始的,几乎每个新的 API 项目都在使用 REST,或者至少采用了某种降级的、并非真正“RESTful”的方法。
简而言之,REST 是关于回归 HTTP 的基本机制,以允许在 Web 上进行函数调用。例如,与 SOAP 将操作代码发送到信封中不同,REST 使用 HTTP 动词,如GET、POST、PUT、PATCH和DELETE来指示服务器应该做什么。它不是发送函数调用,而是像 HTTP 处理更知名的 HTML 页面或通过 Web 提供的图像一样处理资源;只是这些资源在功能上是有方向的,比如客户或合同。这些业务实体每个都有 URL,就像网页或资源一样。它们的表示可以是 HTML,但更适合 XML 或 JSON,后者比其前身 XML 更轻量。超媒体、格式协商和头部也被用于等效的互操作功能。授权简单地留给任何 HTTP 调用中的等效功能,使用基本身份验证、Bearer 令牌等。简而言之,REST 将基于 Web 的互操作简化到了极致,消除了每一丝冗余,以专注于现有标准的纯粹和完整使用。实际上,REST 并不需要除 HTTP、JSON 或 XML、Unicode 等现有标准之外的其他任何东西。因此,它更多的是一种实践,而不是一种新的协议。
而且它确实有效……它实际上运作得如此之好,以至于互联网上的评论者毫不犹豫地谈论 SOA 2.0,甚至正确的 SOA。有些人引入了新的架构术语,如面向 Web 的应用,以将这种方法与原始 SOA 区分开来。REST 成功的最好证明是,没有编辑利用其名声来尝试强制实施专有实现:REST 之所以运作良好,是因为它没有添加任何东西,而是将任何软件层减少到零,因为一切都已经存在,工程师只需按照其预期的方式使用它即可。
我们缺少什么才能达到实际的服务重用性
这就是我们在撰写本书时的现状,毫无疑问,情况将继续发展,但我们已经达到了一个点,即基于实际的基于 Web 的互操作性,包括两个独立实体之间的互操作性,对于许多公司来说已经成为日常现实,这本身就是一个巨大的胜利。当然,我们总是可以更进一步,但主要道路已经铺好,现在剩下的任务主要是传播这种互操作方式的好做法,而不是想象一种克服任何当前缺点的新方法。
事实上,大部分剩余的问题都是由于缺乏功能数据交换的公认格式,导致存在中介连接器。如果我们想要达到一个理想的地方,在那里全球通用的互操作性不再是问题,而是一个过去的问题,我们就需要为每个数据流拥有一个无可争议的标准。这当然是不可能的,而且我们离这样的满意状态还非常遥远,但一些精确的、非常普遍的、技术上容易的交换形式目前已被涵盖。例如,认证和识别现在由 OpenID Connect、SAML、JSON Web Tokens、SCIM 以及一些其他规范很好地实现了。当然,有很多遗留软件甚至专家工程师没有使用这些,但总体趋势是它们是未来的方向,全球各地都接受这一点,并朝着这些规范努力,这些规范将成为未来的便利标准,就像 ASCII 和 Unicode 对于文本的二进制表示一样。还有一些其他领域被涵盖,或者至少有很好的、功能齐全的规范可以解决这个问题,例如 CMIS 用于电子文档交换或 BPMN 2.0 用于工作流建模。
但绝大多数的交换都没有被一个无可争议的标准所覆盖,大量的连接器仍在开发中,以建立应用程序之间的对应关系。这在当今全球 IT 领域是一个巨大的资源浪费,因为这些中介连接器并没有为顾客和最终用户增加任何额外的价值。但现实是,制定一个标准需要花费大量的时间,正如我们在第二章中看到的。尽管如此,让我们尝试关注积极的一面:现在这个运动正在活跃,情况每年都在改善,有一个强大的互操作性基础,其中技术问题现在已经得到解决。只剩下语义和功能互操作性需要处理。这将是下一章的主题,但在讨论这一点之前,我们需要回到“服务”这个概念本身,并解释一个好的服务应该如何定义。然后我们将使用这些原则来制定我们演示系统的第一个服务,使用上一章中展示的架构原则,并将它们应用于之前展示的演示应用程序,并在本书的其余部分对其进行更详细的开发。
服务的特征
服务是一个如此模糊的术语,以至于需要一个完整的章节来给出一个良好的概念理解——而不是一个单一的定义。
如同服务所解释的
“作为服务”这个表达在许多公式中被使用:SaaS代表软件即服务,PaaS代表平台即服务,CaaS代表容器即服务,等等。你有没有考虑过为什么如此不同的事物使用这个共同的名称?这本身可能就是对服务最好的定义:从其他事物中受益,而不必处理通常与之相关的外部性。酒店房间是一种服务,因为你可以享受到床和屋顶的好处,而不需要购买和维护一栋房子,甚至不需要打扫房间。SaaS 是一种服务,因为你可以使用软件(操作其界面、存储数据并检索它、实现复杂的计算、导出结果),而不需要安装软件、购买长期许可证、操作它、安装新版本等等。IaaS 是一种服务,因为它提供了你对基础设施的预期(CPU 功率、RAM、I/O、存储、网络带宽和使用),而不需要你担心购买服务器的硬件方面、操作它们、租用一些房间、整理电力和冷却、物理上保护它们、在出现故障时更新硬件等等。
解释作为服务的表达式是必要的,因为“服务”这个词本身非常通用,人们可能会有些困惑,为什么我们谈论面向服务架构,然后是 SOAP 网络服务的概念,然后是网络环境中的服务,等等。当我们在这本书中谈论服务时,我们真正指的是作为软件功能向用户提供的服务,而用户无需关注其实现:用户不需要知道使用的是哪个平台,服务器在哪里等等。他们只需要知道尽可能少的信息,即一个 URL 和定义交换语法的合同,以便与该服务进行互操作。
这让你想起了什么吗?仅仅依赖于某物的功能定义,而不考虑任何与软件相关的约束?这正是书中已经提到过的内容,特别是我们讨论了四层 CIGREF 图模型的部分:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_4.jpg
图 8.4 – 使用 CIGREF 图解耦
当谈论提供作为服务的功能时,可以将其视为从第二层(业务能力图)获得某些内容,而无需担心它在第三层和第四层(技术层)如何实现。
完全去除中间件
作为服务的方法的一个优点是它允许我们完全去除中间件。实际上,我们真正想避免的是直接、点对点的互操作,这会导致大量的耦合,如下面的图所示:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_05.jpg
图 8.5 – 点对点互操作
但中间件,虽然引入了一个间接层,却带来了两个问题。第一个问题是它引入了额外的软件复杂性,这可能很难维护。第二个问题是我们仍然处于 CIGREF 图的软件层,这意味着,如果没有进行标准化(没有标准化消息),我们可能会遇到两步耦合而不是简化它!以下方案表达了这种潜在的危险:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_06.jpg
图 8.6 – 通过中间件进行互操作
企业服务总线(ESB)通常被提出作为避免集中实体的解决方案,但它们实际工作的方式仍然意味着存在(尽管是分布式的)可能导致耦合的软件代理:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_07.jpg
图 8.7 – 与企业服务总线进行互操作
避免这种耦合的一种方法是从功能角度标准化消息:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_08.jpg
图 8.8 – 与标准化解耦功能进行互操作
但如果我们达到了这样一个状态,即已经创建了一个功能标准,那么中间件实际上不再需要映射数据或转换任何格式,因为f和f'函数实际上是相同的(否则它们不会被包含在单一的数据流中)。中间件唯一的功能仍然是路由、认证以及一些可以通过 HTTP 简单实现的功能,而不需要任何中间件。因此,中间件简单地消失了,我们达到了之前所表达的理想情况:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_09.jpg
图 8.9 – 间接解耦原理
在这里,唯一剩下的困难是一个功能性的,即描述与业务相关的需求。诚然,这可能是一件非常困难的事情,但主要区别在于这是我们需要在任何情况下克服的内在复杂性(否则软件将无法正确工作),而不是偶然的技术复杂性,它进入我们的设计阶段并增加了版本控制、维护等问题。这是解耦的本质,也是使系统更容易演化的关键。
再次强调,尽管我们应该尽可能努力实现这一点,并且这确实是一种创建低耦合互操作性的方法,但这种交互并不总是容易实现。MOM 和其他中间件系统在不久的将来不会退役,因为它们仍然是互操作复杂消息、应用调解以及在无法在信息系统中对消息进行完全标准化时确保交付鲁棒性的好选择。
外部互操作性最终成为现实
所有这些都可能听起来有点理论化,但正是这种方法使我们最终达到了这样一个阶段,即前面图示中的软件A和软件B之间的互操作性(图 8.5至8.9)不再依赖于中间件或其他阻碍其复杂化的工件。展示这一点最好的方式是提供一些实际例子。
在我之前工作的一家公司中,两个客户(一个区域委员会和一个城镇)希望以这种方式进行互操作,即当区域委员会将其列表中的关联添加时,城市会自动接收到信息并将其存储在其自己的数据库中,前提是它是注册的城市。这种方式需要一些重要的前期工作,这是我的雇主完成的,即定义法国协会的标准格式。由于我们对主题很了解,这仅花了几天时间,我们就将此格式提交给法国政府,以便在他们的开源 forge 中发布,因为他们没有为这个现有的标准。这个格式是两个客户之间的功能合同。他们同意,无论他们可能对其软件进行何种更改,协会 JSON 的内容始终如下(此提取高度简化并翻译成英语以提高可读性):
{
"name": "Old-time developers of Brittany",
"registrationNumber": "FR-56-973854763",
"organizationType": "uri:ORGANIZATIONS:ASSOCIATIONS",
"creationDate": "2019-01-04T12:00:00Z",
"representatives": [
{
"role": "accountant",
"lastName": "Gouigoux",
"firstName": "JP"
}
],
"legalAddress" : {
"streetNumber": 282,
"cityName": "Saint-Nazaire",
"zipCode": "44600"
}
}
事实上,区域委员会在这个项目之前已经是客户,所以他们已经在使用基于此格式的我们道德人参考软件。因此,在这一方面,我们只需要定制事件管理系统,以便在发生创建、修改或删除协会的事件时调用第二个客户的回调地址。这是通过以下语法完成的:
{
"webhooks": [
{
"topic": "POST+*/api/organizations",
"callback": "https://saint-nazaire.fr/referentiel_associations/modules/index.php?refOrga={registrationNumber},
"method": "PUT",
"filter": "organizationType=='uri:ORGANIZATIONS:ASSOCIATIONS' and zipCode=='44600'"
}
{
"topic": "PUT+*/api/organizations/{registrationNumber}",
"callback": "https://saint-nazaire.fr/referentiel_associations/modules/index.php?refOrga={registrationNumber},
"method": "PUT",
"filter": "organizationType=='uri:ORGANIZATIONS:ASSOCIATIONS' and zipCode=='44600'"
}
{
"topic": "PATCH+*/api/organizations/{registrationNumber}",
"callback": "https://saint-nazaire.fr/referentiel_associations/modules/index.php?refOrga={registrationNumber},
"method": "PUT",
"filter": "organizationType=='uri:ORGANIZATIONS:ASSOCIATIONS' and zipCode=='44600'"
}
{
"topic": "DELETE+*/api/organizations/{registrationNumber}",
"callback": "https://saint-nazaire.fr/referentiel_associations/modules/index.php?refOrga={registrationNumber}&setActive=false,
"method": "PUT",
"filter": "organizationType=='uri:ORGANIZATIONS:ASSOCIATIONS' and zipCode=='44600'"
}
]
}
为了稍作解释,webhooks 是外部系统对由给定应用程序发出的事件的注册。在我们的案例中,当区域委员会的参考服务接收到新组织或现有数据变更的数据时,通过参考服务的 API 方法,会引发相关事件,上述定制文件提取将这些事件与提供的 URL 的调用相关联。这个 URL 是由第二个客户(圣纳泽尔市)使用 PHP(但具体技术不重要)公开的。例如,当我们对一个新组织应用POST操作时,回调 URL 会调用创建实体的标识符以及PUT动词。这也是我们引入事实的地方,即该城市只对关联(不是所有组织)感兴趣,特别是那些在其领土上的组织,使用filter属性。
随后,URL 实现可以自由地按其意愿工作,而不依赖于发射器。在某些操作中,给定标识符上发生事件的现实就足够了(例如,在区域议会信息系统中收到DELETE命令时,可以取消关联)。在其他情况下,例如创建关联时,JSON 内容——其精确语法由双方达成一致——将通过回调中获得的标识符通过GET操作检索(因为关联的所有信息都有用)或简单地读取回调调用体(因为最重要的数据被发送到那里,当然使用相同的合同语法)。
这个例子证明是一个成功的实验,因为每个客户都可以自由地以他们想要的方式发展他们的系统,改变技术或其他参数,而他们的合作伙伴甚至不需要知道这一点。在某个时候,城市可能会对其邮政编码区域外的关联感兴趣,只需简单地注册一个新的带有更新过滤器的 webhook 内容。这不会影响事件发射器,甚至不会影响其授权方案:如果城市要求在部门(法国的一个介于地区和城市之间的地理单位)之外被调用,事件就会被发送,但通过接收到的标识符读取信息最终只会导致403 Forbidden HTTP 状态码。这种特定的机制最初让我们决定在回调请求中永远不发送任何数据,以简化授权机制。但是,在某个时候,有人决定强制被调用实体始终以GET调用回复以获取新关联的名称和基本信息是一种带宽浪费。性能并不是问题,但在这种情况下,简单性比授权失误的风险更重要,因为这项数据在法国是公开的,而且很容易获得。
标准化实现互操作性
上述例子演示了一个特定数据模式(我们称之为关键格式,但我们将在本章末尾和下一章中更详细地讨论这一点)必须被设计出来以自由和松耦合的方式交换数据的情况。但更好的情况是,这种合同在行业中已经存在。这是另一个我有幸处理的实际案例,特别是,因为当时我工作的那家小公司迫使一家更大的公司遵守我们的工作方式,仅仅因为我们使用了公认的标准。让我更好地解释一下情况……
我们的旗舰应用程序,一种类型的 ERP,生成 PDF 文档和其他二进制文件,这些文件应该被存储。在相当长的一段时间里,这些文件会存储在数据库旁边的网络共享中,有时也会存储在通过 UNC 链接访问的专用服务器上。电子文档管理系统在几年后开始成为主流,我们需要调整我们的应用程序,使其能够使用这些系统来存储文档。对于这一点,自然的选择是内容管理互操作性服务规范,因为 OASIS 发布了一个功能齐全的 1.1 版本,支持多个元数据模式、分类、版本控制以及许多我们甚至不需要的功能。而且,这也恰好是这个功能领域唯一使用的标准,这使得架构决策变得非常简单。
因此,我们最终使用了标准操作中的几个操作(在第一步中,我们只需要创建文档,向它们添加元数据和二进制内容,然后通过对其元数据内容的查询检索文档),这花费了我们几周时间才将其添加到我们的应用程序中。客户非常满意,因为对软件的简单定制就能使文档出现在他们的 Alfresco 或 Nuxeo EDM 系统中,因为这些应用程序是原生 CMIS 1.1 兼容的。但真正证明这种规范性方法重要性的,是我们第一次遇到一个配备了专有 EDM 的客户:编辑,一家相当大的公司,在我们的共同客户的信息系统中占有重要地位,希望我们修改我们的应用程序以支持他们的专有网络服务,以便发送文档和元数据。在我们最初拒绝后,情况变得有些紧张,但我们很幸运,信息系统所有者是一个聪明的人,她完全理解低耦合的价值。她明智地询问,如果她必须选择另一个供应商来提供这项服务,一个合作伙伴需要付出多少努力。EDM 提供商表示,如果我们的公司被另一家公司取代,他们就不需要做任何事情。就我们公司而言,我解释说——在相反的假设中——我们可能需要重写一些代码以适应另一个专有协议。这对客户来说已经足够了,即使她不是技术专家,也能意识到这种操作方式有问题,并要求使用基于标准的、合同式的通信渠道。由于客户的反对,EDM 提供商别无选择,只能在自己的成本下在其产品中实现对 CMIS 标准的支持。
这在许多方面都证明是一次非常令人满意的经历:
-
首先,我必须承认,重新演绎大卫对抗歌利亚是我职业生涯中最令人自豪的时刻之一。
-
其次,我们没有在会议中添加任何东西到我们的软件中,因为它已经准备好支持 CMIS。
-
第三,客户赞赏我们在帮助他们达到更好、更具进化性的系统方面的专业知识,而不是像其他合作伙伴那样试图将他们推入供应商锁定的情况。
-
第四,互操作性项目在技术上非常容易领导,因为我们只需向合作伙伴提供我们需要的 API 调用 Postman 集合,他们就能从 CMIS 规范的角度验证它们。互操作性调用中没有“隐藏参数”,一切都是明确的,并且严格通过 OASIS 标准进行规范。我们只需在认证的情况下进行一次调整。
-
最后,即使最初不愿意的合作伙伴也承认,在项目结束时,这种方法有助于避免项目中的乒乓效应,即双方都拒绝对对方的非工作调用承担责任,最终导致时间全球损失,客户不满意。我真正相信 CMIS 支持将为他们的产品在未来的某个时候开辟新的机会。
保持完全兼容性
所有这些听起来像是一个美丽的梦想,到处都是粉红色的独角兽和彩虹,但使用国际标准和规范构建的伟大 API 并不能防止互操作性中的最后一种危险。实际上,情况正好相反,API 越干净、越易用,这种危险就越大。听起来很奇怪,不是吗?欢迎来到 Hyrum 定律(www.hyrumslaw.com/),它陈述了以下内容:
在一个 API 有足够多的用户的情况下,
无论你在合同中承诺了什么:
您系统的所有可观察行为
将会有人依赖。
您的 API 越成功,向前兼容性就越重要,因为不可能打破许多客户端的使用。但毕竟,这只是成功的一面,如果你的 API 在你的环境中是最常用的,这确保了很大的市场份额和显著的收入,这并不是一个坏价格。Hyrum 定律更为严厉,因为即使是你没有正式承诺的 API 的一些部分,也可能成为让你陷入麻烦的事情。例如,性能的突然变化可能会使你的最大客户无法继续使用你的 API。即使是较小的、非合同的修改也可能让你陷入这种麻烦。你知道吗?即使是移除一个错误,也可能让一些 API 用户不满意,因为——以一种扭曲的方式——他们的系统依赖于这种特定的行为来运行。这听起来可能很荒谬,但它比你想象的要普遍得多。毕竟,一些 API 用户按照它们的顺序而不是它们的标识符来消费响应属性是非常常见的。
在一定程度上,Hyrum 定律可以被认为是面向对象编程中 Liskov 替换原则的 API 等价物:即使一个类可以通过实现相同的接口来替换另一个类,但如果在函数调用具有相同的参数值时其行为不同,那么实际的兼容性(以及因此的可替换性)并未实现。
管理 API
即使这更多的是一个操作问题,管理大量的 API,包括所有授权访问问题、日志记录,以及可能对 API 消费进行计费、版本跟踪等问题,可能会构成一个严峻的挑战。一些专门的软件产品存在,通常被称为API 网关。它们通常以反向代理的形式实现,充当前端服务器,隐藏实际的 API 暴露。
根据您是否需要一个低耦合的系统或一个非常集成的系统,您可以使用 WSO²或 Ocelot(如果您使用 ASP.NET 实现 API 系统)等系统。
服务依赖反转
如果你记得上一章中的以下架构,你会回想起为了使卫星模块依赖于实现业务领域模型的主体模块,即使调用来自后者并前往前者,使用了端口和适配器模式:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_10.jpg
图 8.10 – 六角架构
这仅仅是依赖倒置原则在架构中的应用,描述一个传统接口被一个模块调用,而无需知道这个接口背后使用了什么实现。在**面向对象编程(OOP)**的代码中,这通常是通过对象注入来实现的。
在面向服务的系统中,尤其是在使用 Web API 时,间接层是通过调用者使用的 URL 来完成的,而无需知道其背后是什么。如果对这个模块的依赖不是问题,那么调用可以是直接的。但如果业务领域模块调用 API,直接的依赖对于演化和找到反转依赖的方法来说并不是一个好的主意。
这通常是通过使用某种回调机制来完成的,其中领域模型模块从外部(在我们的例子中是依赖)被指示调用它应该调用的 URL,可能在其定制中,也可能在运行时初始化步骤中。在前面的 webhooks 的第一种解释中,这就是当城镇需要更改区域委员会应考虑通知城镇的事件过滤器时发生的情况:区域委员会依赖于城镇是不正常的,因为城镇是信息功能请求者。这就是为什么城镇向区域委员会提供回调 URL 的最佳方式是通过注册事件,可能通过/subscribe API 来实现。
这样,我们就达到了一个很好的责任分离,因为地区议会负责以下事项:
-
暴露一个 API,允许客户端从数据参照服务的持久化机制中创建、修改和删除组织
-
暴露一个 API,允许客户端(可能是其他客户端,也可能是相同的客户端)在组织中注册事件
-
当代码中出现事件时,在注册时调用这些客户端提供的 URL
-
在注册时应用提供的过滤器,仅发射请求的事件
在另一端,城镇负责以下事项:
-
在组织参照系上注册它需要观察的事件
-
提供一个可访问的回调 URL,并指向必要的实现
当这种基于事件机制的机制用于每次交互以提供非常低的耦合度时,术语是事件驱动架构(EDA)。在其最先进的形式中,EDA 添加了许多非常精确定义的责任,以允许以下操作:
-
不同的注册和发射机制认证和授权方法
-
通过在必要时重新应用调用并,如果需要,警告管理员,在尝试了一定次数后,事件已被存储以供稍后向某些已注册客户端发射
-
处理大量事件
-
处理大量已注册客户端
-
服务级别协议管理,以及其他许多功能
在正确的实现中,基于 EDA 的系统是软件系统中解耦的最成功成果,允许不同模块完全透明的演变和线性性能。但尽管它在理论上有很长的存在,实际实现却非常少。
现在已经从不同的角度介绍了并研究了“服务”的概念,我们将回到我们的示例信息系统,并将这些新知识应用到它上面。
应用到我们的演示系统中
现在您应该对“服务”的概念不再有任何秘密,是时候看看我们在演示系统中涵盖的一些实际应用,以加强本章的收获。鉴于我们追求的是现代的,选择显然是示例系统的不同模块将通过 REST API 相互交互。尽可能保持中间件尽可能透明。在某些情况下,我们可能需要一些连接器来进行调解,但除此之外,应用程序将与其他集中式 API 进行通信,这些 API 将单独实现(这将通过容器编排器中将要实施的服务概念来完成)。
需要分析的接口
首先,我们将从六角架构图开始,列出所有业务域模型及其依赖关系。上一章中使用的 C4 方法表明,我们需要至少三个业务域,即书籍、作者和销售。
例如,如果我们专注于书籍,那么依赖关系包括持久化机制、作者缓存模块、书籍的 GUI 系统、书籍的 API 控制器,以及一些技术卫星,如日志记录、身份和授权管理。这可以概括如下:
https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/entp-arch-dn/img/B21293_08_11.jpg
图 8.11 – 六角架构的示例
在敏捷方法方面,我并不是说这包含了我们旅程结束时将出现的所有接口。但为了使这个练习尽可能真实,我在写书的同时创建了这个示例信息系统,这样就不会有任何东西被隐藏起来,并且你可以跟随我推荐的精确设计方法,当然我也尽力遵循。
因此,现在我们已经列出了第一个接口,我们需要比仅仅一个名称更精确一些。它们将要做什么?它们将如何设计以提供干净、未来兼容的使用?最重要的是,这些选择将如何反映我从第一章开始就推动的业务/IT 对齐原则?
使用规范和标准
由于我已经谈了很多关于规范和标准至关重要的内容,如果不从它们开始,就无法精确定义接口,那将是一个可怕的信号。而且,当我们使用标准时,精确描述我们在上一节中讨论的接口是非常容易的,因为我们只需要命名它(并且可能引用将要使用的版本)并且所有标准的功能、格式、语义和其他操作都通过标准的文档立即明确定义。
例如,让我们从认证和识别服务开始。对于这个特定的接口,我们将使用基于 OAuth 2.0(RFC 6749)和 JSON Web Tokens(RFC 7519)的 OpenID Connect 协议,OAuth 2.0 本身的 JWT 配置文件是标准化的(RFC 7523)。再次强调,规范和标准的好处是它们极大地简化了我们的工作。如果我要用同样的精确度描述没有标准的接口使用,那么这一章将会更长。对于这个服务,引用几个 RFC(当然,在下一章中,使用这些规范的优秀实现)就足以使一切变得明确。
那么数据库接口呢,或者更准确地说,持久化接口呢?决定使用一个 NoSQL 文档型方法,因为它听起来最适应我们讨论的业务实体和我们要处理的数据量。关于 MongoDB 可能不是一个非常知名的事实,但大多数使用的协议都是开放标准,实际上也被许多其他 NoSQL 数据库实现所使用。如果你想要改进你本地的 MongoDB 数据库,你只需要更改连接字符串即可切换到 Atlas 服务或 Azure CosmosDB 实例,因为一切工作方式都是相同的。MongoDB Wire Protocol 规范是在 Creative Commons Attribution-NonCommercial-ShareAlike 3.0许可下发布的。使用的 BSON 格式(bsonspec.org/#/specification)是公开文档化的,并且可以被任何软件实现。还有更多。除了对软件进行适当的适应以满足我们的需求,以及它易于创建免费数据库的事实之外,标准化方面是使 MongoDB 成为我们示例应用程序的一个合理选择的关键。
好的,现在转到授权问题!在软件授权管理方面存在两个主要规范,即admin角色拥有所有权限,operator可以根据投资组合读取和写入实体,而reader角色只能读取数据(例如)。然后,使用 OPA 可能不是正确的选择,因为它会添加很多开销。当然,真正的疑问,再次,是考虑时间。当然,到本书结束时,我们的示例应用程序将非常简单,使用 OPA 将是过度设计。然而,这个练习的目标是展示如果我们旨在一个真实、工业、自由发展的信息系统,我们应该如何工作。鉴于我们假设权利管理将会更复杂,我们将立即开始使用适应的接口,在我们的情况下意味着 OPA 1.0。
记录功能的情况有些不同,因为这并不是一个直接的功能,而是一个技术特性。然而,这并不意味着不应该使用相同的标准化方法。唯一的区别是,这种间接级别不会像其他规范那样在国际层面上标准化,而是将在平台上本地化。我们的示例应用程序主要使用.NET Core,因此我们将使用该技术的标准,而恰好有一个标准全局接口在Microsoft.Extensions.Logging中,称为ILogger,它也存在一个泛型类ILogger<T>。我们将在技术章节中返回来看如何使用它,也许我们甚至会通过使用像 Serilog 这样的语义日志系统来增加一些趣味。但就目前而言,只需说日志机制也将得到标准化即可。
值得注意的是,该领域的某些参与者目前正在努力实现第一级标准化,例如 Elastic 的 ECS 规范(详情请见www.elastic.co/guide/en/ecs/current/ecs-reference.html)。由于 Elastic 是观察平台的主要出版商之一,且该规范是开源的,我们可以对它作为标准的传播抱有一定的希望,尽管只有时间才能证明一切。
我在哪里可以找到规范和标准?
当我教授或咨询关于业务/IT 一致性,特别是关于需要参考规范和标准的问题时,这个问题总是在某个时候出现:我们该如何搜索规范? 我必须说,我对这个问题感到非常惊讶,原因有几个:
-
寻找它们就像任何互联网搜索一样简单,几乎所有这些规范都是公开的,为了实现它们的目标,需要尽可能的可见,因此找到它们在技术上绝对没有任何困难。这表明,大多数在 IT 行业工作的人(根据我培训或教授的人数,我确实有一些显著的统计数据)对自己的行业标准一无所知,这相当令人烦恼。我可以理解,不是很多人知道 BPMN 2.0,因为流程是一个特定的用例,并不是所有应用程序都需要工作流引擎。但为什么一些架构师不知道 OAuth 2.0,因为这在互联网上几乎无处不在,几乎所有软件应用程序在某个时候都需要某种形式的身份验证?
-
即使一些非专业人士也知道一些最著名的规范和标准提供者,例如 ISO 或 IETF。甚至**请求评论(RFC)**这个术语也被很多人所理解。当然,一些生产规范的 IT 特定组织,如 OASIS,可能不太为人所知。但另一方面,**万维网联盟(W3C)**是一个非常活跃且被广泛认可的机构。那么,为什么问这个问题的人没有本能地从这些组织开始,搜索他们所需的内容呢?
对于一些客户,我在某个时候甚至创建了一份白皮书,其中包含了我在当时工作的业务环境中(公共和政府机构)使用的近一百个规范和标准,因为这个问题总是反复出现,我希望有一个快速的答案,不仅告诉他们在哪里可以找到他们需要的东西,还提供给他们已经找到的答案。这有一个简单的原因:因为我发现真正的问题不是这些人不知道“在哪里可以找到规范”,而是这表明了他们对使用这些规范的能力存在疑虑。规范和标准可能因为数百页解释所有可能情况而显得有些令人畏惧。即使是简单的 RFC 也确实不易阅读。
但这个问题也有其他答案:
-
首先,找到合适的规范并开始使用它并不需要你阅读规范说明。实际上,只有当你需要实现其中大部分内容时,阅读它才会对你有益。
-
在大多数情况下,你会使用实现规范的组件,你所要做的就是确认它们是被认可、已经建立起来的模块。
例如,为了在我们的示例信息系统中使用 OpenID Connect,我们基本上不需要了解该协议本身,因为我们将依赖 Apache Keycloak,它以透明的方式为我们实现它。我们唯一需要处理的是选择身份提供者以及 Keycloak GUI 简化的一些自定义设置。
即使你必须深入研究规范的细节,大多数时候,你只需要理解其中的一小部分。例如,在我们的示例应用程序中,我们将在某个时候肯定需要实现某种对作者合同二进制文档的支持;这意味着我们当然会使用 CMIS 1.1,因为这是这个用例的公认标准。但因为我们只会发送文档,添加二进制和元数据,并查询文档作为回报,我们可能只使用整个规范中的 10%。
最后,一个好的规范通常已经相当广泛地传播并在国际上使用。所以,阅读完整的规范说明总是很有趣的,但让我们说实话:你在最初接触标准的方式主要是通过模仿在参考网站上找到的一些示例调用,并根据你的需求进行调整。只有当你达到一定程度的复杂性时,在 RFC 的全文中找到实施细节的精确细节才会变得容易一些。
对于其他接口的关键格式
对于这个主题的最后一部分,接下来出现的逻辑问题是:当我们的环境中没有规范或标准时,我们该怎么办?
对于这个问题,我总是首先本能地回答,“你愿意打赌确实没有我可以向你展示的规范吗?” 大多数时候,这个问题会回到之前的问题,只是表明提问者对规范感到不舒服,或者他们害怕这会很难(但实际上,规范反而让你摆脱了所有困难的设计方面)。因为,让我们面对现实,今天我们几乎为所有事情都有规范。好吧,在 IT 领域可能比机械领域少一些规范。但是,对于每个常见功能都有标准。你有一套通用的技术规范,用于国际数据传输中使用的每个实体,以及包括银行、保险、旅行等在内的每个常见的人类活动。甚至还有一个 ISO-Gender 规范(ISO/CEI 5218),用于以数字格式表示人类性别。
答案的第二个部分涉及当确实没有适用于你所在环境的规范时我们应该做什么。对这个问题的答案已经在本章中给出了一部分:然后你创建一个被称为关键格式的东西,它具有与真实规范相同的标准化目标,但仅限于你自己的环境。当然,目标是追求普遍性总是更好的。不仅因为,你永远不知道,但如果你投入足够的努力,并且其他人对此感兴趣,你的格式可能会成为规范(这是规范出现的方式:它总是从对业务领域极其了解的个人开始,他们努力将他们的知识转化为技术性的东西,然后其他参与者将其作为交换的合理基础达成一致)。而且因为追求普遍性将使你的关键格式尽可能接近规范,并带来尽可能多的优势。
对于这一点,规则是尽可能快地回归到现有的规范。当然,似乎不存在一个关于作者概念的国际化规范(尽管都柏林核心的creator属性允许我们从一个资源到创建该资源的个人或组织之间建立联系),但因为它指向个人,所以许多其他相关的规范会迅速适用,例如社会保险号码用于唯一标识,ISO 8601 用于创作日期,等等。同样的情况也适用于书籍:当然,我们可能找不到一个完美的标准来精确地满足我们样本应用的需求,特别是其持久化系统,但尽管如此,还是有关于语言的规范(ISO 639),以及用于注册书籍识别的国际公认标准代码,如国际标准书号(ISBNs),以及我们将在系统中记录的书籍描述中几乎一切事物的标准。
现在,真正的问题是什么应该放入书中以及作者的关键格式?这是一个如此重大的问题,以至于需要单独用一章来阐述。好消息是,下一章将解释如何回答这个问题。
摘要
在本章中,我采用了简短的历史方法(详细的方法本身就可以写成一本书)来解释在面向服务中涉及的风险是什么,以及这个看似简单却难以定义的“服务”一词在过去几十年是如何被实施的。我们肯定还没有到达故事的结尾,但如今,似乎最好的方法就是使用带有中间件的 REST API,尽可能通过使用规范和标准来减少。这不仅避免了昂贵的转换连接器,因为互动中的每个人都使用同一种语言,而且还帮助我们了解我们的设计是否正确,因为联盟和专家已经对这个业务领域进行了很多思考。
标准化的 API 使得今天在不破坏它们的情况下改变重要信息系统的一些部分变得容易。它们允许进行国际银行业务、更高效的保险系统、简化出国旅行,以及许多工业 IT 世界的成就。
我们讨论了规范,还讨论了兼容性、服务的演变、服务将通过接口如何集成,以及更多内容。到本章结束时,我们回到了我们的示例应用程序,并展示了将用于实现它将公开的一些服务的规范。现在,一个难题仍然存在:当没有针对商业需求的标准化格式,而我们又需要创建一个关键的格式(当然,尽可能使用规范来定义其内部属性)时,我们如何确定这个格式的内容?我给出的最佳答案是使用领域驱动设计(DDD)。这正是下一章的主题。
907

被折叠的 条评论
为什么被折叠?



