发布于 2026-01-06 0 阅读
0

优秀软件架构的要素是什么?开发者无法估算:限界上下文、网络空间与代理、所有权 == 作者身份、协作单元。

优秀的软件架构有哪些要素?

开发人员无法估算

有限上下文

网络空间与代理人

所有权 == 作者身份

合作单元

“所有模型都是错误的,但有些模型是有用的。”——乔治·博克斯

好坏并没有统一的标准。以下是我总结的关于优秀软件架构的AAA原则:

  • 问责制:良好的软件架构使每个团队对其相应的业务目标负责。
  • 自主性:良好的软件架构应该允许每个团队在很大程度上独立地开展工作,而不会频繁地受到其他团队的阻碍。
  • 摊销:良好的软件架构促进前瞻性思维,允许将基础设施的前期成本摊销。

问责制 >> 自治 >> 摊销。在 90 年代,代码重用(摊销)在面向对象社区非常流行。之后,SOA/DDD 又非常强调自治。但我发现,在实践中,摊销和自治在指导边界定义方面都存在模糊之处。很难说服人们 X 比 Y 更好。实际上,模块的功能应该源于其职责。

开发人员无法估算

“问责制”才是关键。我认为“缺乏问责制”才是软件开发最大的危机,它比“无法应对所谓的复杂性”更为严重。

开发人员无法进行估算已不是什么秘密。这给优秀的软件工程带来了许多非常根本性的问题:

  • 因为我们永远无法确定真正需要多少人,所以中层管理人员会尽可能多地增加人手,只要编制允许。为什么?很简单,他们的薪水与他们管理的人数成正比。
  • 为了保持软件的“可维护性”而对其进行重构,并没有任何商业价值。“可维护性”到底是什么意思?只要投入足够的人手,问题总能解决。软件工程又不是什么高科技,它能有多难?

解决这个问题的关键不在于掌握故事估算的诀窍,而在于如果我们与业务负责人组成同一个团队,我们就无需进行估算。每个软件团队都对应一个且仅对应一个业务团队,他们共享同一个 OKR。重要的是,每个团队只负责一件事。

图像

这是典型的公司组织结构图。每个子团队的 OKR 都与其上级团队保持一致。OKR 的关键结果都是可衡量的,以便每个团队都能承担相应的责任。

典型的糟糕软件架构是这样的:许多小型团队各自负责一个微服务。业务负责人通常需要多个微服务的支持才能实现目标。每个需求都需要反复与不同的软件团队沟通。软件团队无法为其微服务设定清晰的业务目标,因此也无法说明他们对业务的贡献。

我再次强调:软件架构的首要目标是让软件团队对业务负责。

有限上下文

从宏观角度来看,架构的核心在于限定上下文(详见领域驱动设计)。这就像企业组织结构图在软件领域的体现:

图像

以电子商务领域为例,业务被划分为若干个限界上下文。没有哪个团队能够覆盖所有限界上下文的业务流程。这通常是一件好事,大问题被分解成小问题,业务和技术团队可以在一个限界上下文中协同工作,朝着共同的目标前进。

网络空间与代理人

一个有限的上下文对于一个团队来说仍然太大。至少在微服务架构下,我们认为情况确实如此。如何将其进一步分解成更易于管理的部分呢?我的模型是“网络空间与代理”。我们作为程序员所构建的东西本质上可以归结为两件事:

  • 网络空间
  • 一些具有一定智能的代理在网络空间与我们互动。

网络空间和我们所生活的现实世界一样,都是虚拟世界,它也基于因果关系。其中有两种法则:

  • 自然法则:自然本身的运作方式
  • 社会法:一种模仿自然法以建立社会秩序的人为体系。

引力是自然法则的一个例子,“偿还债务”是社会法则的一个例子。两者运作方式相同:给定某些原因,就必然会根据法则产生某些结果。我们使用 C/C++/Java/Go/……等等,随你喜欢怎么称呼它。从光线追踪算法、文字处理器到电子商务平台,大体上都是将一些规则嵌入系统中。这些“法则”需要像水泥一样,保持静态和可预测性,构建现实世界。

