分層架構中的服務層-服務層實戰

作者: Virus-BeautyCode  來源: 博客園  發布時間: 2011-05-26 12:09  閱讀: 4482 次  推薦: 1   原文鏈接   [收藏]  

  引言

  服務層是在交互的兩個層中間又定義了另外一個層,典型的是在表現層和業務邏輯層之間。這個中間層只是實現應用的用例的類集合。

  服務和面向服務的出現,使得整個解決方案更有價值、更加成功。與表現層相比,服務層提供了松散的耦合,服務層提供商定的協議,可重用性,跨平臺的部署。服務向其他類一樣,允許你調整你需要的抽象總數。

  真實世界的表現層,主要是用戶前端。用戶做的每一件事都通過表現層和用戶界面。

  企業級的應用,可能會有多種數據表現接口。一個接口可能就是一個用戶界面,也可能是每一個支持的平臺,例如:移動應用、Web、WPF、Windows、Silverlight,或者是其他軟件平臺。另一個接口可能是后端應用,傳入數據或者是獲取數據,并且轉變他。可能還有一個接口是一個使用應用的連接者代理-系統整合方案中的內部處理邏輯。

  服務層響應來自表現層的輸入。相應的,表現層不關心另外一端的操作和模塊。重要的是模塊聲明了它能做什么。

  表現層和服務層都不包含業務邏輯。表現層只知道服務層提供的粗粒度接口,服務層只知道一系列可行的相互作用的協議,處理本質的細節:事務,資源管理,協調,數據消息。

  服務層在現實生活中的例子

  SOA的出現,與服務層的出現一致,加強了服務層的概念,使他更吸引人。一些人爭論說在多層架構中使用SOA是有創造力的。爭論這些是毫無意義的,就好像爭論是先有雞,還是先有蛋。

  在實際的使用中,使用服務層的目的,背后的理由,很多程序員和架構師還未能理解。下面,讓我們分析一個現實生活中的例子。

  我們中的很多人都有做初級程序員的經歷。在某些時候,我們還會碰到一個目中無人的老板。老板可能會說:嗨,我們需要馬上為客戶定制一個系統。

  你聽到了嗎?老板就是表現層。老板關心的是向經理人發送一個簡單的命令。在他的眼里,經理人暴露了一個任務和責任的列表。老板不關心經理人實際如何完成任務,但是他知道公司和經理人之間的協議中規定的經理人應該做什么(協議中也會提到,如果經理人不滿足要求,就會被替換掉)。經理人就是服務層。最終,經理人協調其他資源來完成這個任務。

  如果你左右看看,在現實生活中你會找到很多服務層模式的例子。例如:孩子向父母要零花錢,編輯要求修改文稿,你從ATM中取現金,等等。

  什么時候使用服務層

  在任何有點復雜的應用中都應該使用服務層。如果在一個簡單的文檔管理系統,或者是一個快速建立的網站,可能只是存在幾個星期的網站中建立服務層很可能會沒有回報。

  在一個分層系統中,沒有理由不使用服務層。一個可能的例外就是簡單的前端和一系列只是滿足用例的應用服務。在這種情況下,服務層很可能只是一個發報機,沒有任務組合工作。簡單的服務層還不如直接調用業務邏輯層。

  相反的,在你擁有多個前端,而且是大量的應用邏輯,將應用邏輯存放在一個地方,而不是在每個應用接口中都保留副本會更加好。

  服務層的優點

  服務層增加抽象,解耦兩個交互的層。在任何你想獲得一個更好的系統的時候,你都應該構建一個服務層。服務層使用粗粒度的遠程接口最小化表現層和業務層之間的通信次數。

  通過服務(例如:WCF)來實現服務層的時候,你能感覺到其他的好處,例如:通過配置來改變綁定信息。

  服務層的缺點

  因為抽象是服務層的主要優點,對于簡單的系統可能有點過頭了。

  服務層不是必須使用例如WCF這樣的服務技術。在ASP.NET中的表現層,你可以把code-behind類叫做服務層。這時候使用WCF代替普通的類,可能有點過頭了,很可能會降低性能。考慮在你的系統中使用WCF,需要考慮性能,如果性能下降的無法忍受,請選擇其他服務層技術。

  服務層適用于什么樣的場景

  表現層調用服務層。是一個遠程調用,還是一個本地調用?

  Martin 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信息傳到業務邏輯層中,也是不錯的。

1
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()