l

2023年6月11日 星期日

領域模型不要直接依賴Repository

June 11 19:23~20:38


▲圖1:將Repository放到Entities Layer(錯誤示範XD) 

 


前言

6/5~6/7到客戶家上【領域驅動設計與簡潔架構入門實作班】,有一位學員問了Teddy不少問題,像是:

  1. 為什麼Teddy建議Entities Layer的物件不要直接操作Repository?
  2. 為什麼有人說UoW(Unit of Work)和Repository在DDD裡面算是Anti-Pattern?
  3. 為什麼Teddy沒有用Specification模式?


這些問題,應該是有在下功夫研究DDD的人,才會提出來的問題。針對這三個問題,Teddy分三集來說明,今天先談第一個問題。

***

增加領域模型與測試複雜度

Entities Layer是Clean Architecture存放領域模型(Domain Model)的地方,它應該只表達問題領域的業務邏輯,儘可能與外在世界、框架無關。Repository是領域驅動設計(Domain-Driven Design;DDD)中,用來存取聚合(Aggregate)的設計模式。也就是說,Repository隔離了儲存層,讓它的使用者無需知道儲存聚合的實作細節。

Entities Layer知道Repository又怎樣?首先,這麼作讓Domain Model依賴資料存取介面,雖然這個依賴透過Repository介面做到依賴反轉,但「資料存取」的概念還是洩漏到Domain Model,增加不必要的複雜度。

這種不必要的複雜度增加,可以從測試的角度看出來。針對Entities Layer物件的測試,理想上就是傳統軟體測試所說的單元測試,而且是可以做到「測試隔離」(test in isolation)。這樣子的單元測試,因為與外在世界無關,所以可以跑得很快且可以單獨測試業務邏輯。

看到這裡鄉民們可能會想:「我有學過Test Double(測試替身),我可以在測試案例中注入Test Double,這樣就可以寫出與世隔絕的單元測試。」

使用Test Double雖然可以讓使用Repository的Entities Layer物件做到隔離測試,但是付出的代價就是單元測試變得複雜且可能出現重複程式碼。現在,要測試Entities Layer物件之間,都要在單元測試的Arrange階段先設定Repository替身,這會複雜化且重複Arrange區塊的程式碼。

***

弱化階層式架構

請參考圖1,如果將Repository放在Entities Layer,從Clean Architecture的角度來看,為了滿足相依性原則,BoardRepository必須是一個介面,然後在Interface Adapters Layer實作BoardRepositoryImpl。如此一來,雖然滿足相依性原則,卻造成了BoardRepositoryImpl跨層依賴於BoardRepository。雖然在鬆散式階層架構中允許跨層依賴,但Teddy認為這會弱化了階層式架構的一致性。

***

其他人怎麼說

請參考圖2,在IDDD書中也提到,不要將Repository注入給Aggregate。


▲圖2:Teddy的FB廢文1 

 

如圖3所示,在Unit Testing一書中也提到,在領域模型中直接使用資料庫(相當於Repository)會造成程式碼過度複雜。


▲圖3:Teddy的FB廢文2 

 

***

結論

只要程式可以正確動起來,設計沒有絕對的對、錯,但有合適程度的差別。關於這個問題Vladimir Khorikov有一篇很棒的blog: <Domain model purity vs. domain model completeness (DDD Trilemma)>,鄉民們可以參考。

***

友藏內心獨白:領域模型越乾淨越好。

沒有留言:

張貼留言