11

領域驅動設計學習筆記(7):Aggregate (下)

 2 years ago
source link: http://teddy-chen-tw.blogspot.com/2020/01/7aggregate.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

領域驅動設計學習筆記(7):Aggregate (下)

January 01 21:52~23:28

E2llz-nL7zYvl55N-fYMihTHjDiMlyE-K7UKuqdAFz0TQ_kDlDGHsVmhoAXosdbz4HvM3RrqdN_i2jlyjjfLuC4sZ91xFI4fE8I4vwa62CEYy8hR4bFZbYyB2RlpOGcqpknx26gCdREY2-3ARSfnOqM3D_yRCVVntrFa-Os8Aal3g6SxNlSqvDJdwx8xH_lBrBtgwVvTr6Vga_rHhYmwmRG2SfcDoLGa7IYl1X-zrtSSLOtywl8tlnwVEjmgV-T6yEGebI3ZAOibIP4TacV6sLC05TaY9X4i-wImZb4xu-xl-g3Po0IQu-0Gw5aHUXhywtPj4fRcjHpBruP3u2JypgV21TsJH2jIR874Fl8WjQ0yKoi8Nsr7oidLhJt1uShcHgcLNMJkBqalxeb1xgj4F0lQYjzvjVNRHwqEK43pGnvmp2sZbAYO8UQYqbo0sHOwPN0IlD3NO2YOXgi6N4kTgz6HWRNtUh1hLAnzLY2AsSNtXXEMej2KQQZZHGAY5PjEwcZRbgeaG4YomaKWMiBn-lwV2q66NCSmnNqn067kkWEnIAwiAtWpfx1OP_evCqsN6JMLbciMDXokDuZpArRoXeNZ3aIMjgzp2mZmdZP2BzHdediYd7vCJC10yK7tUhpzr1Sjq2Jf119nEJrkzxujfN09v869j-56M5LmO4KHxDcVTdRttVe-uvutWHERCDXFNo-Hz17ngjunsI5gGRh5oUgZ0axXdkQfD8tcL-evJFw3gzD2LA=w982-h404-no

▲圖1:Clean Kanban領域模型(部分)

問題

領域驅動設計(Domain-Driven Design;DDD)的Aggregate物件之間透過ID(字串或GUID)參考,而不像傳統物件導向設計(OOAD)直接透過記憶體參考(memory reference)來存取依賴物件。對於「從小」熟練物件導向分析與設計(OOAD)的Teddy而言,剛開始覺得這樣做好像不太方便啊。只記錄Aggregate ID,還要透過Repository來取得該Aggregate物件,這樣不是有點麻煩嗎?

記憶體參考

圖1是Teddy所開發的看板系統的部分領域模型:

  • Board:代表一個看板。
  • Workflow:代表一個工作流程,一個看板可以有多個工作流程。
  • Stage:代表垂直的工作階段。
  • Swimlane:代表水平的工作階段。

如果領域模型直接透過記憶體參考,也不套用Aggregate,寫程式的時候就可以:

  • board.getWorkflows()傳回看板的全部工作流程物件,或用board.getworkflowsById()得到某個工作流程物件。
  • workflow.getBoard()得到這個看板所屬的board物件。

這樣一來透過物件的reference可以輕鬆自在存取整個領域模型,甚至可以:

stage.getworkflow().getBoard().getUser()……一直不斷的get下去。當然程式寫成這樣就產生壞味道,但是無限制的記憶體參考很容易「引誘犯罪」,讓開發人員寫出這種程式。

Aggregate形成邊界

qK2oP2p2EQ4-IvwNDqCb1cyH-aCUMA5vs9v3ZBgmdyjJ7helaz5yvQvczFis3whTt-KClrseBLy7doRHROE4qbWKvAxJ-9wNz3kb8WZlApKa-AJRS2WbOHt6Pep9WEij3oQZcUGaUPKa76J4SFiaJPA3EBU1Q1WPOVLCXUHhsKh4zR-W7Yugk8q58pJVs1Swui7Tc1lkctMOfAI2NQE2GUc-qe_EEj4n7N1li4YI8DpBDTM87FMMT-cwYHdqrUk_FHrxAqKei3GhJoqDHgRJC_9C217eIbYTDqrXvqIH8LI83lQUgsLg1SobWHl4sHeusritnfcGdmrDobn5aXfxIesltELuotyfJdcOtlz66w1u9SYdxgWEaEwBOd8z6w5B-TSPkSs-3ARPgi5GV4XBPETQhAsvIukbq7zLudL85y6sVd3dbslzh2vsrCdiksC7yAnwZ7RJ_Ax0z4g5V0WSg_3UY_iEw0p5XLw7ZhxI4m-tKJwYrf-fLJ42MNaTbAqd4QhaaGJfDwd4rwPAxx7XDgUqse2dsbIq_Iuds_IfujQ5OlewNyf7QJFpSEWEgL8yBpXU0YbaS78kfZ-r-PzvrVZ_TyDdogXY-FhqoiK4QIA4UNfIi9G2MkyhEEvHYwlznmKV6PT7qdq_m76PNTctKia3sZWxFuR0fvIdlsZ0PBvMutQTAcag_Z5CrR3lexc2eOwTKtVG5-6pSLyrhed20CvShNWXkavy5R7bOKR-zPOCo0Nxhw=w974-h544-no

圖2:套用Aggregate形成物件的邊界

