領域驅動設計與整潔架構基礎課程筆記之二

今天是領域驅動設計與整潔架構課程第二天的課程。

一樣是記錄關於此堂課程的筆記。

建模後續

  1. 雖然建模需要能夠剛剛解決問題,但對應到 Solution Problem 可能會有衝突,這時就可能會需要因 force 做取捨,如 CAP 定理的取捨,是透過分析需求,了解有更強的 force 來取決要捨棄哪一個特性。
  2. 建模的過程通常不會包含 Domain Event,不然會很難閱讀。
  3. 什麼時候才是塑模完成:Alberto Brandolini: More requirement please

將模型分組

模型分組並沒有絕對的對或錯,只有一些參考的方法如下:

  1. 一個 Aggregate 必須要是一個 transaction,如果需求可以接受最終一致性,那就不需要放同個 aggregate。
  2. 一個 Aggregate 的範圍越大,平行處理就越難,因為會卡在 transaction 的物理限制。
  3. 真實世界的例子幾乎都是最終一致性。
  4. 因為「接受」最終一致性的特性,因此 Aggregate 可以做為最小部署單位,而不需要整個 Bounded Context 一起部署。
  5. Aggregate 不完全是技術的 pattern,也跟 problem model 有關
  6. 切得好,同時 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
  • 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,今天總算直接聽課程。

聽完是覺得跟我目前設計程式的方法類似,只是看該怎麼放程式而已,因此倒沒有什麼卡關的地方。