天天看點

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

引言

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

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

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

  企業級的應用,可能會有多種資料表現接口。一個接口可能就是一個使用者界面,也可能是每一個支援的平台,例如:移動應用、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資訊傳到業務邏輯層中,也是不錯的。

繼續閱讀