企业应用架构模式(典藏版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2 三个基本层次

本书主要就三个基本层次的架构展开讨论:表示层、领域层和数据源层(这里的命名取自文献[Brown et al.])。表1-1总结了这些层次。

表1-1 三个基本层次

表示逻辑处理用户与软件间的交互。可能简单到只是命令行或基于文本的菜单系统,但是当前的客户界面往往是功能完善的富客户图形界面,或者是基于HTML的浏览器界面(本书中的“富客户”指代Windows/Swing/fat-client用户界面,而不是HTML浏览器)。表示层的主要职责是向用户显示信息并把来自用户的命令解释成领域或数据源上的各种动作。

数据源逻辑主要关注与其他系统的交互,这些系统将代表应用完成相关的任务。它们可以是事务监控器、其他应用、消息系统等。对于大多数企业应用来说,最主要的数据源逻辑就是数据库,它的主要责任是存储持久数据。

剩下的部分就是领域逻辑,也称为业务逻辑。它就是应用必须做的所有领域相关工作,包括根据输入数据和已存储的数据进行计算、验证从表示层传入的任何数据,以及根据从表示层接收的命令来确定应该调度哪些数据源逻辑。

有时,层次组织成领域层对表示层完全隐藏了数据源。但更多的时候,是表示层直接访问数据存储。虽然这样做并不纯粹,但是在实践中往往运行良好。表示层可能解释来自用户的命令,通过数据源将相关数据从数据库中提取出来,然后让领域逻辑层在向用户显示相关数据之前先处理这些数据。

单个应用通常在上述的三个层次上都包含多个包。如果某个应用不仅要支持用户通过富客户界面操作,还要支持用户通过命令行形式操作,则它会有两个表示层:一个支持富客户界面,另一个支持命令行。对于不同的数据库,通常也需要多个数据源组件,特别是在与已有的包通信时常常见到。即便是领域逻辑,也有可能被分割成相互独立的不同区域,特定的数据源包只能由特定的领域包使用。

到目前为止,我们一直都在讨论用户。这很自然地会引出一个问题:如果驱动软件的不是人,情况又怎么样呢?例如,驱动者可能是时髦的Web Service或是一个老土但实用的批处理过程。对于后者,用户将是一个客户程序。这样,很明显,表示层就有可能与数据源层出现某些相似之处,因为它们都涉及连接到外部世界。这就是Alistair Cockburn的Hexagonal Architecture模式[wiki]背后的逻辑,它将任何系统都视为由到外部系统的接口所围绕的一个核心。在Hexagonal Architecture中,所有外部的东西本质上都是外部接口。因此,它是一种对称视图,而不是本书中的非对称分层方案。

然而,我认为这种非对称性是有益的。因为,我认为在为别人提供的接口和使用别人的服务之间应该做出明确区分。这就是表示层和数据源层相对于核心的本质差别。表示层是系统向其他人提供服务的外部接口,不管外面是复杂的人类还是一个简单的远端程序。数据源层是向你提供服务的接口。这样区分的好处是:客户的不同将改变你对服务的看法。

对每个企业应用,尽管我们能够识别出常见的表示层、领域层和数据源层,但是具体如何分离要取决于应用的复杂程度。从数据库中读取数据并把它显示在Web页面上的简单脚本,可能全部在一个过程中。我仍然会努力将这三个层分开,不过在这里可能只是把每个层的行为放到三个不同的子程序中。随着系统变得更加复杂,就可以将三个层分解成分离的类。如果复杂度继续增加,则把类分配到不同的包中。我的总体建议就是根据不同的问题,选择一种适合的分离方式,但是切记一定要进行某种形式的分离——至少在子程序级别。

伴随着分离,还有一条关于依赖性的持续规则:领域层和数据源层绝对不要依赖于表示层。也就是说,在领域层和数据源层的代码中,不要出现调用表示层子程序的情况。这条规则将简化在相同的基础上替换表示层的代价,也使得在更深层次上修改表示层更容易且不会产生严重影响。领域层与数据源层的关系更复杂,其取决于数据源层的架构模式。

处理领域逻辑时,其中一个最困难的部分就是区分什么是领域逻辑,什么是其他逻辑。我喜欢的一种不太正规的测试办法就是:假想向系统中增加一个完全不同的层,例如为Web应用增加一个命令行界面层。如果为了做到这一点,必须复制某些功能,则说明领域逻辑渗入了表示层。类似地,你也可以假想一下,将关系数据库更换成XML文件,看看情况又会如何?

举一个例子。我所知道的一个系统有一张产品列表,其中,当月销售量比上月销售量大10%的产品需要用红色显示。为实现这一点,开发者在表示层逻辑中比较当月和上月的销售量,然后将差别大于10%的产品显示为红色。

这样做的麻烦就是将领域逻辑放到了表示层中。为了进行适当的分离,需要在领域层中定义一个方法,用来指示该产品的销售量是否较上月有较大提高。该方法完成销售量的比较,返回一个布尔值。表示层则只需要简单地调用一下这个布尔方法,如果返回值为真,则用红色突出显示这个产品。这样,该过程就分解成两部分:确定需不需要突出显示,选择如何突出显示。

当然,我担心这样也许有些太教条主义了。Alan Knight在审阅本书时评论说:他自己“很头大,将部分领域逻辑混入表示层到底是滑向地狱的第一步呢,还是只有教条的纯粹主义者才会反对的合理做法?”我们感到不安的原因是这两者都有!