代码改变世界

分层架构中的服务层-服务层实战

2010-09-06 12:14  Virus-BeautyCode  阅读(5545)  评论(9编辑  收藏  举报

  引言

  服务层是在交互的两个层中间又定义了另外一个层,典型的是在表现层和业务逻辑层之间。这个中间层只是实现应用的用例的类集合。

  服务和面向服务的出现,使得整个解决方案更有价值、更加成功。与表现层相比,服务层提供了松散的耦合,服务层提供商定的协议,可重用性,跨平台的部署。服务向其他类一样,允许你调整你需要的抽象总数。

  

  真实世界的表现层,主要是用户前端。用户做的每一件事都通过表现层和用户界面。

  企业级的应用,可能会有多种数据表现接口。一个接口可能就是一个用户界面,也可能是每一个支持的平台,例如:移动应用、web、WPF、windows、Silverlight,或者是其他软件平台。另一个接口可能是后端应用,传入数据或者是获取数据,并且转变他。可能还有一个接口是一个使用应用的连接者代理-系统整合方案中的内部处理逻辑。

  服务层响应来自表现层的输入。相应的,表现层不关心另外一端的操作和模块。重要的是模块声明了它能做什么。

  表现层和服务层都不包含业务逻辑。表现层只知道服务层提供的粗粒度接口,服务层只知道一系列可行的相互作用的协议,处理本质的细节:事务,资源管理,协调,数据消息。

  服务层在现实生活中的例子

  SOA的出现,与服务层的出现一致,加强了服务层的概念,使他更吸引人。一些人争论说在多层架构中使用SOA是有创造力的。争论这些是毫无意义的,就好像争论是先有鸡,还是先有蛋。

  在实际的使用中,使用服务层的目的,背后的理由,很多程序员和架构师还未能理解。下面,让我们分析一个现实生活中的例子。

  我们中的很多人都有做初级程序员的经历。在某些时候,我们还会碰到一个目中无人的老板。老板可能会说:嗨,我们需要马上为客户定制一个系统。

  你听到了吗?老板就是表现层。老板关心的是向经理人发送一个简单的命令。在他的眼里,经理人暴露了一个任务和责任的列表。老板不关心经理人实际如何完成任务,但是他知道公司和经理人之间的协议中规定的经理人应该做什么(协议中也会提到,如果经理人不满足要求,就会被替换掉)。经理人就是服务层。最终,经理人协调其他资源来完成这个任务。

  如果你左右看看,在现实生活中你会找到很多服务层模式的例子。例如:孩子向父母要零花钱,编辑要求修改文稿,你从ATM中取现金,等等。

  什么时候使用服务层

  在任何有点复杂的应用中都应该使用服务层。如果在一个简单的文档管理系统,或者是一个快速建立的网站,可能只是存在几个星期的网站中建立服务层很可能会没有回报。

  在一个分层系统中,没有理由不使用服务层。一个可能的例外就是简单的前端和一系列只是满足用例的应用服务。在这种情况下,服务层很可能只是一个发报机,没有任务组合工作。简单的服务层还不如直接调用业务逻辑层。

  相反的,在你拥有多个前端,而且是大量的应用逻辑,将应用逻辑存放在一个地方,而不是在每个应用接口中都保留副本会更加好。

  服务层的优点

  服务层增加抽象,解耦两个交互的层。在任何你想获得一个更好的系统的时候,你都应该构建一个服务层。服务层使用粗粒度的、远程接口最小化表现层和业务层之间的通信次数。

  通过服务(例如:WCF)来实现服务层的时候,你能感觉到其他的好处,例如:通过配置来改变绑定信息。

  服务层的缺点

  因为抽象是服务层的主要优点,对于简单的系统可能有点过头了。

  服务层不是必须使用例如WCF这样的服务技术。在ASP.NET中的表现层,你可以把code-behind类叫做服务层。这时候使用WCF代替普通的类,可能有点过头了,很可能会降低性能。考虑在你的系统中使用WCF,需要考虑性能,如果性能下降的无法忍受,请选择其他服务层技术。

  服务层适用于什么样的场景

  表现层调用服务层。是一个远程调用,还是一个本地调用?

  Flower关于分布式对象设计的推荐是:不要分散你的对象。我们可以理解为:“除非是必须的,或者是有好处”。就想你所知道的,必要性和好处实际容易变化的,难以量化,但是在一些特殊的方案中,他们很容易识别。

  因此,在什么场景适合服务层呢?通常来说,如果你有一个服务层,可以很容易的跨层移动,那是一件好事。在这点上,例如WCF这样的服务技术是一个正确的工具。

  如果客户端是web网页,服务层最好是位于本地的web服务器上。如果站点成功了,你可以将服务层分离到独立的应用服务器,来增加扩展性。

  如果客户端是桌面应用,服务层会部署到一个独立的物理层,并且通过远程来访问。这个方法类似于Software+Services的架构,客户端除了GUI什么都没有,全部的应用逻辑都在远端。如果客户端使用Silverlight,服务层会发布在Internet上,你可以建立一个完美的RIA(Rich Internet Application)应用。

  实战服务层模式

  实现服务层依赖于两个技术选择。第一个选择就是那什么方法或者是调用来作为服务层的基础。使用普通的类还是服务?如果选择服务,又该选择哪种服务的实现技术?在windows或者是.NET平台,你的选择比较少。你可以选择WCF service、asp.net xml web service,或者是类似于REST之类的服务。

  如果你对于.NET框架有一些了解,你应该知道创建一个wcf service或者是web service,就好像创建普通的类,然后添加一些attribute。当然,还有很多细节需要考虑,例如:web service的WSDL(web service description language)web服务描述语言,WCF的配置和数据协议。服务最终是一个包含其他内容的类。

  设计服务层的类

  服务层使用的类应该暴露一个协议,无论是WCF的协议还是实现接口。实现接口是一个比较好的做法,因为它更清晰的描述了一个类可以做什么,将会做什么。接口使用DTO接收和返回数据,推荐粗粒度的方法,以便它可以最小化网络传输,最大化网络吞吐量。

  如何将需要的方法映射为接口和类呢?在用例的基础上,列出一系列所需的方法,然后将他们分为逻辑组。每个组建立自己的服务或者是类。

  大多数情况,你的结果是问题域的每个实体建立一个服务类,OrderService,CustomerServcie等等。这些都是应用需要的。但是,如果用户的行为相对比较小,行为比较相似,这时候一个服务类可能就够用了。否则,一个单一的服务类会迅速变大,会很难以维护和变化。

  通常来说,我们认为没有严格定义的的准则,例如:每个实体都要有自己的service,或者是一个service需要满足用户所有实体。服务层在系统的表现层和其他部分之间进行调节。服务层包括了粗粒度的服务(也是用例驱动的),在他们的编程接口中,实现了用例。

  服务层和系统的其他部分相比,是独立的,对于表现层来说只是一个调用内部处理流程的接口。如果用例有变化,你很可能只是修改服务层而不用修改业务逻辑。在一个相对较大的应用中,对于服务层的编程接口来说,你应该先看一下你的用例,然后使用通用的方法组织类中的方法。

  实现一个服务层的类

  我们推荐每个类应该实现一个接口。如果你选择WCF,这是严格要求的,而且从整体上来讲是一个好方法。

 

代码
[ServiceContract (Namespace="http://www.dsn.com/wcf")]
    
public   interface IOrderService
    {
        [OperationContract]
        
bool  Submit(BeautyCode.Entity.Order order,BeautyCode.Entity.CommunUser user,out BeautyCode.Entity.CException exception);
        
        [OperationContract]
        List
<BeautyCode.Entity.Order> FindOrders(BeautyCode.Entity.OrderFind find, BeautyCode.Entity.CommunUser user, out BeautyCode.Entity.CException exception);
        
        [OperationContract]
        BeautyCode.Entity.Order GetByOrderSeqNo(
string orderSeqNo, BeautyCode.Entity.CommunUser user, out BeautyCode.Entity.CException exception);
    }

 

 

 

 

代码
[ServiceBehavior (InstanceContextMode= InstanceContextMode.PerCall )]
    [AspNetCompatibilityRequirements (RequirementsMode
=AspNetCompatibilityRequirementsMode .Allowed )]
    
public  class OrderService:ServiceBaseImpl  , IOrderService
    {

        
private void  ThrowException(BeautyCode.Entity.CommunUser user)
        {
            
        }


        
public bool Submit(BeautyCode.Entity.Order order, BeautyCode.Entity.CommunUser user, out BeautyCode.Entity.CException exception)
        {
            
throw new NotImplementedException();
        }

        
public List<BeautyCode.Entity.Order> FindOrders(BeautyCode.Entity.OrderFind find, BeautyCode.Entity.CommunUser user, out BeautyCode.Entity.CException exception)
        {
            
throw new NotImplementedException();
        }

        
public BeautyCode.Entity.Order GetByOrderSeqNo(string orderSeqNo, BeautyCode.Entity.CommunUser user, out BeautyCode.Entity.CException exception)
        {
            
throw new NotImplementedException();
        }
    }

 

 

   首先假设接口直接使用领域模型对象。在上面的例子中的Order类,代表我们在领域模型中建立的order实体。如果我们使用实际的领域模型对象,我们假设在业务逻辑中使用领域模型的模式。如果你使用数据表模型的模式,上面代码中的order类应该替换为DataTable。我们一会在回到DTO的讨论上来。

  submit方法需要整合应用内部的服务,检查用户的账户状态,检查订单中商品的有效性,同步厂商的商品信息。submit方法是一个典型的服务层方法,它对表现层提供了单一的协议,与不同的领域模型和业务逻辑进行多个步骤的操作。

  FindOrders方法返回一个order集合,GetByOrderSeqNo返回一个特定的order。此外,假定我们在业务逻辑层使用领域模型的模式,没有专门的数据传输对象。很多的架构师推荐在服务层不出现Create、Read、Update、Delete(CRUD)方法。FindOrders和GetByOrderSeqNo方法本质上来说就是CRUD中的Read方法。

  而且,方法依赖于你的用例。如果用例中有用户点击一个地方显示订单列表,或者是单个订单的详细信息的需要,那么这些方法就必须要有。

  

  处理角色和安全

  FindOrders方法应该只是返回当前用户可以看到的订单。

  如果你把安全当回事来考虑的话,就应该在服务层的每一个方法中检查调用者的身份,对未授权的用户拒绝方法的调用。

  如果你不想在每个方法中重复验证用户的身份,那就需要在服务层的方法上面添加attribute来实现身份验证。

  服务层起到一个看门人的作用,通常不需要将role信息传输到业务逻辑层中去验证,除非有好的理由。但是,如果有这么一个好的理由,使得你不得不将role信息传到业务逻辑层中,也是不错的。