轉帖|其它|編輯:郝浩|2010-11-02 11:59:39.000|閱讀 886 次
概述:Tiny Library使用應用服務層向用戶界面層提供服務,具體實現是采用Microsoft WCF Services。在Tiny Library的解決方案中,是由TinyLibrary.Services項目為整個系統提供這一WCF服務的。按照傳統的應用系統分層方法,TinyLibrary.Services項目位于領域模型層之上、用戶界面層之下,它是UI與Domain的交互界面。TinyLibrary.Services的實現中,與DDD相關的內容主要是數據傳輸對象(DTO),至于如何編寫與實現WCF服務,那是.NET技術上的問題,本文不會做太多的討論。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
Tiny Library使用應用服務層向用戶界面層提供服務,具體實現是采用Microsoft WCF Services。在Tiny Library的解決方案中,是由TinyLibrary.Services項目為整個系統提供這一WCF服務的。按照傳統的應用系統分層方法,TinyLibrary.Services項目位于領域模型層之上、用戶界面層之下,它是UI與Domain的交互界面。TinyLibrary.Services的實現中,與DDD相關的內容主要是數據傳輸對象(DTO),至于如何編寫與實現WCF服務,那是.NET技術上的問題,本文不會做太多的討論。
數據傳輸對象(Data Transferring Object,DTO)
在TinyLibrary.Services中,有一種特殊類型的對象,我們稱之為數據傳輸對象。根據Fowler在PoEAA一書中的描述,DTO用于進程間數據交換,由于DTO是一種對象,它能夠包含很多數據信息,因此,DTO的采用可以有效減少進程間數據交換的來回次數,從而在一定程度上提高網絡傳輸效率。
由于DTO需要跨越進程邊界(在我們的案例中,需要跨越網絡邊界),因此,DTO是可以被序列化/反序列化的,它不能包含上下文相關的信息(比如,Windows句柄)。正因為如此,DTO中的數據類型都是很簡單的,它們可以是原始數據類型(Primitive Data Types)或者是其它的DTO。
有些朋友在閱讀Fowler的PoEAA一書時,對于DTO的理解還是有困難,我在此將我的理解寫下來,供大家參考
在Tiny Library案例中,我選用了WCF的Data Contracts作為DTO的實現標準,因為這樣做不僅能夠基于客戶端需求設計合理的DTO結構,而且還可以利用WCF的DataContractSerializer實現DTO的序列化/反序列化。不僅如此,WCF為我們在服務器與客戶機通訊上提供了技術支持和有力保障。
打開TinyLibrary.Services項目,我們可以看到一個IDataObject的泛型接口,其定義如下:
public interface IDataObject<TEntity> where TEntity : IEntity
{
void FromEntity(TEntity entity);
TEntity ToEntity();
}
我們可以要求所有的DTO都實現這個接口,以便使其具有將實體轉換為DTO,或者將DTO轉換為實體的能力。在Tiny Library案例中,我并沒有強制要求所有的DTO都實現這個接口(也就是說,Tiny Library本身不規定DTO必須實現這個接口),我引入這個接口的目的,就是想說明,其實我們是可以這樣做的:在我們的系統中為DTO設計好一個合理的框架,以便讓DTO獲得更強大的功能。引入這個接口的另一個目的就是為了編程方便:實現基于某個實體的DTO,只需要使其實現IDataObject接口即可,Visual Studio會自動產生方法樁(Method Stubs),無需手動編寫代碼。
請注意TinyLibrary.Services.DataObjects.RegistrationData這個類,它與BookData、ReaderData有很大的區別,它并不是Registration實體的映射體現,換句話說,它所包含的所有狀態屬性,并不是與Registration實體所包含的屬性一一對應。例如,RegistrationData這個DTO中包含書名(BookTitle)以及ISBN號(BookISBN)的信息,而這些信息都是來自于Registration的關聯實體:Book。RegistrationData的設計完全是為了迎合用戶界面,因為在UI上,我們需要針對某個讀者列出他/她的借書信息,而RegistrationData包含了這所有需要的信息。
應用層職責
在《Entity Framework之領域驅動設計實踐》系列文章中,我曾經提到過,根據DDD,應用系統分為四層:展現層、應用層、領域層和基礎結構層。在Tiny Library案例中,TinyLibrary.Services充當了應用層的職責,它不負責處理任何業務邏輯,而是從更高的層面,為業務邏輯的正確執行提供適當的運行環境,同時起到任務協調的作用(比如事務處理和基礎結構層服務調用)。
public void Return(string readerUserName, Guid bookId)
{
try
{
using (IRepositoryTransactionContext ctx = ObjectContainer
.Instance
.GetService<IRepositoryTransactionContext>())
{
IRepository<Book> bookRepository = ctx.GetRepository<Book>();
IRepository<Reader> readerRepository = ctx.GetRepository<Reader>();
Reader reader = readerRepository.Find(Specification<Reader>.Eval(r => r.UserName.Equals(readerUserName)));
Book book = bookRepository.GetByKey(bookId);
reader.Return(book);
ctx.Commit();
}
}
catch
{
throw;
}
}
上面的代碼展示了“還書”操作的具體實現方式,我們可以看到,位于應用層的WCF Services僅僅是協調倉儲操作和事務處理,業務邏輯由reader.Return方法實現:
public void Return(Book book)
{
if (!book.Lent)
throw new InvalidOperationException("The book has not been lent.");
var q = from r in this.Registrations
where r.Book.Id.Equals(book.Id) &&
r.RegistrationStatus == RegistrationStatus.Normal
select r;
if (q.Count() > 0)
{
var reg = q.First();
if (reg.Expired)
{
// TODO: Reader should pay for the expiration.
}
reg.ReturnDate = DateTime.Now;
reg.RegistrationStatus = RegistrationStatus.Returned;
book.Lent = false;
}
else
throw new InvalidOperationException(string.Format("Reader {0} didn't borrow this book.",
this.Name));
}
配置文件
TinyLibrary.Services是整個案例的服務供應者(Service Provider),因此,整個系統服務器端的配置與初始化應該由TinyLibrary.Services啟動時負責執行。因此,基于服務器的系統配置應該寫在TinyLibrary.Services項目的app.config中。其中包括:Apworks的配置、Unity(或者Castle Windsor)的配置、WCF Services的配置以及Entity Framework所使用的數據庫連接字符串的設置。
從實踐角度考慮,在基于CQRS體系結構模式的應用系統中,各個組件的初始化和配置邏輯應該位于WCF Services的Global.asax文件中,以便在WCF Service Application啟動的時候,所有組件都能成功地初始化。我將在今后的CQRS案例中進一步描述這一點。
至此,Tiny Library的服務端部分基本介紹完畢,回顧一下,這些內容包括:領域建模、倉儲實現和應用服務層。下一講將簡單介紹一下Tiny Library的Web界面的設計與開發。由于本系列文章的重點不是討論某個技術的具體實現,因此,在下一講中不會涉及太多有關ASP.NET MVC的細節內容。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:網絡轉載