如圖2所示,套用DDD的Aggregate設計模式,原本領域物件變成兩個Aggregate:Board與Workflow,得到如圖3所示的Board與Workflow類別。

GnV1yAJ-T6F6-fYlYhIuIBOFPvmc9X9EEvgYp2fVk23coCNrg2X6tn-lJfOsj0PQ5L_nitg_UpNdbHFBqnsdwnc1EwE8Q_x3p2RkBxotDLP5mW0VG6KDqvQkpRNHXGoTDZoYwrPVETjcKFEzmEemydteGgpOtneZXv56DFEjXX6GuaohS32P3LqEb-EVvdqsVVFydNG4-sdvGL3xG6PT7AoiJDH1Wcn_6nLE6wXXtTKZWTiVz93hesXmfBEZRZ11V4gzH9X1W5uWZVwfQSfVqthk5AWf7qEdhgfg10LxiwaSq5-lrIZfxcNXUk-w_hvxKekdYwsW7WGsupd3NSldaulGTZL9QPaEJUJlmocJT_l1nYqxqsmPfq7y6FE8al0j_BF4-GWyz4XjaZzjeHtGroDBlG8rRVOqbm6oIMA_5-qOzkaQDE7-bw8ibdlXClsu54QLERewJP81sdwbXP9Wr15DtoXk2NzTpgwZ5TZJD2TOT3azneiX2AzNzmgbW0dbv5wJrcCyDwfol0vbspDmUFdhDJievosVVCuKcb5UVFRu3l6GCJxCJrrrZt9QUdMD4Yj3tpzbNGgg4zm249XnISoH0RtHENLWGqZ11QGEizVUcTGKLGl0Fpn0VF1NrC7N6Y1zjoeBVB9WoriVTS8qoUB8ZWcAEUSlgS0ce-mqe1HiOirZMCii06FfyUun4fmgPmSr2LYg4tr7UTAwyNW61KRQR4CesHwzx_zPRR9NMXfr6kmU9w=w1440-h588-no

圖3:Board與Workflow類別。

套用Aggregate有幾個好處:

  • Board與Workflow變成獨立的Aggregate,因此可以各自更新,不必放在同一個交易(transaction)裡面。如此一來可以提高並行處理的彈性。
  • 一個Aggregate對應一個Repository,物件儲存問題變得簡單很多。
  • Aggregate內部物件的存取必須透過Aggregate Root,因此可以讓Aggregate Root來確保對於Aggregate的操作不會違反invariant,也就是保持程式狀態的正確性。

當然有好處也有缺點:

  • 最大的缺點(也是優點)可能就是Aggregate之間透過領域事件(domain event)達成狀態同步,由原本同步的方式變成非同步方式,程式開發模式跟傳統不同,習慣需要改變一下,重新學習。
  • 無法像傳統一樣,只要透過物件參考就可以得到整個領域物件(或大部分領域物件)的資料。例如,如果使用者介面要顯示看板所有的工作流程,從Board物件無法直接得到Workflow物件,因為Board只記錄Workflow的ID,必須再透過WorkflowRepository才能得到Workflow物件。

View Model

CleanArchitecture%255B4%255D

▲Clean Architecture,圖片來源在此

將領域模型與使用者介面模型(View Model)切開,原本就是DDD的重點。但是領域模型最終還是要展示給使用者看,此時只要多設計一層View Model,將領域模型轉成使用者介面方便操作的View Model就可以了。從Clean Architecture的角度來看,讓Interface Adapter層(第三層)來負責做轉換。

3xP4OMOet7xn1cfv_jbGr5b-jD2ZA93GRLtIrrm2_Vnx0s3f4X-0eRSG17ueMU0pjwV00lPm75hAxVoce5TF4UqIYWKamxrbXblyJmzAmZvPxj1RKScWzqcGXWxP09kwCFJzk_68-YYjmRa2h84lPfwuwID1taLVrKGfmr9YYgB4xiQu0GlQttHBZnUO0n1y7jpWkugsEC-8PrtcthJ1cRi-qCg1ubx5JsBm_C3Sn4TvKr84LyZLxM5OzMn8FeUJ02snf_kUeAkql2agVz9Jz8TLHzHF0If6rRJfRVMoUN-s_kDdcWR-JoBbj1FzBiFxNylHq7F6NNgOgf6pAXwT2XT8LYgdMiyX653vKMhCUp_09Gna4M3JpGJxD11aBjYvFbCTUKWGu9--ApcL9PNylehTYCDJjvKbBB18zmLGMdKwF9zMUWoA3BY8-5oj7Fvx4rVbo3NylldDcOsI9h4b8Ba0SHw4V8F6trSbHW0ARV8oSNGAjH9G1Y38TUxdhdf4pLZEGo9RSW5NSfG-XJdtFrYpkClwRW99-DZyz-DgkXEBqEaTz6cmpjUi3yxNSTqrJSwCz1vfcv96WbLXAi9jz88rtQtXDjpX_k4YFEy-46aL0-1SnKxZlHVGP3-Pspq_kYTXOfQSRQwAMJxIoLI29adwCrVSj8AMhC2UbDl3c7n6nNLLcR8l7nM_KyVna0StcMplC8p0UBP7EylQleyW4kD0aIwr0aCqtZTy6KRVx78BRn4PRw=w1020-h296-no

▲設計BoardDto給UI使用。

結論

釐清邊界,單一責任,分層負責,系統的結構才會清楚明白,軟體也才能變軟。

友藏內心獨白:把一件事情做到最好就很厲害了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK