經過幾天的努力拜讀大牛高手文章,終於對事件匯流排有所瞭解,特此記錄下來,以免忘記
1、定義相關的介面:
A 事件介面
1 public interface IDomainEvent 2 { 3 DateTime OccurredOn(); 4 5 /// <summary> 6 /// 設定為已讀 7 /// </summary> 8 /// <returns></returns> 9 void Read(); 10 11 /// <summary> 12 /// 是否已讀 13 /// </summary> 14 bool IsRead { get; } 15 }
B 事件訂閱介面
public interface IDomainEventSubscriber { Type SubscribedToEventType(); void Handle(object domainEvent); }
2、定義相關實現
A 事件實現
public abstract class DomainEvent : IDomainEvent { public readonly DateTime OccurredOnTime; public string ID; protected DomainEvent() { this.ID = Guid.NewGuid().ToString(); this.OccurredOnTime = DateTime.Now; this.IsRead = false; } public DateTime OccurredOn() { return this.OccurredOnTime; } public void Read() { this.IsRead = true; } public bool IsRead { get; private set; } }
B 事件訂閱實現
public abstract class DomainEventSubscriber<T> : IDomainEventSubscriber where T : IDomainEvent { /// <summary>訂閱的事件型別 /// </summary> /// <returns></returns> public Type SubscribedToEventType() { return typeof(T); } public abstract void HandleEvent(T domainEvent); public void Handle(object domainEvent) { if (domainEvent is T) { this.HandleEvent((T)domainEvent); } else { throw new NotSupportedException(string.Format("當前訂閱者支援的事件型別是:{0},當前事件是:{1}", typeof(T).FullName, domainEvent.GetType().FullName)); } } }
3、定義事件匯流排實現
public class DomainEventBus { public delegate void DistributeExceptionHandle(IDomainEventSubscriber subscriber, IDomainEvent domainEvent, Exception exception); /// <summary> /// Key:DomainEvent的型別,Value訂閱該型別事件的訂閱者列表 /// </summary> private static readonly Dictionary<Type, List<IDomainEventSubscriber>> _subscribers = new Dictionary<Type, List<IDomainEventSubscriber>>(); private static readonly object _lockObj = new object(); public event DistributeExceptionHandle DistributeExceptionEvent; private static DomainEventBus _instance; public static DomainEventBus Instance() { if (_instance != null) return _instance; var temp = new DomainEventBus(); Interlocked.CompareExchange(ref _instance, temp, null); return temp; } public void Publish<T>(T aDomainEvent) where T : IDomainEvent { if (aDomainEvent.IsRead) return; var registeredSubscribers = _subscribers; if (registeredSubscribers != null) { var domainEventType = aDomainEvent.GetType(); List<IDomainEventSubscriber> subscribers; if (!registeredSubscribers.TryGetValue(domainEventType, out subscribers)) { aDomainEvent.Read(); //未找到訂閱者,但是訊息還是消費掉。 return; } foreach (var domainEventSubscriber in subscribers) { var subscribedTo = domainEventSubscriber.SubscribedToEventType(); if (subscribedTo == domainEventType || subscribedTo is IDomainEvent) { Distribute(domainEventSubscriber, aDomainEvent); } } aDomainEvent.Read(); } } private void Distribute(IDomainEventSubscriber subscriber, IDomainEvent domainEvent) { try { subscriber.Handle(domainEvent); } catch (Exception ex) { OnDistributeExceptionEvent(subscriber, domainEvent, ex); } } public void Subscribe(IDomainEventSubscriber aSubscriber) { lock (_lockObj) { var registeredSubscribers = _subscribers; var domainEventType = aSubscriber.SubscribedToEventType(); List<IDomainEventSubscriber> subscribers; if (!registeredSubscribers.TryGetValue(domainEventType, out subscribers)) { subscribers = new List<IDomainEventSubscriber>(); registeredSubscribers.Add(domainEventType, subscribers); } if (subscribers.Any(ent => ent.SubscribedToEventType().FullName == aSubscriber.SubscribedToEventType().FullName && ent.GetType().FullName == aSubscriber.GetType().FullName)) //相同的訂閱只接收一次。 return; subscribers.Add(aSubscriber); } } protected virtual void OnDistributeExceptionEvent(IDomainEventSubscriber subscriber, IDomainEvent domainEvent, Exception exception) { var handler = DistributeExceptionEvent; if (handler != null) handler(subscriber, domainEvent, exception); } }
4、定義具體的事件
public class OrderCreated : DomainEventCore.DomainEvent { public string OrderId { get; private set; } public string UserId { get; private set; } public string Receiver { get; private set; } public OrderCreated(string orderId, string userId, string receiver) { this.OrderId = orderId; this.UserId = userId; this.Receiver = receiver; } }
5、定義具體事件觸發後要執行的方法
public class OrderCreatedSubscriberPaymentContext : DomainEventSubscriber<OrderCreated> { public override void HandleEvent(OrderCreated domainEvent) { //TODO anything Console.WriteLine("Order ID:{0},i have payment",domainEvent.OrderId); } }
public class OrderCreatedSubscriberSellingPriceContext : DomainEventSubscriber<OrderCreated> { public override void HandleEvent(OrderCreated domainEvent) { //TODO anything Console.WriteLine("Order ID:{0},i have show price", domainEvent.OrderId); } }
6、程式碼測試
class Program { static void Main(string[] args) { //事件訂閱 DomainEventBus.Instance().Subscribe(new OrderCreatedSubscriberSellingPriceContext()); DomainEventBus.Instance().Subscribe(new OrderCreatedSubscriberPaymentContext()); var tempGuid = Guid.NewGuid().ToString(); var entity = new OrderCreated(tempGuid, "mikechang", "sprite"); Console.WriteLine("生產一個新的訂單:{0}",tempGuid); //事件釋出 DomainEventBus.Instance().Publish(entity); Console.ReadLine(); } }