第二十四章 局部的边界

全面的架构边界是昂贵的。它们需要许多交互的多态边界接口,输入和输出数据结构,以及将双方隔离为可独立编译和部署的组件所需的所有依赖管理。这需要很多工作。维护也是很多的工作。

在很多情况下,一个好的架构师可能会认为这样一个边界的成本太高,但是如果以后需要的话,可能还是要为这个边界保留一个位置。

这种前瞻性的设计在敏捷社区中被许多人所诟病,这是对YAGNI的侵犯:“你并不需要它”(“You Aren’t Going to Need It.”)。然而,架构师有时看这问题,然后想:“是的,但我可能会“在这种情况下,他们可以实现一个局部的边界。

跳过最后一步

构建局部的边界的一种方法是完成创建独立可编译和可部署组件所需的所有工作,然后将它们维持在同一个组件中。交互的接口在那里,输入/输出数据结构在那里,一切都建立起来了——但我们把它们全部编译和部署成一个单一的组件。

显然,这种局部边界需要相同数量的代码和准备设计工作作为完整的边界。但是,它不需要管理多个组件。没有版本号跟踪或发布管理负担。这个差别不能掉以轻心。

这是FitNesse的早期战略。FitNesse的Web服务器组件被设计成可与Wiki分离并测试FitNesse的一部分。这个想法是,我们可能想通过使用该Web组件创建其他基于Web的应用程序。同样,我们不希望用户必须下载两个组件。回想一下,我们的设计目标之一是“下载即用”(download and
go)。我们的意图是,用户将下载一个jar文件,并执行它,而不必寻找其他jar文件,计算版本兼容性,等等。

FitNesse的故事也指出了这种方法的一个危险。随着时间的推移,很明显,不再需要一个单独的Web组件,Web组件和wiki组件之间的分离开始减弱。依赖性开始以错误的方向越线。现在,将它们重新分开是件麻烦事。

一维的边界

全面的架构边界使用相互的边界界面来保持双向隔离。在初始设置和正在进行的维护中,双向保持分离都是昂贵的。

图24.1显示了一个更简单的结构,用于保存后来扩展到完整边界的地方。它体现了传统的策略模式。ServiceBoundary接口由客户端使用,并由ServiceImpl类实现。

图24.1策略模式

应该清楚的是,这为未来的架构边界设定了舞台。为了将ClientServiceImpl隔离,必须进行依赖性反转。还应该清楚的是,分离可以很快降解,如图中令人讨厌的虚线箭头所示。没有相互接口,除了开发人员和架构师的勤奋和规约外,没有什么能够阻止这种反向通道。

门面

一个更简单的边界就是门面模式,如图24.2所示。在这种情况下,甚至依赖倒置也被牺牲了。边界由Facade类定义,Facade类将所有服务列为方法,并将服务调用部署到客户端不应访问的类。

图24.2 门面模式

请注意,Client对所有这些服务类具有传递依赖性。在静态语言中,更改其中一个Service类中的源代码将强制Client重新编译。另外,你可以想象用这个结构创建有多容易出现层层反馈的问题。1

小结

我们已经看到了局部实现的架构边界的三种简单方法。当然还有很多其他的。这三个策略只是作为例子提供。

每种方法都有自己的一套成本和收益。在某些情况下,每一个都适合作为最终完整的边界的占位符。如果这个边界从未实现,每一个都可能被降级。

决定哪里会有架构边界,以及是否全面或局部地实施这个边界,是架构师的职责之一。

1. you can imagine how easy backchannels are to create with this structure.

results matching ""

    No results matching ""