寫在前面

閱讀目錄:
- 概念中的了解
- 代碼中的實作
- 後記
掀起了你的蓋頭來,讓我看你的眼睛,你的眼睛明又亮呀,好像那水波一模樣;掀起了你的蓋頭來,讓我看你的臉兒,看看你的臉兒紅又圓呀,好像那蘋果到秋天。。。
Hi,Unit Of Work,掀起你的蓋頭來,原來 You are so beautiful !
Unit Of Work:維護受業務事務影響的對象清單,并協調變化的寫入和并發問題的解決。即管理對象的CRUD操作,以及相應的事務與并發問題等。Unit of Work是用來解決領域模型存儲和變更工作,而這些資料層業務并不屬于領域模型本身具有的。
關于Unit Of Work的更多詳情,請檢視:http://martinfowler.com/eaaCatalog/unitOfWork.html,Unit Of Work中的“Unit”是單元的意思,知道單元測試的朋友都知道其也包含“Unit”單詞,但是是一個意思嗎?Unit Test(單元測試)雖然也包含“Unit”這個單詞,但是意義并不是一樣,單元測試中的“Unit”可以看做是最小單元,比如組裝飛機的最小零部件,但是Unit Of Work(工作單元)并非無此,注意後面“Work”單詞,意思是可以“工作”的單元,比如一場籃球比賽需要兩個隊,10名上場球員參與,這樣完成的“動作”才會稱之為籃球比賽,也就是“工作單元”,一個籃球隊或是一個籃球隊員并不能完成或稱為籃球比賽,但是這個工作的"單元"也隻是相對而言,比如上籃動作就隻需要一個籃球隊員就可以完成,那這個籃球隊員就可以看做是“工作單元”。需要注意的是,Unit中可以包含很多“動作”,可以是一個也可以是多個,比如上面的例子,如果“單元”中包含對于多個動作,那這個“單元”中所有的動作都是“内聚”的,脫離這個“單元”這個動作就沒有意義了,比如籃球比賽中的一次吹罰,當然這隻是字面上了解的意思,也隻是我個人的一些看法,希望看到着沒有被我忽悠到。
扯了一些不沾邊的東西,我們再看一個現實中例子,也最能說明Unit Of Work所包含的意思,就是銀行轉賬操作,包含兩個動作:轉出方扣錢和轉入方加錢,這兩個動作要麼都完成,要麼都不完成,也就是事務操作,完成就Commit(送出),完不成就Rollback(復原)。
回到Unit Of Work的定義,Unit of Work是用來解決領域模型存儲和變更工作,在ORM進行持久化的時候,比如Entity Framework的SaveChanges操作,其實就可以看做是Unit Of Work,也就是定義中所說“用來解決領域模型存儲和變更工作”,但是如果項目是基于Entity Framework進行DDD(領域驅動設計)開發設計的,那Entity Framework中的Domain Model就必然包含業務邏輯,這就不符合“而這些資料層業務并不屬于領域模型本身具有的”,也就是說Unit Of Work必須獨立于Domain Layer(領域層),注意獨立的業務是“資料層”業務,并不是業務場景中的“業務”,比如“轉賬業務”,轉出方扣錢和轉入方加錢這個業務就屬于“資料層業務”,有的人會把Unit Of Work放在Domain Layer(領域層)中,其實是有些不恰當的,應該是放在Infrastructure Layer(基礎層)中,但其實也隻是相對而言,如果涉及到具體的業務單元子產品,具體實作可以放在領域層中。
在DDD(領域驅動設計)開發設計中,Unit Of Work的使用一般會結合Repository(倉儲)使用,有關Repository可以參閱dudu的一篇文章:http://www.cnblogs.com/dudu/archive/2011/05/25/repository_pattern.html,文中的解釋很清楚直白:
Repository:是一個獨立的層,介于領域層與資料映射層(資料通路層)之間。它的存在讓領域層感覺不到資料通路層的存在,它提供一個類似集合的接口提供給領域層進行領域對象的通路。Repository是倉庫管理者,領域層需要什麼東西隻需告訴倉庫管理者,由倉庫管理者把東西拿給它,并不需要知道東西實際放在哪。
Unit Of Work所做的工作可以看做是收集Repository出入庫的“商品”,便于一次裝車,運輸過程中如果沒有出現問題,那這車的所有“商品”就安全到達,如果出現問題,那這車的所有“商品”全部打回,這輛車就是“單元”的意思。
關于Repository和Unit Of Work的關系,簡單畫了個示意圖:
點選檢視大圖
關于Unit Of Work項目中的應用,可以參照dax.net的Byteart Retail項目,本人現在也正在學習中,項目是基于DDD設計實作的,下面是IUnitOfWork的示例代碼:
1 namespace ByteartRetail.Domain
2 {
3 /// <summary>
4 /// 表示所有內建于該接口的類型都是Unit Of Work的一種實作。
5 /// </summary>
6 /// <remarks>有關Unit Of Work的詳細資訊,請參見UnitOfWork模式:http://martinfowler.com/eaaCatalog/unitOfWork.html。
7 /// </remarks>
8 public interface IUnitOfWork
9 {
10 /// <summary>
11 /// 獲得一個<see cref="System.Boolean"/>值,該值表述了目前的Unit Of Work事務是否已被送出。
12 /// </summary>
13 bool Committed { get; }
14 /// <summary>
15 /// 送出目前的Unit Of Work事務。
16 /// </summary>
17 void Commit();
18 /// <summary>
19 /// 復原目前的Unit Of Work事務。
20 /// </summary>
21 void Rollback();
22 }
23 }
根據UnitOfWork中的概念描述“這些資料層業務并不屬于領域模型本身具有的”,是以IUnitOfWork放在Infrastructure Layer(基礎設施層),其實IUnitOfWork的具體管理實作是放在領域層的,但不會放在Domain Model(領域模型)中,具體的資料層業務會結合Repository,也就是說IUnitOfWork會貫徹所有的Repository實作,因為它要對所有倉儲的的持久化做統一管理:
1 /// <summary>
2 /// Represents that the implemented classes are repository contexts.
3 /// </summary>
4 public interface IRepositoryContext : IUnitOfWork, IDisposable
5 {
6 /// <summary>
7 /// Gets the unique-identifier of the repository context.
8 /// </summary>
9 Guid ID { get; }
10 /// <summary>
11 /// Registers a new object to the repository context.
12 /// </summary>
13 /// <typeparam name="TAggregateRoot">The type of the aggregate root.</typeparam>
14 /// <param name="obj">The object to be registered.</param>
15 void RegisterNew<TAggregateRoot>(TAggregateRoot obj)
16 where TAggregateRoot : class, IAggregateRoot;
17 /// <summary>
18 /// Registers a modified object to the repository context.
19 /// </summary>
20 /// <typeparam name="TAggregateRoot">The type of the aggregate root.</typeparam>
21 /// <param name="obj">The object to be registered.</param>
22 void RegisterModified<TAggregateRoot>(TAggregateRoot obj)
23 where TAggregateRoot : class, IAggregateRoot;
24 /// <summary>
25 /// Registers a deleted object to the repository context.
26 /// </summary>
27 /// <typeparam name="TAggregateRoot">The type of the aggregate root.</typeparam>
28 /// <param name="obj">The object to be registered.</param>
29 void RegisterDeleted<TAggregateRoot>(TAggregateRoot obj)
30 where TAggregateRoot : class, IAggregateRoot;
31 }
UnitOfWork的具體操作會在EntityFrameworkRepositoryContext中完成,并在EntityFrameworkRepository中注冊IEntityFrameworkRepositoryContext接口類型映射,EntityFrameworkRepository作用就是在Repository集合中去完成持久化,工作單元的持久化,看下EntityFrameworkRepositoryContext中的示例代碼:
1 using System.Data.Entity;
2 using System.Threading;
3
4 namespace ByteartRetail.Domain.Repositories.EntityFramework
5 {
6 public class EntityFrameworkRepositoryContext : RepositoryContext, IEntityFrameworkRepositoryContext
7 {
8 private readonly ThreadLocal<ByteartRetailDbContext> localCtx = new ThreadLocal<ByteartRetailDbContext>(() => new ByteartRetailDbContext());
9
10 public override void RegisterDeleted<TAggregateRoot>(TAggregateRoot obj)
11 {
12 localCtx.Value.Entry<TAggregateRoot>(obj).State = System.Data.EntityState.Deleted;
13 Committed = false;
14 }
15
16 public override void RegisterModified<TAggregateRoot>(TAggregateRoot obj)
17 {
18 localCtx.Value.Entry<TAggregateRoot>(obj).State = System.Data.EntityState.Modified;
19 Committed = false;
20 }
21
22 public override void RegisterNew<TAggregateRoot>(TAggregateRoot obj)
23 {
24 localCtx.Value.Entry<TAggregateRoot>(obj).State = System.Data.EntityState.Added;
25 Committed = false;
26 }
27
28 public override void Commit()
29 {
30 if (!Committed)
31 {
32 localCtx.Value.SaveChanges();
33 Committed = true;
34 }
35 }
36
37 public override void Rollback()
38 {
39 Committed = false;
40 }
41
42 protected override void Dispose(bool disposing)
43 {
44 if (disposing)
45 {
46 if (!Committed)
47 Commit();
48 localCtx.Value.Dispose();
49 localCtx.Dispose();
50 base.Dispose(disposing);
51 }
52 }
53
54 #region IEntityFrameworkRepositoryContext Members
55
56 public DbContext Context
57 {
58 get { return localCtx.Value; }
59 }
60
61 #endregion
62 }
63 }
UnitOfWork的操作會貫徹所有Repository的持久化,在Byteart Retail項目中的領域層,有很多的類和接口關聯,比如IEntity、IAggregateRoot、IRepository、IRepositoryContext、Repository、RepositoryContext、EntityFrameworkRepositoryContext等等,用類圖表示有時候不太直覺,畫了一個簡單的示例圖,友善了解UnitOfWork在DDD中的應用始末:
點選檢視大圖
左半部分:IEntity、IAggreateRoot、IRepository<TAggregateRoot>、Repository<TAggregateRoot>等,可以看做是倉儲庫,和領域模型相關(存在于領域層),右半部:IUnitOfWork、IRepositoryContext、RepositoryContext、IEntityFrameworkRepositoryContext等,可以看做是倉儲的持久化(工作單元),這兩者通過EntityFrameworkRepository進行IoC注冊對象,完成所有Repository的整個工作單元的協調、管理。
You don't know you're beautiful,that's what makes you beautiful ! -你不知道你是如此的美麗動人,這就是你美麗動人的所在!
如果你覺得本篇文章對你有所幫助,請點選右下部“推薦”,^_^
參考資料:
- http://www.cnblogs.com/OceanEyes/archive/2012/10/29/UnitOfWork--ByEyes.html
- http://www.cnblogs.com/wlflovenet/archive/2011/08/05/EFandMvc9.html
- http://www.cnblogs.com/mecity/archive/2011/07/17/2108508.html
作者:田園裡的蟋蟀
微信公衆号:你好架構
出處:http://www.cnblogs.com/xishuai/
公衆号會不定時的分享有關架構的方方面面,包含并不局限于:Microservices(微服務)、Service Mesh(服務網格)、DDD/TDD、Spring Cloud、Dubbo、Service Fabric、Linkerd、Envoy、Istio、Conduit、Kubernetes、Docker、MacOS/Linux、Java、.NET Core/ASP.NET Core、Redis、RabbitMQ、MongoDB、GitLab、CI/CD(持續內建/持續部署)、DevOps等等。
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接。
分享到:
QQ空間
新浪微網誌
騰訊微網誌
微信
更多