在我们构建的网络空间之上,人类彼此互动,从社交网络到交易,无所不包。然而,人类扮演的角色正日益被我们编写的人工智能代理所取代。例如,不再由编辑挑选内容,而是由机器人生成新闻并为你准备首页。这些代理变得越来越复杂,总有一天,它们会从网络空间走向现实世界。

这两种代码的运作方式截然不同。网络空间通过推导因果关系来维护自然/社会秩序。而智能体则收集结果,推断模型以实现某个目标的最大化。区分智能部分与系统的其他部分至关重要。作为人类,我们需要规则保持稳定,才能建立稳定的预期。如果“法则”不断变化,网络空间就会变成一个“魔法”之地,与我们现实世界的经验脱节。

图像

模型、视图和控制构成了网络空间。人类和机器人是智能体。模型根据自然或社会规律所定义的因果关系维护数据完整性。视图和控制为人类/机器人提供便捷的交互界面。

所有权 == 作者身份

网络空间部分仍然过于庞大。它通常是一个多步骤的业务流程。例如

图像

而且,界限清晰的上下文彼此关联。例如

图像

问责制问题源于我们使用的编程语言无法涵盖完整的因果链。我们可以在白板上绘制一个整体的流程图,但要实现它,就必须将其分解成许多小的服务/功能。工作流引擎经常被提及,因为它与问题领域非常契合,但BPMN并非编程语言。

各个步骤之间存在很强的因果关系。产品详情页显示的促销活动需要显示在购物车中,需要在结账页面显示,并最终应用到收据上。我们使用的“函数”只能描述 500 毫秒时间范围内的因果关系。我们必须将流程拆分成更小的步骤,或者拆分成服务于不同角色用户的不同服务,因为因果关系被隐藏在混乱的实现中。软件运行起来就像接力赛,一个服务将职责传递给另一个服务。理想情况下,代码应该反映流程图,并且读起来也应该像流程图一样。

更糟糕的是,职责划分并不明确,这经常导致团队之间就责任归属问题争论不休。这种高度政治化的环境令开发人员感到不满。讽刺的是,当事情进展不顺时,却没有人能够承担全部责任,因为每个人都只是从整体中分了一小部分。

目前解决此问题的最佳实践是在所有内容之上设置一个门面团队。“所有权 = 作者权”,我们只愿意为自己编写的内容负责。这是人之常情。为了让这些团队成员产生所有权意识,我们允许使用轻量级的代理/包装器或编排服务将微服务粘合在一起。但这通常会导致自治度低。

理想的编程语言应该提供描述业务流程的“功能”。多个并发的因果链将通过消息传递相互关联。这样,我们可以为每个业务流程分配一个独立的软件团队。他们可以对其负责的内容承担全部责任。他们与人工操作员和机器人组成一个团队,共同承担损失和收益,而不是成为成本分摊中心。

合作单元

还有一点出了问题。过去,我们协作所使用的单元是语言提供的模块,例如程序集、包和类。但现在情况已经不同了。软件模块需要进行版本控制并单独部署,才能支持多个团队独立工作。

但我们真的“总是”需要为不同的微服务使用不同的语言和工具吗?语言差异和工具的脱节使得跨团队沟通更加困难。你可以掌控你的流程,掌控你的服务,但这不应该妨碍你与同事使用同一种语言。一种编程语言可以扮演三种不同的角色:连接机器、开发者和团队。如今,编程语言主要作为连接机器和人的工具,导致团队在整体上缺乏沟通。

解决方案应该将软件视为一个整体,而不是局限于单个“操作系统进程”的狭隘视角。引入一个新的微服务的成本应该与启动一个由独立包中的函数支持的独立线程一样低。理想的编程语言不应该对所有函数调用一视同仁。


文章来源:https://dev.to/taowen/what-makes-up-the-software-pd0