ABP學習實踐(七)--領域事件

事件是在軟件開發過程中經常用到的一種思路和形式,事件常常是和觀察者模式、訂閱發佈這樣的詞彙聯繫在一起。在ABP框架中同樣也少不了事件,也就是領域事件。


1.領域事件的使用範圍

在具體業務中常常會有這樣的需求,以前面的貨品管理功能爲例,對於某種特定類型的貨品,我們希望在貨品庫存數量低於某個特定值的時候得到提醒,以便於進行採購補貨或其他操作,就是常說的庫存預警功能。解決這個問題最簡單的思路就是寫一個方法循環查詢庫存數量,當數量低於特定值時執行預先設定的操作。但這樣有兩個比較明顯的問題:一是循環的讀取查詢庫存耗時耗力,浪費大量資源;二是庫存預警功能和貨品管理功能的代碼關聯性太強,一旦業務需求發生調整,改起來真是“牽一髮而動全身”,耦合性太強。在這樣的背景下,事件就有了用武之地。
簡單來說,一個類可以定義其專屬的事件並且其它類可以註冊該事件並監聽,當事件被觸發時可以獲得事件通知。當我們需要解耦業務邏輯以及對領域對象的變化做出反應時,就需要用到領域服務。

2.定義並註冊領域事件

在ABP框架中,領域事件相關的類位於Abp.Events.Bus命名空間下。事件是派生自 EventData 的類。在領域層AbpDemo.Core中定義一個貨品數量變更的事件。

    /// <summary>
    /// 貨品庫存變更事件
    /// </summary>
    public class GoodsNumChangedEventData:EventData
    {
        /// <summary>
        /// 貨品標識
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// 貨品名稱
        /// </summary>
        public string GoodsName { get; set; }
        /// <summary>
        /// 當前數量
        /// </summary>
        public int GoodsNum { get; set; }
        /// <summary>
        /// 數量下限
        /// </summary>
        public int MinNum { get; set; }
    }

在應用層AbpDemo.Application中使用依賴注入來獲取對 IEventBus 的引用,實現對領域事件的註冊,之後可以在具體的業務邏輯代碼中觸發事件。在貨品管理模塊中,以出庫操作爲例,當出庫完成後,如果貨品數量低於預先設定的下限,則觸發事件。

   /// <summary>
    /// 貨品管理-應用服務
    /// </summary>
    public class GoodsAppService: AbpDemoAppServiceBase<Goods,DetailGoodsDto,string,CreateGoodsDto,UpdateGoodsDto,PagedGoodsDto>,IGoodsAppService
    {
        private readonly IGoodsRecordManager _goodsRecordManager;//出入庫記錄領域服務
        private readonly IGoodsManager _goodsManager;//貨品管理領域服務
        public IEventBus EventBus { get; set; }//事件總線
        private const int MinNum = 50;//貨品數量下限
        public GoodsAppService(IRepository<Goods,string> repository,IGoodsRecordManager goodsRecordManager,IGoodsManager goodsManager):base(repository)
        {
            _goodsRecordManager = goodsRecordManager;
            _goodsManager = goodsManager;
            EventBus = NullEventBus.Instance;
        }

        /// <summary>
        /// 出庫
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<DetailGoodsDto> Out(InOutGoodsDto input)
        {
            Goods entity = Repository.FirstOrDefault(input.Id);
            if (entity != null)
            {
                entity.GoodsNum = entity.GoodsNum - input.GoodsNum;
            }
            GoodsRecord record = input.MapTo<GoodsRecord>();
            record.OperateType = GoodsOperateType.Out;
            string recordId = await _goodsRecordManager.OutRecord(record);

            entity = await Repository.UpdateAsync(entity);

            if (entity.GoodsNum<=MinNum)//貨品數量低於下限時觸發事件
            {
                EventBus.Trigger(new GoodsNumChangedEventData
                {
                    Id = entity.Id,
                    GoodsName = entity.GoodsName,
                    GoodsNum = entity.GoodsNum,
                    MinNum=MinNum
                }) ;
            }

            DetailGoodsDto result = entity.MapTo<DetailGoodsDto>();
            return await Task.FromResult(result);
        }
    }

3.領域事件的訂閱和處理

在事件觸發後,事件的訂閱者就可以收到通知,進而對事件進行處理。在ABP框架中,要對領域事件進行處理,就要實現IEventHandler接口。

    public class GoodsChangedManager : IEventHandler<GoodsNumChangedEventData>, ITransientDependency
    {
        public void HandleEvent(GoodsNumChangedEventData eventData)
        {
            string message = string.Format("貨品{0}當前庫存爲{1},低於最低允許庫存{2},請及時採購補充!", eventData.GoodsName, eventData.GoodsNum, eventData.MinNum);

            /*
             * To do
             * 後續處理
             * */
        }
    }

在上面的代碼中,當貨品數量低於下限時觸發事件,在HandedEvent方法中就可以進行具體操作了,比如說通知其他領域對象或發送消息到外部。

在ABP框架中,項目啓動時所有實現IEventHandler接口的類都會自動註冊到事件總線中。當事件發生, 通過依賴注入(DI)來取得處理器(handler)的引用對象並且在事件處理完畢之後將其釋放。除了自動註冊事件外,ABP框架還支持手動註冊和卸載事件。

                //註冊事件
                var goodsChangedEvent = EventBus.Register<GoodsNumChangedEventData>(data =>
                {
                    /*
                     * To do
                     **/
                });
                //取消註冊事件
                goodsChangedEvent.Dispose();

以上示例比較簡單,只是爲了說明問題,更多深度用法可以查看官方文檔並在實際工作中靈活運用。


源代碼示例-Github

發佈了85 篇原創文章 · 獲贊 163 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章