September 09 07:29~8:30
▲Pascal這種身材應該算是充血模型,把房子都壓垮了還不充血嗎 XD
緣起
幾天前在臉書DDD台灣社團看到有人問「貧血模型」與「充血模型」的問題,問題大意如下:「假設有一個Product Entity,針對該Entity實作查詢產品功能。」
方法一:將查詢功能做在Product身上
public class Product {
private final ProductRepository repository;
public Product (ProductRepository repository) {
this.repository = repository;
}
public Optional<Product> getProductByName(String productName) {
return repository.findByName(productName);
}
}
***
方法二:將查詢功能做在Use Case身上
public class GetProductUseCase {
private ProductRepository repository;
public GetProductUseCase (ProductRepository repository) {
this.repository = repository;
}
public void execute(GetProductInput input, GetProductOutout output) {
output.setProdeuct(repository.findByName(input.getProductName()));
}
}
***
發問者認為:
- 方法一的Product有getProductByName這個「行為」,感覺符合充血模型(Rich Domain Model)的定義。但是,讓Product直接耦合Repository好像又不太對。
- 方法二將getProductByName從Product身上拔除,升等為GetProductUseCase,拿掉Product對Repository的依賴。但是,如此一來Product身上就光溜溜沒有行為,變成Martin Fowler所說的貧血模型(Anemic Domain Model)。
到底要怎麼做比較好?
***
不是貧血、充血的問題
關於上述問題,Teddy覺得發問者所舉的例子本質上和Product Entity屬於充血模型或是貧血模型並無直接關係,而是和CQRS(Command Query Responsibility Segregation;命令查詢職責分離)比較有關。
在方法一的所謂充血模型範例中,Product身上的getProductByName(String productName)方法,是一個Query(查詢,回傳資料但不會改變系統狀態的操作),它本來就不需要也不應該存在Product身上。從CQRS的角度來看,Entity主要是要表達Command(命令,會改變系統狀態但不會回傳值的操作),因為發問者把Command與Query耦合在Entity身上,才會有「將getProductByName放在Product身上讓Product產生對Repository的依賴」的困擾。
方法二將getProductByName升級成GetProductUseCase,它現在變成一個代表Query的使用案例,如此一來實現了CQRS。把getProductByName從Product身上拔掉,並不會讓Product變成貧血模式,反而可以讓Product專心去負責原本應該表達的「Command Model(或稱為Write Model)」。這才是DDD(領域驅動設計)要套用的地方,應用在Write Model,而不是在Read Model。
在發問者所舉的例子中,Product就只有Query而有沒任何Command,所以將Product的Query拔掉之後,Product身上就沒有其他行為了,才會誤認此時的Product變成了貧血模型。
***
本來無一物
以上情境,讓Teddy想起有一次聽 Kevlin Henney 的演講,他提到一個故事(以下為Google翻譯後經Teddy修改過的中文):.
尊貴的 Qc Na 大師與他的學生 Anton 同行。Anton希望能引起師父的討論,他說:「師父,我聽說物件是很好的東西——這是真的嗎?」Qc Na憐憫地看著自己的學生,答道:「笨學生——物件只是窮人的閉包(closures)。」
被打槍之後,Anton離開了他的師父,回到了他的小研究室,打算研究閉包。他仔細閱讀了整個「Lambda:The Ultimate...」系列論文及其同類論文,並實作了一個帶有基於閉包(closure-based)的物件系統的小型 Scheme 直譯器。他學到了很多東西,期待著告訴師父關於他的進步。
在與 Qc Na 的下一次散步中,Anton 試圖給他的師父留下深刻印象,他說:「師父,我仔細研究了這件事,現在明白物件確實是窮人的閉包。」 Qc Na 的回應是用棍子打Anton,說:「你什麼時候才學得會?閉包是窮人的物件。」
那一刻,Anton開悟了。
Object is a poor man’s closure, and closure is a poor man’s object.
***
下集待續
先講完背景故事,下一集Teddy從DCI(Data, Context, Interaction)的角度,再談貧血模型與充血模型的不同觀點。
***
友藏內心獨白:貧血模型是富人的充血模型,充血模型是富人的貧血模型 。
作者已經移除這則留言。
回覆刪除