領域驅動設計與整潔架構基礎課程筆記之二
今天是領域驅動設計與整潔架構課程第二天的課程。
一樣是記錄關於此堂課程的筆記。
建模後續
- 雖然建模需要能夠剛剛解決問題,但對應到 Solution Problem 可能會有衝突,這時就可能會需要因 force 做取捨,如 CAP 定理的取捨,是透過分析需求,了解有更強的 force 來取決要捨棄哪一個特性。
- 建模的過程通常不會包含 Domain Event,不然會很難閱讀。
- 什麼時候才是塑模完成:Alberto Brandolini: More requirement please
將模型分組
模型分組並沒有絕對的對或錯,只有一些參考的方法如下:
- 一個 Aggregate 必須要是一個 transaction,如果需求可以接受最終一致性,那就不需要放同個 aggregate。
- 一個 Aggregate 的範圍越大,平行處理就越難,因為會卡在 transaction 的物理限制。
- 真實世界的例子幾乎都是最終一致性。
- 因為「接受」最終一致性的特性,因此 Aggregate 可以做為最小部署單位,而不需要整個 Bounded Context 一起部署。
- Aggregate 不完全是技術的 pattern,也跟 problem model 有關
- 切得好,同時 ORM 也會比較好寫(上課有提到例子是 Java Hibernate 相關的,但我聽不懂)
取捨的例子一:「移動卡片的並發操作」與「WIP limit 規則」是衝突的。
取捨的例子二:workflow 與 lane 同時操作的需求是不存在的,因此也不需要特別拆兩個 aggregate。
最後,如果能夠順利解釋下面這張圖的話,就算是懂 EventStorming 了。
再一次推薦,上圖是參考 Alberto Brandolini 所寫的電子書 EventStorming。
這個圖有一個重點,EventStorming 分析的主軸都在 write model,而不是 read model(指 CQRS 的 C 與 R)。因此 EventStorming 在複雜的資料讀取上,比較沒辦法做呈現。
DDD 術語
DDD 的三個重要元素:
- Bounded Contexts
- Domain Events
- Aggregates
其他參考的關鍵字:
- Collaborative Modeling,DDD 是一個共同協作建模的過程
- Strategic Design,DDD 透過戰略設計來找出 Bounded Contexts,並協助建模的過程
- Modeling in Code,DDD 建模完成後,可以直接應用在程式碼上
Ubiquitous Language(通用語言)
- 在某個 Bounded Context 應該是相同的,並再寫進程式裡。傳統 OO 並沒有這個概念
- 其他參考 https://martinfowler.com/bliki/BoundedContext.html
- 可以從業務邊界找 Bounded Context,或是透過 EventStorming 找
- 傳統 Waterfall 的問題會有 Model 轉變的過程,因此階段越多,Model Translation 就越多,就越不容易出現共同語言,因此會非常難維護。
Pattern
參考: http://teddy-chen-tw.blogspot.com/2016/08/domain-driven-design-reference.html
最左邊的 Model-Driven Design 是 Goal,右邊是相對的細節:
- Services
- 無狀態的物件
- 只擁有演算法,必要的參數都由其他人提供
- Entities
- 有狀態的物件
- 因為我們在意它狀態的變更,它才會是 entity。可參考: https://ithelp.ithome.com.tw/articles/10223150
- 可以靠 ID 去區分是否為同一個 entity
- Value Objects
- 無狀態的物件
- 靠 Value 去區分是否為同一個 value object
- Domain Events(圖上或書上沒有,這是後來加上去的)
- 無狀態的物件
- 記錄狀態改變的內容
第二層
- Factories
- 產生 Aggregates 的方法
- 最常採用 Builder pattern
- Aggregates
- Act as root of :Aggregate 一定會有個 root ,會是 Entity
- maintain integrity with :Entity 的一致性由 Aggregate 處理
- Aggregate 具有 Invariant(不變)的特性
- Repositories
- access with 讓 Aggregates 存取它
- Teddy 不建議讓 Entity 存取(如果 Aggregate root 是 Entity,那確實 Entity 可以存取 Repository)
- Layered Architecture
- 如:Clean Architecture,這代表完成後的結果應該要應用在分層式架構上,照理來說,傳統的三層式架構也是可以的。
圖裡的 Smart UI 指的是用 UI driven 或 Database driven,它與 DDD 是互斥的。
這是另一張圖:model integrity patterns,但是是第三天才提到的。
Domain Event
分兩種類型:
- 一種是 Bounded context 之內
- 一種是 Bounded context 之外
應用之一是在維護最終一致性(eventual consistency),另一種應用是 Event sourcing。
儲存物件狀態的方法也有兩種:
- state sourcing 存的是 current state
- event sourcing 存的會是 state stream
參考 CQRS(Command Query Responsibility Segregation),DDD 所考慮的是 write model,而沒考慮 read model
Clean Architecture
Clean Architecture 是分層架構,分成四層如下:
- Clean Architecture 裡的 DDD,指的是 Use Cases 與 Entities。
- DDD 裡的 Clean Architecture,指的是分層式架構
課堂我有個提問:老師有提到,Interface Adapter 只專注在轉換協定,如 HTTP 或 gRPC 等。那 Authentication 應該放在哪一層?
Authentication 是指:我要在哪一層決定 request 能不能使用 use cases?
會問這個是有原因的:Application Business Rules 照理不應該放 Authentication,因為應用程式只處理屬於它的邏輯,但在進入協定轉換前的最外層 Frameworks & Drivers 做 Authentication 又不大可能。可是 Interface Adapter 同時做轉換又做 Authentication 也很奇怪。
這個問題當天直接在討論實作了,最終結論是放在 Interface Adapter,但我感覺這個答案哪裡怪怪的。不過在第三天的時候有解答了。第三天我問了另一個問題:
Clean Architecture 看起來是設計一個應用程式的架構,所以會需要考慮 I/O(指最外層)。那 Library 能不能也用 Clean Architecture 的方法來設計?
當然能,就當作沒有最外層就好了。
不過我真正要問的是下一個問題:那有沒有可能一個 Clean Architecture 應用程式去「直接」使用另一個 Clean Architecture 的架構。當然我們都可以透過 Domain Event 傳來傳去達成應用程式溝通,這是沒問題的,但直接使用有沒有可能?有的話,那另一個 Clean Architecture 是不是就會被放在最外層?
答案是,對。
因此,Interface Adapter 應該不是做協定轉換,而是做資料轉換。而這個前提下,協定處理和 Authentication(有可能是另一個 Clean Architecture)都應該是放在最外層才對。
軟體架構定義
Clean Architecture 裡提到:
軟體架構是建置系統的人給的形狀。
形式的形式是把系統劃分元件,以及元件布置方式以及這些元件之間溝通方式。
其他參考書之一:建構微服務第 16 章
軟體架構是為了開發、部署、維運和維護與其他 Non-function requirement 而設計的。
需求會隨時時間變化而變化,而知識會隨著時間知道越多,因此 Agile 與 Lean 都有提到延遲決策(Defer commitment),最理想的就是 Just In Time。Change cost 會隨著時間成指數成長,理想上應該只有跟 scope 有關,這也是我們學習軟體架構的終極目標。
雖架構與功能無關,但好的架構要能夠讓使用者看得出系統的功能。
– Teddy 課程所講的,但記得這應該是 Clean Architecture 書裡寫的,待確認。
The ultimate goal of software architecture is to minimize the lifetime cost of the system and to maximize programmer productivity.
– Clean Architecture
Clean Architecture 三個原則:
- 分層原則
- 階層式架構
- Entities: 放核心商業邏輯
- Use Cases: 應用程式邏輯
- 一樣是邏輯,相較寫在 Entities 會比較有彈性,但就會有許多重複程式碼
- Interface Adapters
- Frameworks and Drivers
- 相依性原則
- 上層模組不應該使用下層模組(要注意這裡上下層的意義跟 DIP 說的上下層是不同的)
- 應用 DIP 來達成上層模組使用下層模組
- 跨層原則
- 跨層有分鬆散、嚴格原則
- Ex: Entities 要傳出去,必須要轉另一個物件出去
- 盡可能保持 Entities 與 Use Cases 裡面的整潔
- 分層式架構,同一層的兩個物件能不能互相呼叫,use case 能不能呼叫 use case?可以,但要注意互相依賴的問題。
今天上課的內容比較多沒聽到的,當初一直懶得看 Clean Architecture,今天總算直接聽課程。
聽完是覺得跟我目前設計程式的方法類似,只是看該怎麼放程式而已,因此倒沒有什麼卡關的地方。