l

2020年9月8日 星期二

再談以前端為主的思考對於領域模型的影響

September 08 11:25~13:30

▲大部分的人都是「外貌協會」成員,UI當然非常重要,但不適合從UI來驅動領域模型的設計。

***

工商服務

想要看見別人看不到的設計盲點,想要知道怎麼套用設計模式才是恰如其分的設計,想要跟同事針對設計問題吵架時可以嘴砲勝利,歡迎參加第22梯次【Design Patterns這樣學就會了–入門實作班】,招生中。

***

背景介紹

前幾天Teddy談了〈為什麼不要把後端的領域物件直接傳給前端?〉,今天再舉一個同樣是在開發ezKanban所遇到的例子,討論以前端UI為主的思考方式,會如何影響到後端的領域模型,造成不必要的耦合。

約兩周前Teddy和ezKanban團隊開發Tag的功能,讓使用者可以幫看板中的卡片用Tag來分類,如圖1。


▲圖1:卡片上的多個Tags


ezKanban是一個支援多人同時使用的即時協作系統,如果很多人同時操作同一個看板,系統必須自動保持前端狀態同步。如圖3所示,ezKanban使用WebSocket做為後端主動通知的管道。當前端使用者執行某一個功能,會呼叫後端的Use Case。例如,呼叫CreateTagUseCase新增一個Tag。Use Case執行完畢後產生TagCreated Domain Event(領域事件),後端會將這個TagCreated Domain Event(轉成DTO)廣播給在同一個Board的使用者。當前端收到廣播的Domain Event之後,只要依據Domain Event的種類與內容,在前端直接更新本地UI狀態,便可達到狀態同步的目的。


▲圖3:ezKanban透過WebSocket廣播需要即時同步的領域事件

***

前端需要顯示Tag Name

當前端收到後端傳來的Domain Event,會把它顯示在畫面右上角,讓使用者知道系統狀態已經改變。參考圖4,Teddy把feature這個Tag指定給某張卡片,因此前端顯示如下訊息:「Teddy assigned a tag feature to card」。


▲圖4:前端將收到的領域事件顯示在畫面上


要顯示領域事件就顯示啊,有什麼好說的?」問題就出在這顯示的內容上面。ezKanban是採用領域驅動設計(Domain-Driven Design;DDD)+ Event Storming + Clean Architecture + TDD的方式開發,請參考圖5,原本的Event Storming關於Assign Tag的輸入,並沒有tag name,因為把一個Tag貼到Card上面並不需要tag name,只要有tag id即可。但是如果沒有tag name,要發TagAssigned Domain Event的時候會少了這個資料,導致前端無法顯示tag name。


▲圖5:前端將收到的領域事


因此當下ezKanban團隊的第一個反應就是:「把tag name加到AssignTagUseCase的輸入參數,再把它傳給Card,這樣子Card在發Domain Event的時候可以有tag name。

這就是因為前端顯示需求而影響後端use case layer與entity layer的例子。

***

啊不然勒?

「Separation of Concerns」,我們不應該只是因為前端顯示的需求而污染了use case layer與entity layer,特別是entity layer要好好保護它。既然都有了tag id,其實只要在將Domain Event傳給前端的時候,依據tag id 去找出tag name,然後把tag name塞到傳給前端的Domain Event DTO就可以了。最後實作的程式碼如圖6所示。


▲圖6:將領域事件傳給前端前在把tag name塞進去即可

***


不是不重要

這幾天討論前端與後端領域模型的這兩篇文章,以及之前一系列Clean Architecture/DDD談到資料庫是細節,不應該從資料庫來思考軟體設計,並不是說前端、UI、資料庫不重要。它們都很重要,軟體是一個整體,每一層都做好了,才能夠交付end-to-end價值。

但是,還是那句老話:Separation of Concerns。在討論複雜的業務邏輯的時候,就應該以問題領域的業務邏輯為主去思考,而不是從使用者介面或是資料庫表格的設計,來反推業務邏輯與領域物件應該怎麼設計。

把主要的事情確定了,其他次要的事自然會浮現。

***

友藏內心獨白:先後順序不要搞錯,要先脫衣服再洗澡,不要洗完澡才脫衣服。


2 則留言:

  1. 既然都有了tag id,其實只要在將Domain Event傳給前端的時候,依據tag id 去找出tag name,然後把tag name塞到傳給前端的Domain Event DTO就可以了

    對這句話, 有些疑惑
    但又覺得您說的也是正確的
    必須多讀取一次資料庫, 如果情境是大量併發的時候
    會不會造成系統負擔呢?

    回覆刪除
    回覆
    1. 我认为,这个问题可通过底层架构中的自动缓存等方式来解决。

      刪除