本书绝大多数篇幅讨论的都是逻辑层次——将系统中各部分分离,以降低不同部分之间的耦合。即使是都运行在同一台计算机上,不同层次间的分离也是有用的。当然,系统物理结构的不同会有所影响。
对于大多数信息系统来说,主要的决定就是在哪里运行处理工作,是在客户机上,还是在台式机上,又或是在服务器上?
通常,最简单的情况是将所有东西都运行在服务器上。在这种情况下,一个使用Web浏览器的HTML前端是一个好方法。这样做最大的好处是所有的东西都在有限的环境内,很容易升级和修复。不必操心将应用部署到许多台式机并使它们与服务器保持同步。也不必操心与台式机上其他软件的兼容性问题。
支持在客户机上运行的一般论点是响应速度或断接操作。任何运行在服务器上的逻辑在响应用户操作时,都需要一个来回的通信开销。如果用户想摆弄一些东西并看到即时的反馈,这个通信来回就会成为障碍。它还需要在网络连接保持的状态下运行。当然,网络的分布可能会无所不在,但是至少在我写下这些文字的时候,31000英尺高的地方还没有。也许在不久的将来,就会到处都有了,但有不少人需要立即工作,不愿等待无线网络覆盖每个角落。断接操作带来了特殊的挑战,但我并不想在本书中对此进行过多讨论。
有了这些一般性约束,我们就可以逐层分析了。数据源层一般都是运行在服务器上。例外情况是:当需要断接操作时,可以将服务器的功能复制到一台足够强大的客户机上。在这种情况下,在断接的客户机上对数据源的任何修改,都需要同步到服务器上。正如我前面提到的那样,关于这方面的讨论,我想留到以后某个时候或留给另一位作者。
在何处运行表示层主要取决于用户界面的种类。如果运行的是一个富客户,则意味着表示层运行在客户端。如果运行的是一个Web界面,则意味着表示层运行在服务器端。当然,也有例外——例如,在桌面上远程操作Web服务器的客户端软件(如Unix中的X server)——当然,这是极少数情况。
如果要建立一个B2C系统,就没什么选择了。无论是谁都可以连接到你的服务器,你也不想因为客户用的是TRS-80来在线购物就把他拒之门外。在这种情况下,可以在服务器上完成所有工作,并提供一个HTML界面给浏览器。这样做的缺点就是每个决策都需要从客户端到服务器走一个来回,可能会影响响应时间。可以通过可下载的applet或浏览器脚本来缓解问题,但是它们同时会带来浏览器兼容性等其他问题。使用的HTML越纯粹,事情就会变得越简单。
即使你们的每一台台式机都是由你们公司的信息系统部门手工精心搭建的,这种简单性仍然非常诱人。因为即使是非常简单的富客户系统,也会遇到保持客户端更新和避免与其他软件的兼容性错误等问题。
人们希望有富客户表示层的主要原因是:有些任务对于用户而言太复杂了,为了有一个可用的应用系统,它们的需要超出了Web GUI的能力。当然,人们已经在逐渐习惯于采用使Web前端更可用的各种方法,这些方法减少了对富客户方式的需求。因此,我非常赞同只要有可能就用Web表示,只有在必需的情况下才使用富客户。
剩下来的就是领域逻辑了。领域逻辑可以全都运行于服务器端,也可以全都运行于客户端,也可以将其拆分。同样,全部运行在服务器端是方便维护的最佳选择,向客户端转移是为了响应时间及断接使用的需要。
如果你必须在客户端运行某种逻辑,可以考虑将所有逻辑都运行在客户端——这样至少保证了相关的东西都在一起。通常这意味着富客户端——在客户机上运行Web服务器不会显著提高响应速度,尽管它可以用于处理断接操作。在这种情况下,仍然可以使用 事务脚本 或 领域模型 将领域逻辑放在与表示分离的模块中。将所有的领域逻辑放在客户端的问题是升级和维护代价高。
将领域逻辑分割到桌面和服务器上,应该是最差的选择,因为这样做无法确定任意一块逻辑到底在哪。采用这种方式的主要原因是:只有一小部分领域逻辑需要在客户端完成。其中的诀窍就是将这一小部分独立出来成为自包含的模块,使得它不依赖于系统的任意其他部分。这样,就可以在客户端或服务器端运行它了。这将需要采用一些烦人的小技巧,但它的确是一个解决问题的好办法。
一旦选择了处理节点,接下来就应该尽可能使节点的所有代码保持在单一进程内完成(可能是在同一个节点上,也可能拷贝在集群中的多个节点上)。除非不得已,否则不要把层分离在多个进程中完成。因为那样做不但损失性能,而且增加复杂性,因为必须增加类似下面的模式,如 远程外观 和 数据传输对象 。
重要的是要记住,以下因素被Jens Coldewey称为 复杂性助推器 (complexity booster):分布、显式多线程、范型差异(例如对象/关系)、多平台开发以及极限性能要求(如每秒100个事务以上)。所有这些因素都会带来很大的代价。当然,有时我们无法回避它们,但是要切记:这里罗列的每一项都会为开发和持续维护带来开销。