簡單的學習,實現,領域事件,事件儲存,事件溯源

2022-01-12 19:59:05 字數 3981 閱讀 1857

自己以前都走了彎路,以為學習戰術設計就會ddd了,其實ddd的精華在戰略設計,但是對於我們菜鳥來說,學習一些技術概念也是挺好的

經常看到這些術語,概念太多,也想簡單學習一下,記憶力比較差記錄一下實現的細節1.領域事件是過去發生的與業務有關的事實,一但發生就不可更改,所以儲存事件時只能追加

3.領域事件具有時間點的特徵,所有事件連線起來會形成明顯的時間軸

4.領域事件會導致目標物件狀態的變化,聚合根的行為會產生領域事件,所以會改變聚合的狀態

在聚合根裡面維護乙個領域事件的聚合,每乙個事件對應乙個handle,通過反射維護乙個資料字典,通過事件查詢到指定的handle

領域事件實現的方式:目前看到有3種方式,mediatr,訊息佇列 ,發布訂閱模式

eshoponcontainers 中使用的是mediatr

enode 中使用的是equeue,equeue是乙個純c#寫的訊息佇列

使用已經寫好的訊息佇列rabbitmq ,kafka事件儲存:儲存所有聚合根裡面發生過的事件

1.事件儲存中可以做併發的處理,比如command 重複,領域事件的重複

2.領域事件的重複通過聚合根id+版本號判斷,可以在資料庫中建立聯合唯一索引,在儲存事件時檢測重複,記錄重複的事件,根據業務做處理

3.這裡要保證儲存事件與發布領域事件的一致性

如何保證儲存事件與發布領域事件的一致性

先儲存事件然後在發布領域事件,如果發生異常,就一直重試,一直到成功為止,也可以做一定的處理,比如重試到一定的次數,就通知,進行人工處理

聚合快照:聚合的生命週期各有長短,有的聚合裡面有大量的事件,,事件越多載入事件以及重建聚合的執行效率就會越來越低,快照裡面儲存的是聚合

1.定時儲存整個聚合根:使用定時器每隔一段時間就儲存聚合到快照表中

2.定量儲存整個聚合根:根據事件儲存中的數量來儲存聚合到快照表中

事件溯源的實現方式

1.首先我們需要實現聚合in memory,

2.在commandhandler中訂閱 command命令,

建立聚合時 ,在記憶體中維護乙個資料字典,key為:聚合根的id,value為:聚合

修改,刪除,聚合時,根據聚合根的id,查詢出聚合

如果記憶體中聚合不存在時:根據聚合根的id 從聚合快照表中查詢出聚合,然後根據聚合快照儲存的時間,聚合根id,查詢事件儲存中的所有事件,然後回放事件,得到聚合最終的狀態由於基礎非常的差,所以實現的方式都是以最簡單的方式來寫的,存在許多的問題,**中有問題的地方希望大家提出來,讓我學習一下

**的實現目前還沒有寫快照的部分,也沒有處理eventstorage中的命令重複與聚合根+版本號重複,具體的請看湯總的enode,裡面有全部的實現

1.怎樣保證儲存事件,發布事件的最終一致性

2.怎麼解析eventstorage中的事件,回放事件

先儲存事件,當事件儲存成功之後,在發布事件

儲存事件失敗:就一直重試,發布事件失敗,使用的是cap,cap內部使用的是本地訊息表的方式,如果發布事件失敗,也一直重試,如果伺服器重啟了,rabbitmq裡面訊息為ack,訊息沒有丟,重連後會繼續執行

儲存事件,發布事件

/// /// 儲存聚合根中的事件到eventstorage 發布事件

///

///

///

///

where taggregationroot : iaggregationroot

});}

/// /// 發布領域事件

///

///

public async task publishdomaineventasync(listdomaineventlist)

using (var transaction = await connection.begintransactionasync().configureawait(false))

}await transaction.commitasync().configureawait(false);

}catch (exception e)}}

}/// /// 發布領域事件重試

///

///

///

public async task trypublishdomaineventasync(listdomaineventlist)

);});

await policy.executeasync(async () =>);}

/// /// 儲存聚合根中的事件到eventstorage中

///

///

var status = (int)eventstoragestatus.failure;

using (var connection = new sqlconnection(connectionstr))

using (var transaction = await connection.begintransactionasync().configureawait(false))

;var eventstoragesql =

$"insert into eventstorageinfo(id,aggregaterootid,aggregateroottype,createdatetime,version,eventdata) values (@id,@aggregaterootid,@aggregateroottype,@createdatetime,@version,@eventdata)";

await connection.executeasync(eventstoragesql, eventstorage, transaction).configureawait(false);}}

await transaction.commitasync().configureawait(false);

status = (int)eventstoragestatus.success;

}catch (exception e)}}

catch (exception e)

}return status;

}///

);});

var result = await policy.executeasync(async () =>

);return result;

}/// /// 根據domainevent序列化事件json

///

///

///

public string events(idomainevent domainevent)

解析eventstorage中儲存的事件

public async task> getaggregaterooteventstoragebyid(guid aggregaterootid)

'");

listdomaineventlist = new list();

foreach (var item in eventstoragelist)}}

return domaineventlist;}}

catch (exception ex)

1.事件沒持久化就代表事件還沒發生成功,事件儲存可能失敗,必須先儲存事件,在發布事件,保證儲存事件與發布事件一致性

1.使用事件驅動,必須要做好冥等的處理

2.如果業務場景中有狀態時:通過狀態來控制

3.新建一張表,用來記錄消費的資訊,消費端的**裡面,根據唯一的標識,判斷是否處理過該事件

4.q端的任何更新都應該把聚合根id和事件版本號作為條件,q端的更新不用遵循聚合的原則,可以使用最簡單的方式處理

5.倉儲是用來重建聚合的,它的行為和集合一樣只有get ,add ,delete

6.ddd不是技術,是思想,核心在戰略模組,戰術設計是實現的一種選擇,戰略設計,需要物件導向的分析能力,職責分配,深層次的分析業務雖然學習ddd的時間不短了,感覺還是在入門階段,在學習的過程中有許多的不解,經常問enode群裡面的大佬,也經常@湯總,謝謝大家的幫助與解惑。

領域事件 事件風暴

領域事件 領域專家所關心的在領域中的一些事件。將領域中所發生的活動建模城一系列的離散事件。每個事件都用領域物件來表示。領域事件是領域模型的組成部分,表示領域中所發生的事情。重要的事件肯定會在系統其他地方引起反應,因此理解為什麼會有這些反應同樣也很重要。martin fowler 乙個領域事件可以理解...

C 事件 事件學習好例子

一 委託的簡介 二 事件的簡介 三 委託和事件的使用 四 總結 一 委託的簡介 1 委託的宣告 delegate handlername parameters 例如 public delegate void printhandler string str 委託宣告定義了一種型別,它用一組特定的引數以...

事件 事件的新增與刪除

事件的繫結 ele.addeventlistener 事件型別 匿名函式或者函式名,執行型別 注意引入函式名的話,不要加小括號,執行型別的話 false為事件冒泡,true為事件捕獲 var box document queryselector box var box1 document query...