- 何為領域驅動設計?
2004年著名建模專家Eric Evans發表了他最具影響力的書籍:《Domain-Driven Design: Tackling Complexity in the Heart of Software》(中文譯名:領域驅動設計:軟件核心復雜性應對之道),書中提出了領域驅動設計(簡稱 DDD)的概念。
領域驅動設計事實上是針對OOAD的一個擴展和延伸,DDD基於面向對象分析與設計技術,對技術架構進行了分層規划,同時對每個類進行了策略和類型的划分。
領域模型是領域驅動的核心。采用DDD的設計思想,業務邏輯不再集中在幾個大型的類上,而是由大量相對小的領域對象(類)組成,這些類具備自己的狀態和行為,每個類是相對完整的獨立體,並與現實領域的業務對象映射。領域模型就是由這樣許多的細粒度的類組成。基於領域驅動的設計,保證了系統的可維護性、擴展性和復用性,在處理復雜業務邏輯方面有着先天的優勢。
摘自:http://kb.cnblogs.com/page/112298/
- 領域模型的種類?
- 失血模型:模型僅僅包含數據的定義和getter/setter方法,業務邏輯和應用邏輯都放到服務層中。這種類在Java中叫POJO,在.NET中叫POCO。
- 貧血模型:貧血模型中包含了一些業務邏輯,但不包含依賴持久層的業務邏輯。這部分依賴於持久層的業務邏輯將會放到服務層中。可以看出,貧血模型中的領域對象是不依賴於持久層的。
- 充血模型:充血模型中包含了所有的業務邏輯,包括依賴於持久層的業務邏輯。所以,使用充血模型的領域層是依賴於持久層,簡單表示就是 UI層->服務層->領域層<->持久層。
- 脹血模型:脹血模型就是把和業務邏輯不想關的其他應用邏輯(如授權、事務等)都放到領域模型中。我感覺脹血模型反而是另外一種的失血模型,因為服務層消失了,領域層干了服務層的事,到頭來還是什么都沒變。
- 領域事件和事件總線
- 總管對象(事件總線) EventBus
主要職責是管理領域模型的所有事件,通過Dictionary<Type,List<object>> _dicEventHandler=new Dictionary<Type, List<object>>();主要包括
- Subscribe訂閱相關的領域對象事件
- Publish 觸發所有在事件總線上的領域對象事件句柄
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyDDD { /// <summary> /// 事件總線 /// </summary> public class EventBus { /// <summary> /// 事件總線對象 /// </summary> private static EventBus _eventBus=null; /// <summary> /// 領域模型事件句柄字典,用於存儲領域模型的句柄 /// </summary> private static Dictionary<Type,List<object>> _dicEventHandler=new Dictionary<Type, List<object>>(); /// <summary> /// 附加領域模型處理句柄時,鎖住 /// </summary> private readonly object _syncObject = new object(); /// <summary> /// 單例事件總線 /// </summary> public static EventBus Instance { get { return _eventBus ?? (_eventBus=new EventBus()); } } private readonly Func<object, object, bool> _eventHandlerEquals = (o1, o2) => { return true; }; #region 訂閱事件 public void Subscribe<TEvent>(IEventHandler<TEvent> eventHandler) where TEvent : IEvent { //同步鎖 lock (_syncObject) { //獲取領域模型的類型 var eventType = typeof (TEvent); //如果此領域類型在事件總線中已注冊過 if (_dicEventHandler.ContainsKey(eventType)) { var handlers = _dicEventHandler[eventType]; if (handlers != null) { handlers.Add(eventHandler); } else { handlers=new List<object> { eventHandler }; } } else { _dicEventHandler.Add(eventType, new List<object> { eventHandler }); } } } #endregion #region 宣布事件 public void Publish<TEvent>(TEvent tEvent, Action<TEvent, bool, Exception> callback) where TEvent:IEvent { var eventType = typeof (TEvent); if (_dicEventHandler.ContainsKey(eventType) && _dicEventHandler[eventType] != null && _dicEventHandler[eventType].Count > 0) { var handlers = _dicEventHandler[eventType]; try { foreach (var handler in handlers) { var eventHandler = handler as IEventHandler<TEvent>; eventHandler.Handle(tEvent); callback(tEvent, true, null); } } catch (Exception ex) { callback(tEvent, false, ex); } } else { callback(tEvent, false,null); } } #endregion } }
- 領域對象
領域對象通過實現IEvent接口,標記此領域對象可以往事件總線中訂閱與自己相關的領域對象事件。
用戶注冊領域對象
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyDDD { /// <summary> /// 領域模型對象 /// </summary> public class UserGenerator:IEvent { /// <summary> /// 用戶Id /// </summary> public Guid UserId { get; set; } } }
- 領域對象事件句柄
領域對象事件句柄是領域對象發生變化時,通知EventBus觸發所有的事件句柄
public void Publish<TEvent>(TEvent tEvent, Action<TEvent, bool, Exception> callback) where TEvent:IEvent
用戶注冊發送Email的事件句柄
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyDDD { /// <summary> /// /// </summary> public class UserAddedEventHandlerSendEmail : IEventHandler<UserGenerator> { public void Handle(UserGenerator tEvent) { Console.WriteLine(string.Format("{0}的郵件已發送", tEvent.UserId)); } } }
例子說明:當用戶注冊完成后,通知EventBus,由EventBus觸發相應操作,發送Email或者其他操作。
這樣的好處用戶注冊是主業務,其他屬於拓展,增加或減少需求,只需要往EventBus上訂閱或取消事件即可。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyDDD { class Program { static void Main(string[] args) { EventBus.Instance.Subscribe(new UserAddedEventHandlerSendEmail()); var userGenerator = new UserGenerator{UserId = Guid.NewGuid()}; Console.WriteLine("{0}注冊成功",userGenerator.UserId); EventBus.Instance.Publish(userGenerator, CallBack); Console.ReadKey(); } public static void CallBack(UserGenerator userGenerator, bool result, Exception ex) { } } }