Cluster 3: #8 distância do framework, #11 organizar por ator, #9 constructs corretos
Cluster 3: #8 Framework Distance, #11 Organize by Actor, #9 Correct Constructs
Introduction The constraint is already established by the article: Lehman’s Law of Continuous Change (1974) — every system in real-world use will be requested to change, indefinitely, and the only open question is whether that change is cheap (localized alteration) or expensive (rewrite). The three principles of this cluster do not prevent change — there is no way to stop it from arriving — they attack the source from which it comes, each in a different direction: #8 shields against change coming from outside the system (the framework ecosystem), #11 shields against change coming from people around the system (actors with competing requests), and #9 shields against change coming from the domain itself growing beyond what was validated as safe.
引言 该限制已由文章设定:莱曼持续变化定律(Lehman’s Law of Continuous Change, 1974)——任何实际使用的系统都将被要求无限期地进行变更,唯一悬而未决的问题是这种变更的成本是低廉的(局部修改)还是昂贵的(重写)。本集群的三个原则并不能阻止变更——因为变更必然会到来——它们是从不同的方向攻击变更的来源:#8 屏蔽来自系统外部(框架生态系统)的变更,#11 屏蔽来自系统周围人员(有竞争性需求的参与者)的变更,而 #9 则屏蔽来自领域本身增长超出已验证安全范围的变更。
#8 Framework Distance The conceptual basis is what Robert C. Martin (“Uncle Bob”) formalizes in Clean Architecture (2017): frameworks belong to the outermost ring of the architecture — they are a delivery detail, not the center of the system. Business rules should not import anything from the framework; it is the framework that should adapt to run the business rules (Dependency Inversion applied at an architectural scale, not just between two classes). Martin proposes a simple test for this, known as “screaming architecture”: looking only at a project’s folder structure, it should scream the business domain — “this is a tax compliance system” — and not scream the framework — “this is an ASP.NET project” or “this is an Angular project.” The AngularJS case is a practical example of this failure: teams that wrote business rules using specific AngularJS constructs ($scope, watchers, directives with embedded domain logic) didn’t just face a “switch framework” problem when support ended in 2022 — they had to rewrite the business rules themselves, because they never existed separately from the framework to begin with. The change guaranteed by Lehman’s Law arrived through the ecosystem door, not a business request, and the cost was that of a complete rewrite.
#8 框架距离 其概念基础是罗伯特·C·马丁(“鲍勃大叔”)在《整洁架构》(2017)中所形式化的内容:框架属于架构的最外层——它们是交付细节,而非系统核心。业务规则不应导入框架中的任何内容;相反,应该是框架去适配业务规则的运行(这是在架构层面,而不仅仅是在两个类之间应用依赖倒置原则)。马丁为此提出了一个简单的测试,即“尖叫架构”(screaming architecture):仅通过观察项目的文件夹结构,它就应该“尖叫”出业务领域——“这是一个税务合规系统”——而不是“尖叫”出框架——“这是一个 ASP.NET 项目”或“这是一个 Angular 项目”。AngularJS 的案例就是这种失败的典型实践:那些使用 AngularJS 特定构造($scope、watchers、带有嵌入式领域逻辑的指令)编写业务规则的团队,在 2022 年支持结束时,面临的不仅仅是“更换框架”的问题,而是不得不重写业务规则本身,因为这些规则从一开始就没有与框架分离。莱曼定律所保证的变更通过生态系统的大门进入,而非业务需求,其代价是一次彻底的重写。
(Code examples omitted for brevity, but the principle remains: isolate business logic from framework-specific controllers.)
(代码示例略,但原则保持不变:将业务逻辑与框架特定的控制器隔离。)
#11 Organize by Actor Martin also reformulates the “S” in SOLID here. The classic formulation of the Single Responsibility Principle — “a class should have only one reason to change” — is vague: reason to change, according to whom? In Clean Architecture, he resolves the ambiguity with an operational definition: a module should be responsible to one, and only one, actor — where an actor is the group of people (or business role) that can request a change in that specific module. If two different actors have the power to request changes in the same class, that class has two competing reasons to change — and a change requested by one actor risks silently breaking the behavior that the other actor depends on, without anyone having asked for it.
#11 按参与者组织 马丁在此处重新阐述了 SOLID 中的“S”。单一职责原则的经典表述——“一个类应该只有一个引起它变化的原因”——是模糊的:引起变化的原因,是根据谁的意愿?在《整洁架构》中,他通过一个操作性定义解决了这种歧义:一个模块应该只对一个且仅对一个参与者负责——参与者是指可以请求该特定模块变更的一组人(或业务角色)。如果两个不同的参与者都有权请求同一个类进行变更,那么该类就有两个竞争性的变更原因——而由其中一个参与者请求的变更,可能会在无人知晓的情况下悄悄破坏另一个参与者所依赖的行为。
(Code examples omitted for brevity, but the principle remains: separate code based on who requests the change, not just technical layers.)
(代码示例略,但原则保持不变:根据谁请求变更来分离代码,而不仅仅是按技术层分离。)