自己以前都走了彎路,以為學習戰術設計就會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...