1、編排業務邏輯的模式
1. 事務腳本模式TS(The Transaction Script pattern ) git
TS 鼓勵你跳過任何的面向對象的設計,你直接到所需的用戶操做的業務組件映射。專一於的業務用戶能夠經過表示層完成,併爲每一個請求編寫方法。這個方法被稱之爲事務腳本,此處事務一般是指想要進行商業交易,腳本是指系統中的一系列關係用戶操做的系統操做。 github
TS歷時多年仍然不過期的緣由只有一個:它基於推行可視化的業務邏輯設計,而可視化正上用戶體驗的核心。 web
在TS中,每一個用戶操做都在上下文的物理事務邊界中完成。不一樣於數據訪問層的數據執行腳本,TS的數據訪問一般被封裝在數據個組件中。按照設計,TS不包含使用面向對象設計。TS中的任務邏輯都是If,while,for等組成。 數據庫
在實現中每一個TS都是一個單獨的,可能表現爲一個類的靜態方法,能夠在每一個類本身執行TS。當你這樣作的時候,就已經完美演繹了命令模式。 設計模式
命令模式的主要設計思想是用一個對象來表示動做。命令對象封裝一個動做和它全部的參數。典型的,命令對象公開的標準接口,以便調用方能夠調用任何命令,而無需關注命令類的具體行爲。如: api
public interface IApplicationCommand 架構
{ mvc
int Run(); app
} asp.net
public class BookHotelRoom : IApplicationCommand
{
Customer _guest;
DateTime _checkIn, _checkOut;
String _confirmationNumber;
// other internal members
public BookHotelRoom(Customer guest, DateTime checkIn, DateTime checkOut)
{
_guest = guest;
_checkIn = checkIn;
_checkOut = checkOut;
}
public String ConfirmationNumber
{
get { return _confirmationNumber; }
}
public int Run()
{
// Start the transaction
// Check room availability for requested stay
// Check customer information?(already guest, payment method, preferences)
// Calculate room rate
// Add a new record to the Bookings database table
// Generate the confirmation number
// Commit the transaction???????
// E-mail the customer
// Store the confirmation number to the local member _confirmationNumber
}
...
}
TS模式不對任務數據設計權限,不也會對數據作任何轉換。它只用用於接收和發送數據,因此一般的作法是使用數據類(Dto)來傳遞數據。
2. 域模型模式(The Domain Model pattern)
域模型(DM)一般在DDD設計中使用,但也不侷限於它。DM也是一個經常使用的設計模式,它讓架構師側重於系統的預期行爲和運行時的數據流。
域模型是不一樣於對象模型的一系列集合。域模型忠實於表示業務領域,特別是域內進程的數據流。
域模型是一系列表示業務領域的plain old classes。這些類是數據容器,能夠表現爲屬性也能夠是方法。The term POCO (Plain Old CLR Object)一般指這些域模型。
實際使用咱們不關注類自己,關注的是行爲和事件。這些行爲能幫助你理解在類上發生了什麼。着眼於行爲比着眼於類屬性更有用,另外一方面看來,這就是Tell-Don't-Ask原則的體現。
有些時候域模型須要被持久化,而持久化不是域模型的職責,持久化將在基礎結構層才能被實現。從應用程序看來域模型是業務邏輯數據庫,不依賴任何接口,而持久化依賴接口,使用關係模型來持久化。域模型和關係模型一般要經過ORM工具來完成,如Microsoft's Entity Framework 和 NHibernate
3.反域模式ADM(The Anemic Domain Model (anti-)pattern)
域模式的核心於對象關聯的行爲,"行爲先行"是違背域模型設計原則的,因此產生了另一種模式,反域模式(ADM)。
ADM域模型中,全部對象仍然都遵循命名約定的真實世界域實體、 實體之間的關係仍存在,和模型的總體結構緊密匹配的真實域空間。沒有行爲,只有屬性的實體。
ADM模式建議不要在域對象裏聽任何邏輯。全部的邏輯都被放在邏輯領域的服務組件裏。這此服務是域模型的消費者,而且有存儲和持久化的功能。
■ Analysis leads to objects that closely model the domain space and are tailored to the real
needs
■ The strong domain perspective of things reduces the risk of misunderstandings between the
domain experts and the development team
■ Code is likely to become more readable and easily understandable
■ The fnal result has more chances to really be close to expectations.
2、將關注點從數據轉移到任務
1. Asp.net Mvc中的任務
用戶行爲最終被實現爲controller類中的一個方法調用。controller應該是被用來組織任務的首選。抽象的說,Contrller與WebForm中的postback事件沒有區別。在這兩個方法中,聰明的開發人員都會爲了不web form中的完整的事件執行而選擇使用Controller。
Responsibility-Driven Design (RDD)職責驅動設計由Rebecca Wirfs-Brock 和Alan McKean提出:Roles, Responsibilities, and Collaborations。RDD的本質是將系統分解成一系統可執行的Action動做,而後每一個action被映射到一個組件類。執行action被設計成組件類的職責。組件的解決依賴於它設定的職責。
Asp.net mvc 的controller是RDD模式的一個生動的實現,RDD協調者建議將action分組到一系統application services中,由controller 調用services來執行action返回view model
public ActionResult PlaceOrder(OrderInputModel orderInfo)
{
// Input data already mapped thanks to the model binding
// infrastructure of ASP.NET MVC
// Perform the task invoking a application service and
// get a view model back from application layer
var service = new OrderService();
var model = service.PlaceOrder();
// Invoke next view
return View(model);
}
controller負責展示層和應用層的協調調用,這種狀況下若是須要不一樣的展示層,能夠用如下代碼輕鬆實現:
var service = new OrderService();
var model = service.PlaceOrder();
// Adapt to the new view model
var newFrontendModel = someAdapter.NewViewModel(response);
應用層和展示層的鏈接點是controller,使用依賴倒置模式Ioc容器如microsoft unity來重寫asp.net mvc的controller factory:
var factory = new UnityControllerFactory();
ControllerBuilder.Current.SetControllerFactory(factory);
UnityControllerFactory實現以下:
public class UnityControllerFactory : DefaultControllerFactory
{
public static IUnityContainer Container { get; private set; }
public UnityControllerFactory()
{
Container = new UnityContainer(); // Initialize the IoC
Container.LoadConfiguration(); // Configure it reading details from web.config
}
protected override IController GetControllerInstance(RequestContext context, Type type)
{
if (type == null) return null;
return Container.Resolve(type) as IController;
}
}
而後每一個controller類的默認構造函數要改爲以下:
public class HomeController
{
private IHomeService _service;
public HomeController(IHomeService service)
{
_service = service;
}
...
}
You can use the Unity Mvc library available as a NuGet package For more information, see http://github com/feedbackhound/Unity Mvc5
一樣的問題存在於數應用層和基礎架構層的數據訪問。使用相同的方法——依賴注入。在域模型場景中的數據訪問邏輯容器一般被命名爲repository
public class HomeService
{
private ISomeEntityRepository _someEntityRepo;
public HomeService(ISomeEntityRepository repo)
{
_someEntityRepo = repo;
}
...
}
有意思的是,你不須要在代碼中修改初始化controller來設置factory,你只要確保倉儲類型和接口都映射到Ioc容器裏就能夠。全部Ioc容器都提供配置和api兩方式來映射類型。因此不須要再經過代碼來配置,如今的IoC容器都提供了這種透明的方式來解決依賴關係。若是你通知IoC容器獲取一個Controller類型,這個controller類型依賴一些Application Services類型,反過來,Services類型又依賴一個或多個倉儲類型,全部這些代碼都在一行代碼裏解決。
層與層之間的鏈接通訊建議使用依賴注入實現,但它不是惟一的解決方案。若是你有多個層在同一個進程空間內,比較簡單的方法是直接而且本地實例化對象,不論他們在application layers、servces layers或是respositories layers. 缺點就是他們會在層之間產生高偶合。
// Tight coupling between the class OrderService
// and the class that contains this code
var service = new OrderService();
var model = service.PlaceOrder();
2. 編排域內的任務
一般、應用程序的邏輯層包含了任務編排、域邏輯和一些其它的東西。
域名服務一般包含業多個域實體操做的業務邏輯。大多狀況下,域名服務是複雜、 多步的操做,都在域的邊界內進行,並有多是基礎設施層(infrastructure)。典型的例子是OrderProcessor, BestPriceFinder, 或GoldCustomerEvaluator這樣的域服務。服務的命名以功能命名,而且可讓領域專家和相關人員容易理解。
3、跨邊界傳遞數據
表示層物理邊界跨界,不管是進程跨界仍是物理計算機跨界都是一項很昂貴的操做。訪問一個遠程計算機的效率可能要比進程內通訊慢100倍。
1.層架構間的數據流
下圖表示分層架構一個抽象的數據流。當執行一個命令執行時,數據流從用戶接口以一個input model形勢進入應用層。鑑於這個請求動做,應用程序層須要一系列用來作爲input model的域模型實例。在一個領域分層系統中,持久性特指將轉換成物理模型,媽實體關係模型(relational-data model),在返回結果時將data mode再轉換爲demain model。
理論上講,一個分層的系統由上圖的4種邏輯分離的model組成,但在有的狀況下他們是一致的。data model一般與demain model在基礎設施層是一致的。在Asp.net mvc應用程序中,input model與output model一般是同一個model。
在遵循域模型配置設計的架構中,域實體與數據緊密相關,推薦將域模型向表示層冒泡,而且在必要的時候將他們序列化來傳輸。
在一個解決方案中不多能讓一個實知足整個系統的須要,畢竟不一樣的層可能對數據要求不同。有些狀況下,爲了便捷使在全部層中都使用了demain model,有些狀況下須要使用Dto類,總之、跟據你的須要。
Dto被設計爲用來作物理層之間的數據載體。Dto類只有屬性,沒有行爲。Dto原生能夠被序列化,在遠程組件調用中一般這樣作。
典型的DTO使用如:顯示和處理一個自定義訂單。在處理訂單時須要大量的數據,顯示數據時只須要簡單少許數據。DTO增長了層次的複雜度,可是減小了容器的複雜度。
Data model 與DTO之是能夠經過AutoMapper適配器完成自動轉換。
Mapper.CreateMap<YourSourceType, YourDtoType>();
Once a mapping is defned, you invoke it via the Map method:
var dto = Mapper.Map<YourDtoType>(sourceObject);