在 2010 年 7 月刊的《MSDN 杂志》中,我开始介绍为借阅图书馆构建智能客户端应用程序 的过程。 我将该项目命名为 Alexandria,并决定使用 NHibernate 进行数据访问,使用 Rhino 服务总线实现与服务器之间的可靠通信。
NHibernate (nhforge.org) 是一个对象关系映射 (O/RM) 框架,而 Rhino 服务总线 (github.com/rhino-esb/rhino-esb) 是构建在 Microsoft .NET Framework 上的开源服务总线 实施。 我恰巧参与了这两个框架的深层开发工作,这样就有机会利用我熟悉的技术来实施项目 ,同时为需要了解 NHibernate 和 Rhino 服务总线的开发人员提供一个工作范例。
在上一篇文章中,我介绍了智能客户端应用程序的基本构造块。 我设计了后端以及智能客 户端应用程序和后端之间的通信模式。 此外,我还略微谈到了如何管理事务和 NHibernate 会 话,如何使用和回复来自客户端的消息,以及如何将所有内容融入引导程序。
在本期内容中,我将介绍在后端和智能客户端应用程序之间发送数据的最佳做法,以及分布 式更改管理的模式。 在此过程中,我将介绍其余的实施细节,并为 Alexandria 应用程序提供 一个完整的客户端。
您可以从 github.com/ayende/alexandria 下载示例解决方案。 该解决方案包含三部分: Alexandria.Backend 包含后端代码;Alexandria.Client 包含前端代码; Alexandria.Messages 包含在前两者之间共享的消息定义。
没有单一模型规则
在编写分布式应用程序时,人们最常提出的问题之一是:如何将我的实体发送到客户端应用 程序,然后在服务器端应用更改集?
如果这是您的问题,则您可能是在思考一种主要将服务器端作为数据存储库的模式。 如果 构建此类应用程序,则可以选择使用一些技术来简化此项任务(例如,采用 WCF RIA 服务和 WCF 数据服务)。 不过,使用迄今为止我所概括的体系结构类型时,对于在网络上发送实体实 际上毫无作用。 事实上,Alexandria 应用程序对相同的数据使用了三种不同的模型,其中每 种模型分别最适合应用程序的不同部分。
后端的域模型用于查询和事务性处理,适合与 NHibernate 一起使用。如需进一步优化,可 以拆分查询和事务性处理职责。 消息模型表示网络上的消息,包括与域实体非常接近的一些概 念(示例项目中的 BookDTO 是 Book 的数据克隆)。 在客户端应用程序中,视图模型(类似 于 BookModel 类)将进行优化以便绑定到 XAML 并处理用户交互。
虽然乍看起来这三种模型(Book、BookDTO 和 BookModel)之间有许多共性,但事实上它们 具有不同的职责,这意味着如果尝试将所有这些职责都融入一种模型,则会创建一个繁琐、臃 肿且不通用的模型。 通过按一系列职责拆分模型,我可以使工作变得更简单,因为我可以按其 自身的用途优化每种模型。
从概念性的角度来看,需要为每个用途创建单独模型还有其他原因。 对象是数据和行为的 组合,但当您尝试通过网络发送对象时,则只能发送数据。 这会引出一些有趣的问题。 您会 将应在后端服务器上运行的业务逻辑放在何处? 如果将此逻辑放在实体中,则在客户端执行它 时会发生什么情况?
这种体系结构的最终结果就是您使用的不是真正的对象。 您使用的数据对象是只保存有数 据的对象,而业务逻辑则以针对对象数据运行的过程的方式驻留在其他位置。 由于这会导致逻 辑和代码的分散,更加难以长期维护,因此不赞成这样做。 无论您如何看待此问题,您都需要 在应用程序的不同部分中使用不同的模型,除非后端系统是简单的数据存储库。 这自然又会引 出一个十分有趣的问题:您将如何处理更改?
针对更改集的命令
我允许用户在 Alexandria 应用程序中执行的操作包括:将书籍添加到他们的队列、在队列 中对书籍进行重新排序以及从队列中完全删除书籍,如图 1 中所示。 这些操作需要同时在前 端和后端反映出来。
图 1 对用户的书籍队列可以执行的操作