24 Partial Boundaries
部分边界:完整的架构边界代价高昂,它需要双向多态的边界接口、输入与输出数据结构,以及将两侧隔离为可独立编译和部署组件所需的依赖管理,这不仅实现成本高,维护成本也很大。在许多情况下,优秀的架构师会认为这种成本过高,但仍希望为未来可能需要的边界预留空间。这种前瞻性设计在敏捷社区中常被批评为违反 YAGNI(“你用不到它”),但架构师有时会认为“也许将来会用到”,于是选择实现部分边界。其中一种方式是“跳过最后一步”:完成所有使组件可独立编译和部署所需的设计(包括接口和数据结构),但在实际中仍将它们编译和部署为同一个组件,这种方式在代码量和前期设计上与完整边界相同,但不需要多组件的管理负担,如版本控制和发布管理,这一点不容忽视。FitNesse 早期就采用了这种策略,其 Web 服务器组件原本设计为可与 wiki 和测试部分分离,以便未来复用,但为了实现“下载即用”的目标,最终只发布一个 jar 文件,避免用户处理多个组件和版本兼容问题;但随着时间推移,当发现不再需要独立的 Web 组件时,这种边界逐渐被侵蚀,依赖开始跨越界限,导致如今重新分离变得困难。另一种是单向边界:完整边界通过双向接口实现双向隔离,但成本较高,而更简单的做法是采用类似策略模式的结构,通过 ServiceBoundary 接口供客户端使用,由 ServiceImpl 实现,从而建立依赖反转,为未来扩展为完整边界预留空间,但由于缺乏双向接口,依赖关系容易被破坏,出现不规范的反向依赖,这只能依靠开发者和架构师的自律来避免。更简单的方式是外观模式(Facade):甚至放弃依赖反转,仅通过一个 Facade 类定义边界,将所有服务方法暴露出来,并将调用转发给客户端不应直接访问的服务类,但客户端实际上对这些服务类存在传递依赖,在静态语言中,服务类的源码变更会导致客户端重新编译,同时这种结构也很容易产生越界调用。总之,这三种方式展示了部分实现架构边界的思路,各有成本与收益,在特定场景下可以作为未来完整边界的占位方案,但如果完整边界始终没有实现,这些结构也可能逐渐退化,而架构师的职责之一正是判断哪里未来可能需要架构边界,以及应当完全实现还是仅部分实现。

浙公网安备 33010602011771号