我们曾提到过在表现层中留下少量的逻辑代码也并非十恶不赦,若你接收这个观点,那么也没有太大的问题。否则,你可以选择用另一种不同的系统设计方式,将逻辑从表现层中彻底剥离出来,让表现层中没有任何的业务组件。
那么又该如何从表现出层触发系统的行为呢?这可以通过在表现层和业务层中间添加一个新的层来实现,这个层就是所谓的服务层。
服务层究竟是什么?
从概念上讲,应用程序中添加服务层与使用事务脚本模式类似,其差别(也是个相当大的差别)在于该层的具体实现。服务层和事务脚本的代码都将由用户界面直接调用。方法都将接收一些数据并返回另一些数据,数据也都是通过数据迁移对象(DTO)来传递的。
不过事务脚本组件的代码仅仅用来完成所需要的功能,并不会太在意于抽象或数据及方法的组织。代码中将根据需要调用所有的功能—包括工作流,数据访问代码和序列化等,但在服务层中不能如此随心所欲。
服务层实际上并不执行任何具体的工作,其功能在于组织各个业务对象。服务层非常了解业务逻辑(包括组件,工作流和服务),进而也非常了解领域模型。因此,若业务层使用表模块构造,那么服务层将通过DataSet与表适配器交互。如下表格所示就是各种模式下的选项:
业务逻辑模式 |
数据处理 |
数据对象 |
表模块模式 |
表适配器或表示数据库中数据表的对象 |
强类型DataSet |
活动记录 |
记录对象,即表示物理数据表中一行的对象 |
记录对象 |
领域模型 |
领域模型的对象 |
领域模型中的对象 |
显然,服务层不仅组织业务组件,还组织应用序专有的服务,工作流以及其他任何出现在业务逻辑中的特殊组件。
服务层的目的是什么 在设计软件时,诸如分离关注点,低耦合和高内聚等原则的重要性已经在业界反复强调了许久。在分析如何修改现有对象模型,以便进一步降低其耦合时,首先选择的方式就是添加更多的抽象。如下示例:
public class Action
{
public void Run()
{
Strategy s;
s=new Strategy();
}
}
上面示例中的Action与Strategy两个类紧密耦合,因为Action类中使用Strategy实例。若想破坏两者的依赖,我们可以添加一个中间类,让其仅暴露出Action类所需要的方法,这样这个中间类也就破坏了Action和Strategy之间的依赖关系,如下所示:
public class Action
{
public void Run()
{
IStrategy obj;
obj=StrategyFactory()
}
}
在这个设计中,我们让Strategy类完全对Action类隐藏起来,同时Strategy为Action所提供的功能仍旧可用,这正是中间类的作用。中间的工厂类提供了一个通用的
接口,让调用者仍旧可以使用接口暴露出的方法,而无需关注架构或底层类中发生了怎样的变化,简单而又有效。
那么这又和服务层模式有什么关系呢?
服务层模式的原理与其非常类似,只不过它将工厂模式应用到了一个更高层面的抽象之上。服务层位于系统中两个互相通信的逻辑层之间,使两个层能够在松散耦合并优美地彼此分离开的同时,仍旧可以完美地彼此通信。大多数时候,服务层模式用来定义表现层和业务逻辑层之间的边界,不过这也仅仅是个最常见的场景而已,这个模式本身还可以更加广泛地应用。
组织系统的行为每个用户驱动的交互的核心都包括两个主要的参与者:表现层实现的用户界面和服务层实现的用来响应用户操作的模块,这就说明服务层不仅用来组织业务逻辑,也许还要与持久化层进行交互。
所有的交互都源于表现层,并从服务层中获取响应,根据接收到的输入,服务层将组织业务逻辑层中的组件—包括服务,工作流和领域模型中的对象,并根据需要调用数据访问层。
系统中仅有服务层会发送数据库操作请求吗?也许还有其他情况,业务逻辑中也可能包含一些工作流,或业务服务需要使用到数据访问层。业务逻辑层中唯一需要完全和数据库细节分离的部分就是领域模型。
服务层是表现层和其他层之间边界,理论上服务层应该通过数据迁移对象与表现层交互,并在内部根据需要将其转换成领域模型类。服务层中暴露的每个方法都组织使用到其他服务,包括工作流,以及通过数据映射器或对象/关系映射器支持的语言执行数据库操作。
注意:使用专门的数据类型,例如,数据迁移对象向服务层传入/传出数据仅是个理想的场景,而在现实中不总是那么适合。若你有数千(甚至是只有数百)个领域对象或操作,那么映射数据迁移对象都会是一个不小的工作,在这种情况下,我们不难见到这个看起来很好的设计会经常屈服于实际技术上的困难。最常见的替代做法是放弃为每个操作指定专门的数据迁移对象,而是为每个领域实体仅提供一个数据迁移对象(实际上就是把领域类中的所有行为方法移除),或者直接使用领域实体。客户端是桌面程序,那么服务层一般会部署在另一个物理层上,并需要远程访问。这个做法与软件+服务架构中的做法一样,其中客户端仅包含GUI,也就是说,整个应用逻辑都是远程的。若客户端使用了Silverlight 2且服务层发布?