l

2020年12月29日 星期二

領域驅動設計學習筆記(13):幫Event Storming加上使用者介面

Dec. 29 09:43~11:39


問題

這個月底Teddy去客戶家上了三天的【領域驅動設計與簡潔架構入門實作班】,有好幾位學員都問到:「Event Storming怎麼表達使用者介面設計?」使用者介面是軟體的外觀,關係著軟體好不好看以及好不好用,的確是軟體開發中一個重要的議題。

今天來談談這個問題。

***

Event Storming簡介

Event Storming(事件風暴)在領域驅動設計中經常被使用於讓利害關係人與團隊共同協作,用以釐清業務流程、討論商機、風險、區分bounded context、建立共通語言,乃至於設計與實做軟體的一種方法。Event Storming發明人Alberto Brandolini在《Introducing EventStorming》書中介紹這個方法,圖1為Event Storming工作坊所使用的主要便利貼,不同顏色有不用的用途。


▲圖1:Event Storming常用便利貼顏色與意義


依據《Introducing EventStorming》書中的介紹,Event Storming有三個階段,分別為:

  • Big Picture:依據時間發生先後,在一面「巨大」的牆上,貼上Domain Event、External System、People,並藉此流程觀察Hot Spot(對商業流程有模糊不清、商機、痛點等)。
  • Process Modeling:基於前一個階段的產出,加上Read Model、Policy、Command。
  • Design:加上Aggregate。

一般網路上常看到的Event Storming產出物如圖2所示。


▲圖2:Event Storming示意圖

***

UI/UX設計

▲圖3:《Introducing EventStorming》書中的「The picture that explains everything」


參考圖3,在Event Storming方法中原本就涵蓋了UI/UX。這張圖可以這樣解讀:

***

使用者要使用軟體系統執行某項任務,為了執行這項任務,他需要參考資料以協助他決策判斷。因此,他在真實世界看到一個畫面,這個畫面是由Read Model(綠色便利貼)所產生。例如,你去餐廳吃飯,為了點餐(執行任務)你需要看到菜單(畫面),這個餐單可能是中文、英文、日文,可能只有文字或是圖文並茂,但菜單背後都是該餐廳的菜單資料(Read Model)所後製而成。如何設計給客人看的菜單就牽扯到UI/UX的議題。

使用者獲得足夠決策資訊後,向系統發出一個命令(Command,藍色便利貼),這個命令交給系統中某個聚合(Aggregate,黃色便利貼)去執行,執行完畢後發出一個領域事件(Domain Event,橘色便利貼)。領域事件可能引發後續的處理流程,稱為Process、Rule或Policy(紫色便利貼),後續處理流程再執行另一個命令。例如,告訴服務生(Aggregate)你要點餐(Command),服務生完成點餐動作後產生Order Placed(Domain Event)。Order Placed觸發一個後續處裡的流程—請廚師出菜,因此呼叫「製作餐點」這個Command。

領域事件也可能是由外部系統(External System,粉紅色長方形便利貼)所產生,例如餐廳可能與Uber Eat或Foodpanda合作,由外部系統觸發Order Placed領域事件。

最後,領域事件代表「狀態改變」,因此可從領域事件產生Read Model,再透過這個Read Model製作使用者介面。例如廚師可以從Order Placed領域事件產生「出菜順序」的畫面,作為製作餐點的決策參考資料。

***

範例

看完上述對於「The picture that explains everything」 的說明,在Event Storming中加上使用者介面就很容易了。圖4展示在ezKanban中,Create Project與Move Board To Project這兩個命令(套用Clean Architecture之後,這兩個命令被實作成兩個Use Cases)。使用者介面加在綠色便利貼之前,為了執行Create Project,使用者看到一個畫面,按下Add按鈕,跳出一個New Project 對話框,輸入Project Name按下Submit,把team id與project name當作參數呼叫Create Project使用案例。該使用案例透過Team Aggregate產生一個Project,然後發出Project Created領域事件。該事件代表有一個新的Project產生,因此使用者在執行Move Board To Project使用案例的時候,就可以從畫面看到剛剛新增的Project。


▲圖4:ezKanban範例,在Event Storming加上使用介面。

***

使用者介面如何產生

團隊的UI/UI的人員,可以沿用原本熟悉的工具來繪製使用者介面或是UI操作流程圖,只要將最後的結果截圖貼在Miro上面即可(Teddy與ezKanban團隊使用Miro來繪製Event Storming),不會影響原本的UI/UI設計流程。

最後工商服務一下,想學習DDD + Event Storming + Clean Architecture + TDD,歡迎報名泰迪軟體的領域驅動設計與簡潔架構入門實作班

***

友藏內心獨白:無縫接軌。

2020年12月27日 星期日

領域驅動設計學習筆記(12):Event Storming與User Story

Dec. 27 20:05~21:45

▲ezKanban團隊的開發流程


問題

有朋友問Teddy:「Event Storming做完之後,如何轉成user story與團隊溝通?」

在ezKanban開發過程中,團隊的開發流程從2020年暑假開始從Scrum轉成Kanban。團隊不再額外寫In order too [獲得什麼好處]….As a user, I want to [做什麼事] 這種格式的user story,而是直接將Event Storming的Command變成Use Case放到product backlog裡面。

傳統OOAD的Use Case其粒度一般來講比user story要大,一個Use Case可能有多個執行路徑或多個劇情,每一個執行路徑可視為一個user story。所以直接以Use Case來取代user story,有可能會違反敏捷與精實開發的小批量生產原則—開發的功能儘量切小,可獲得較短的交期(lead time)。不但bug會比較少,品質較好,也可以快速收集使用者回饋。

ezKanban團隊在Event Storming的過過程中不是從CRUD的角度來尋找領域事件,而是找出任務導向(task-oriented)的領域事件,並且採用TDD/BDD/SBE的方式實作Use Case。因此從實作面的角度,也是以step by step、piece by piece、scenario by scenario的方式完成使用者需求,並不會因為沒有將Use Case改寫成user story就造成溝通或是開發上的問題。

***

範例說明:Move Lane使用案例

圖1:Move Lane Command


視覺化是實施看板方法的第一條原則,因此ezKanban軟體自然要提供設計工作流程的功能,讓使用者可以新增Stage(垂直的工作階段)與SwimLane(水平的工作階段)。除此之外,使用者在視覺化工作流程的時候經常需要調整工作流程的順序與層級,因此Move Lane(移動工作階段)使用案例也很重要,如圖1所示。

使用者可以直接用滑鼠將最上層的工作階段拖拉到想要的順序,如圖2所示,使用者正在把Reviewed移到Ready to Deploy之後。


圖2:實作完成的Move Lane畫面

***

實作Move Lane使用案例

因為採用TDD方式開發,因此先撰寫第一個Use Case的測試案例—移動最上層的Stage。請參考圖3,這個測試案例代表最常見使用情況的happy path。


圖3:Move Lane的第一個測試案例

一開始這個測試案例根本無法編譯,因為都還沒寫production code。接著就按照TDD的流程,以最簡單的方式撰寫讓測試案例可以通過的production code,再透過重構來改善設計。

因為移動工作階段這個功能比較複雜,光靠Use Case的驗收測試無法涵蓋所有可能的移動情況,因此以specification by example的精神,以單元測試來代表移動工作階段的各種可能狀況,單元測試案例執行結果請參考圖4。


圖4:Workflow身上的moveLane方法的單元測試


針對move lane的各種狀況,ezKanban團隊目前一共寫了9個單元測試,從單元測試的名稱就可以很清楚看到所想要涵蓋的情況,例如以下兩個單元測試代表移動工作階段的邊界條件測案例。

  • should have correct order when move substage0 from order0 to order0 in the same parent
  • should have correct order when move substage4 from order4 to order4 in the same parent

單元測通過之後,回頭跑Use Case測試案例,順利通過就完成Move Lane使用案例的第一個happy path。

接著撰寫第二個Move Lane使用案例的驗收測試—should succeed when move second root stage containing sublane to first root stage,這個案例比較複雜,代表兩個平行的stage A 和stage B,其中stage B底下還有其他的stages,然後把stage B移到stage A底下。

這兩個驗收測試都通過之後,就可以撰寫rest controller,完成後再寫前端的react程式,把前後端接起來這個Move Lane功能就完成了……第一版。

上述提到兩個驗收測試都是移動Stage,還需要增加移動SwimLane的驗收測試,測試通過後整個Move Lane使用案例才可以算是完成。

目前ezKanban團隊的TDD僅限於use cases與entities這兩層的物件,這兩層以外的開發並沒有採用TDD,而是採用傳統code first方式。

***

通用語言表現在程式碼

領域驅動開發有一個重要的觀念—Ubiquitous Language in Code,能夠做到這個層次,開發團隊本身,以及開發團隊與stakeholders(尤其是domain experts)的溝通也就沒什麼問題。

Teddy整合了DDD、Event Storming、Clean Architecture與TDD/BDD/SBE,首先透過event storming建立ubiquitous language 與domain model,接著透過clean architecture與TDD將ubiquitous language落實在程式碼之中。以上這些都做到之後,如果覺得還是需要撰寫傳統的user story,那也沒關係,就去寫吧。

***

友藏內心獨白:到了離,就不用守了。

2020年12月18日 星期五

領域驅動設計學習筆記(11):Bounded Context(上)

Dec. 18 13:01~17:35

▲ezKanban的儀表板畫面


只有一個Context的問題

Bounded Context在領域驅動設計(Domain-Driven Design;DDD)中是一個很重要個概念,今天先從實作面談一下有無Bounded Context的差別。以往傳統的物件導向設計,整個問題領域就是一個巨大的Context,因此在solution domain自然產生一個很大的物件類別圖, 如圖1所示。

▲圖1:Class diagram,取自JCIS


為了簡化程式設計,這個object graph之內的物件關係往往被設計成雙向連結。例如,圖2是一個用傳統OOAD所設計出的看板系統,其簡化後的類別圖:

  • Team:團隊,每一位使用者至少屬於一個團隊。
  • Project:專案,一個團隊有零到多個專案,可以從團隊得到屬於該團隊的專案,也可以從專案得到所屬的團隊,團隊與專案兩者之間是雙向參考關係。
  • Board:板,代表一個看板。一個專案可以有零到多個看板。專案與板之間也是雙向參考關係。
  • Workflow:工作流,一個板可以有零到多個工作流,兩者之間也是雙向參考關係。
  • Lane:道,代表工作階段。Lane有兩個子類別,分別是代表垂直的Stage,以及代表水平的Swimlane。Lane與Workflow之間也是雙向參考關係。

▲圖2:Class diagram,取自JCIS


程式開發人員可以用team.getProject().getBoard().getWorkflow().getStage() 得到某個Stage。相反地,也可以從stage.getWorkflow().getBoard().getProject().getTeam()得到該Stage所屬的團隊。這麼做的好處是,物件之間靠著參考 (reference) 整個串在一起,操作物件感覺很方便。有優點一定也有缺點,主要的缺點有三項:

  • 相依性太重:任一物件修改都可能會影響其他物件,容易產生漣波效應。
  • 物件永久性與平行處裡的能力受限:為了達到整個系統交易(transaction)的一致性,物件與關連式資料庫經常透過多的表格與外鍵關係來實現。當使用者更新系統物件時,可能需要鎖定多個表格,因而降低系統的平行處理能力。
  • 物件屬性與責任膨脹:整個問題領域只有一個Context,如果問題領域夠大,相同的類別在不同的功能中可能有不同的屬性與責任。例如,從Team與Project的角度,Board只是一種被分類的「東西」。除了board id與board name以外,它們並不在乎Board裡面的實質內容。但是,從Board、Workflow、Lane的角度,Board的內容是整個功能的核心。因為整個系統只有一個Context,因此只有一個Board類別,所以Board類別身上就會包含所有「客戶端」所需要的資料與方法。

***


Bounded Context

▲圖3:兩個bounded contexts


如圖3所示,有了bounded context概念,如果把原本的問題分成兩個bounded contexts:

  • Project Management
  • Board Management

Board同時出現在兩個bounded contexts之中,但是有著不同的意義與屬性。Project Management的Board只需要有board id、board name、project id、team id即可。而Board Management的Board才需要workflows等資料。

如此一來,當Project Management移動Board,就與Board Management的Board沒有關係,解決了上述只有一個Context所造成的三個問題:

  • 相依性太重
  • 物件永久性與平行處裡的能力受限
  • 物件屬性與責任膨脹

區分Bounded Context還有其他好處,像是在多個Bounded Context中找出企業最具競爭力的core domain,以便區分業務優先順序以及安排開發資源。

***

代價

區分Bounded Context所付出的代價就是當企業流程或資料跨越不同Bounded Context時,需要協調這些活動與資料。例如,當Board名稱在Project Management被修改時,需要通知Board Management更新狀態。或是當Board Management新增一個Board時候,也要通知Project Management產生一個Board。

***

友藏內心獨白:要因地制宜。

2020年12月2日 星期三

泰迪軟體2021年上半年開課時間表

Dec. 02 09:29~09:57

▲因應疫情Event Storming (事件風暴) 活動改用線上工具miro


2020年轉眼就要過去,又來到年底年底排課表的時間。今年因為新冠肺炎疫情的緣故,雖然泰迪軟體的生意沒有特別忙,但Teddy在下半年把主要空閒時間都供獻給ezKanban軟體的開發,持續增強【領域驅動設計與簡潔架構入門實作班】的課程深度與累積更多實作經驗。

以下是泰迪軟體2021年上半年課表:

Scrum敏捷方法實作班

  • 假日班:2月20、21日(六、日)。

領域驅動設計與簡潔架構入門實作班

  • 平假日班:1月15、16、17日(五、六、日)。

敏捷開發懶人包:物件導向技能

  • 平日班:2月23日(二)。

Design Patterns這樣學就會了--入門實作班

  • 假日班:3月13、14、20日(六、日、六)。

單元測試實作班

  • 假日班:3月27、28日(六、日)。

看板方法與精實開發實作班

  • 平假日班:5月8、9日(五、六)

Design Patterns這樣學就會了--進階實作班

  • 平假日班:5月28、29、30日(五、六、日)。

軟體重構入門實作班

  • 平假日班:5月14、15、16(五、六、日)。

例外處理設計與重構實作班

  • 平日班:6月22、23日(二、三) 。

***

以為下課程活動照片。


▼領域驅動設計與簡潔架構入門實作班


▼看板方法與精實開發實作班課程照片

螢幕截圖 2017-12-12 10.08.45


▼「單元測試實作班」課程照片。

螢幕截圖 2017-12-12 10.14.10螢幕截圖 2017-12-12 10.15.07


▼「敏捷開發懶人包:物件導向技能」課程照片。

***

友藏內心獨白:2021年還請鄉民們繼續支持。

2020年11月30日 星期一

DDD TW 2020 Conference一日遊

November 30 21:50~23:30

▲DDD是拿來踩在腳下實踐的,不是放在嘴邊打嘴砲


抵達會場

11/27~28日是第一屆台灣領域驅動設計年會舉辦的日子,數月前受到老友張國昭的邀請,在會中給個talk,11/27日八點半出門前往會議場所北投會館。

話說這個北投會館位置還真有點小偏僻,平常上班時間很少出門的Teddy,太高估台北的交通,開車走走停停到了會議場所已經錯過了9:00的第一場活動。

更慘的是,諾大個地下B1停車場,幾乎停滿車,而且也沒有燈號指示空位。沒方向感的Teddy繞了快10分鐘好不容易在角落的地方看到兩個空位,還沒停好車另一個空位立刻也被停走。

▲幾乎沒空位的北投會館停車場

***

離開停車場之後,到達會館入口,居然又花了快10分鐘才找到會議舉辦地點。先辦理報到,工作人員剛好是老朋友Max,算他倒楣,一早先被Teddy念了一頓。這活動場地也太不友善了啊。

***

參加活動

原本第一場想聽「返璞歸真:街口的"唯服務"之路」,等報到完畢都9:40了,只能作罷。本次活動同一時間一共有三場演講同時進行,分為產品軌、技術軌、流程軌。

不包含自己的演講,Teddy一共聽了四場演講如下:

  • DDD X Architecture by Clark:聽說早上技術軌的場地出了點狀況,所以剛進入技術軌會場有點兵荒馬亂。Teddy來的晚,只搶到最後一排的位置,加上會場的投影機解析度 非常 有點差,投影布幕也 很小 不夠大,所以嚴格講起來Teddy幾乎看不到講者的投影片內容 Orz。幸好Clark口條很清楚,說明當問題領域很大的時候,軟體架構應該如何應對以便管理這些眾多的bounded context。

整場演講Teddy印象最深刻的一點是Clark對於中台的解釋,這個詞彙據說是中國阿里巴巴集團所創。之前也有朋友跟Teddy討論過中台,但翻遍Teddy家中所有英文書,完全找不到中台的說明(廢話,因為這是阿里巴巴所提倡的,應該找簡體中文書),而Teddy平常也沒在追蹤中國的軟體技術發展,所以腦中只知道禪寺,卻不知中台為何?

以下是Teddy聽完Clark的解釋對中台所產生的理解—當你的問題領域非常巨大的時候,例如阿里巴巴集團這種規模,集團內各個子集團或部門所開發的系統,很可能會重複開發。例如,對岸很流行的發紅包服務,阿里巴巴集團內不同應用系統都有機會使用發紅包服務,如果不加以管理,很可能整個集團內不同單位重複開發相同的發紅包服務。因此,把這種不同單位都會使用到的服務(但又不是傳統所謂基礎建設平台的服務,例如email、SMS)抽離出來放到介於前台與後台之間,稱之為中台。


▲如果有帶望遠鏡出門就好了

***

  • 領域驅動設計參考過程模型 by 張逸:這場演講播放講者張逸事先錄好的影片,講者參考RUP的做法,介紹他自創的統一領域驅動設計參考模型,希望提供一個框架讓DDD的落實變得更具操作性。Teddy覺得這個做法滿有趣的,必須要整合很多不同的技術,才能把它們放到一個框架中又不會顯得過於突兀。

但是,用過RUP的人都知道,RUP最後失敗之處就在於它太過複雜。任何對於某種統一模型的嘗試,必須要小心不要步入RUP的後塵。

這場活動Teddy還是坐在原本場地最後一排的位置,所以講者的投影片也是看不清楚。只能等主辦單位公布活動錄影,再找時間了解細節。

***

  • 實踐 Clean Architecture(實作高可用性的軟件架構) by Gelis:下午原本要繼續留在技術軌聽好友Rex的演講,但因為Teddy自己在北科教Clean Architecture,同一時間流程軌的「實踐 Clean Architecture」更引起Teddy的注意,所以就「脫軌」跑到另一個場子。

這場演講,Teddy並沒有聽到什麼和Clean Architecture特別值得留意的內容,也沒聽到演講副標題「實作高可用性的軟件架構」在什麼地方。不知道是不是又是場地的問題抑或是什麼神奇的超自然現象,蒙蔽了Teddy的雙眼。

講者最後demo一個Visual Studio外掛程式,宣稱可自動產生符合Clean Architecture的程式碼。但Teddy不解的是,如果沒聽錯的話,講者使用該外掛的步驟,需要先匯入資料庫…….這和Teddy所理解的Clean Architecture好像不是同一個版本的Clean Architecture。

***

  • 金融業導入領域驅動設計之應用實例 by Otto:14:00~14:50這一場Teddy跑到B1的便利商店,買了杯咖啡在B1找個位置準備自己的演講。回到會場之後,聽了Otto介紹國泰金控導入DDD的過程。很可惜這場演講只有20分鐘,而中間又因前一場延遲而晚了幾分鐘開始,所以講者實際上只有10來分鐘的時間分享。整個故事滿有趣的,國內公司真正應用DDD開發系統應該也是極少數,值得參考。

***

Teddy的演講

Otto的演講結束後下一場終於輪到Teddy的演講:「DDD + Clean Architecture:從需求到實作」,介紹 Teddy 與北科大 ezKanban 團隊開發一個線上多人互動看板系統的過程,演講內容包含 Online Event Storming + DDD + Clean Architecture + TDD。

寫到這裡已經沒力了,Teddy的演講投影片在此,有興趣的朋友可自行觀看。演講活動有錄影,待主辦單位剪輯好公布之後再請鄉民自行服用。


***

辛苦了

謝謝DDDTW社群舉辦第一屆台灣領域驅動設計年會,雖然Teddy跟幾位義務當工作人員的老朋友碎念了一整天,大家真的辛苦。

***

友藏內心獨白:下次會更好。

2020年10月30日 星期五

能力 = 紀律 * 技能

Oct. 31 17:29~18:10


在北科資工所兼課這幾年,Teddy在每學期第一堂課都會與學生約法三章,要求學生上課:

  • 不能遲到
  • 不可以使用手機、筆電等3C產品
  • 要參與課堂互動

如果同意再修這門課。

***

曾經有一次在檢討考卷的時候學生拿手機拍照被Teddy發現,學生的理由是:「我看不到投影幕上面的字,所以拍照起來放大」。Teddy只能佩服這位學生的臨場反應能力真的有夠敏捷,但什麼理由都不是理由,違反規定一律請出教室。

還有一次在分組討論的時候有學生使用手機,他的理由很正當:「我在找討論內容的資料」。沒什麼好說的,還是請他離開教室。

最近一次是學生把手機放在桌上,用手指滑了幾下手機。這位學生的理由是:「我沒有玩手機,是手機螢幕上有灰塵,我把灰塵清走」。好吧,這位愛乾淨的同學,下次上課再見。

***

Teddy是台北工專電子科畢業的,在那個時代,念書除了學習技能(skill),也很講求紀律(discipline)。能力再好,做事態度隨隨便便,無法成為一位專業的工程師。

幾年前讀了《Management 3.0: Leading Agile Developers, Developing Agile Leaders》,書中提到:

discipline * skill = competence(紀律 * 技能 =  能力)

Teddy看了是相當認同。紀律,不是要無腦的聽老闆命令,或是拍老闆馬屁,而是要遵守團隊或專業領域中做事的共同規範。例如,你自認自己能力很強,沒把團隊的DoD(Definition of Done)當一回事;開會姍姍來遲;不遵守團隊的working agreement。這種人,除非真的是千年一遇的軟體奇才需要特別加以保護,否則不用也罷。

***

在學校上課,學習技能是基本的,但學習紀律的機會就比較少。Teddy兼任的課程剛好都是敏捷開發相關課程,敏捷開發講的就是軟體,在課程中加入一些「紀律」的要求,也是理所當然之事,絕對不是Teddy心理變態想要刁難學生。

***

友藏內心獨白:有時候不須講理由,只需承擔結果。

2020年9月10日 星期四

Clean Architecture之CQRS Pattern

September 10 14:20~15:20

▲跳出盒子思考,有時候跨層存取也是OK滴


緣起

前幾天Teddy在讀《Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#》,看到第3章有一個段落談Code Structure Within a Bounded Context。這是一個很有趣的議題但談得人並不多,既使是這本書也沒有講得很詳細,但其中有一張如圖1所示的圖引起我的注意。


▲圖1:每一個end-to-end功能橫切軟體架構的每一層,圖片節錄自《Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#》,53頁。

***

Clean Architecture

看到圖1讓Teddy想起《Clean Architecture》第34章談到四種區分package的方法,回頭把這一章又讀了一次,看到圖2這段文字:


▲圖2:《Clean Architecture》 311頁。


當年Teddy在讀這段文字的時候並沒參透這個問題:為什麼在CQRS架構中就特意允許跨層參考?」雖然Teddy知道CQRS架構模式但自己沒有實際實作過,但這次讀懂了。

前陣子讀了一本奇書:《CQRS》。如圖3所示,書中明白建議:

  • 領域驅動設計(Domain-Driven Design;DDD)只需要應用在那些會改變系統狀態的Command。換句話說,只需要在Command套用DDD與Clean Architecture。
  • 針對不會改變系統狀態的Query不需要套用DDD,甚至根本也不要設計領域物件,直接讀取資料庫然後產生前端需要的View Model即可,這樣速度更快。


▲圖3:節錄自《CQRS


看完這本書之後Teddy就把ezKanban的use case改成Command與Query。昨天進一步和ezKanban團隊把帳號管理專案的package改用package by feature的方式來管理,如圖4所示。

▲圖4:ezKanban的帳戶管理專案採用package by feature


在重構成package by feature的過程中,GetUserUseCase這個Query與其它會改變系統狀態的use case分別放到不同的package。此時GetUserUseCase已經完全沒有對於User這個領域物件的依賴,如圖5所示它直接透過新的UserQueryRepository查詢資料庫並回傳UserDto給前端。換句話說,CQRS的Query端,故意省略domain model與資料庫之間的轉換,跳過domain model直接讀取資料庫。也就回答了圖2的問題。


▲圖5:直接操作資料庫並回傳DTO給前端


***

友藏內心獨白:繞了一大圈啊。

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。在討論複雜的業務邏輯的時候,就應該以問題領域的業務邏輯為主去思考,而不是從使用者介面或是資料庫表格的設計,來反推業務邏輯與領域物件應該怎麼設計。

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

***

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


2020年9月7日 星期一

PoEEA之Server Layer

September 07 22:36~23:20

▲PoEAA書中提到四種表達領域邏輯的模式


緣起

好一陣子沒幫部落格文章增加新分類,今天這一篇開啟【盡信書不如無書】這個分類,紀錄Teddy讀書時看到一些自己覺得怪怪的地方。

***

哪裡怪
前幾天Teddy在準備Asian PLoP 2020的演講題目:〈Pattern-Based Problem Solving: One Pattern at a Time〉,翻到《Patterns of Enterprise Application Architecture》第九章Domain Logic Patterns,看到Service Layer這個模式,如圖1所示。


▲Service Layer模式,節錄自《Patterns of Enterprise Application Architecture (PoEAA)》,133頁。


這本書2003年出版至今已有17年,當年Teddy讀到這個模式、看到這張圖,並沒有什麼感覺。但因為這幾年學了Clean Architecture,前幾天再次看到這張圖,當下就覺得不對勁:「Data Source Layer怎麼會畫在軟體架構的最核心?」。

***

資料庫是細節

Clean Architecture四層架構如圖2,資料庫屬於最外層,並非如PoEAA所畫的位於最核心。軟體架構的核心應該是Entity Layer,也就是Domain Model Layer。


▲圖2:《Clean Architecture》建議的四層架構,畫成同心圓(圖片來源在此


在階層式架構中,把資料庫畫成底層或是核心層,在N年前也算是常見的畫法。但以現在的角度來看,對照圖1與圖2,應該可以很清楚看出來,圖2的觀點比較正確。資料庫、使用者介面、框架、驅動程式等,都屬於細節,屬於階層式架構的外層,並非是核心部分。

其次,傳統階層式架構所說的Service Layer,就是Clean Architecture裡面的Use Case Layer。Teddy現在覺得Use Case Layer比較具體,因為Service這個字有很多種含意,因此用Service Layer來代表應用程式所提供功能或服務的邊界,好像有點那麼不是很直覺(就跟這句話一樣XD)。

***

讀書不是照單全收

PoEAA是一本很棒的書,書中所整理的模式很多到今日依然適用且日久彌新。但也有一些模式的內容,因為時代演進需要稍微修正,像今天介紹的Service Layer就是一個例子。

讀書不是照單全收,有時候同意,有時候疑惑,有時候反對。理論上,除非這本書有問題,否則同意的時候應該居多。疑惑時可能是自己離作者太遠,尚無法理解書中的微言大義,但也能不排除內容有誤的能性。

反對,代表自己有看法,這個看法與書中不同。自己的看法可能是錯的,也可能是對的。能夠說出不同,也算是一種讀書的層次。

***

友藏內心獨白:挑毛病也要講出個道理。

2020年9月6日 星期日

為什麼不要把後端的領域物件直接傳給前端?

September 06 20:50~22:22

▲ezKanban的tag功能


前幾天有一位朋友問Teddy:「Clean Architecture提到不要把Entity Layer的物件直接傳到UI端,但真的有需要跨層的時候把物件都轉成DTO再往外傳嗎?」

***

以ezKanban為例子

ezKanban是一個支援多人同時使用的看板系統,圖1為其簡化版的領域模型。Board代表一個看板,其中包含了若干個Workflow(工作流),每個Workflow可以有若干的Lane。Lane區分成兩種,垂直的Lane稱為Stage(階段),水平的Lane稱為Swimlane(泳道)。Card(卡片)可以被放到Lane上面,並在不同的Lane之間移動。


▲圖1:ezKanban簡化版領域模型


假設Board被直接傳到前端,前端設計出如圖2所示的畫面。

▲圖2:ezKanban畫面


到目前為止沒什麼問題,前端UI物件模型與後端的領域模型大致上是一致的。一直到有一天客戶有新的需求:

  • 希望增加Tag的功能來將Card分類
  • 每個Board都可以有自己的Tag,不會與其他Board共用Tag
  • 一張Card可以有多個Tag

如果從UI的角度來思考:「Board包含Tag,然後這些Tag才可以被指定到屬於這個Board的卡片裡面。」為了能夠在畫面上顯示Tag,前端的人很可能會要求後端將Tag加入Board物件成為它的屬性,變成如圖3所示的關係:


▲圖3:因為UI的需求,導致Board與Tag產生關係

***

問題出在哪裡?

直接把Entity Layer的物件傳給前端違反單一責任原則。Entity Layer的物件之所以會存在,是為了解決或是表達問題領域的重要概念與商業邏輯。如果把它們直接傳給UI,拿來當作顯示畫面之用,Entity Layer的物件就擔負了兩種責任:

  • 表達商業邏輯與概念
  • 表達顯示邏輯

如此一來Entity Layer的物件就可能因為兩種不同客戶端的需求改變而跟著改變,換句話說Entity Layer與UI產生耦合,導致程式難以理解、修改與維護。

***

如何解決?

回歸到Clean Architecture與領域驅動設計(Domain-Driven Design;DDD)的角度,先確定商業邏輯,至於使用者介面是屬於細節,等Entity Layer與Use Case Layer確定之後,再交給Presenter產生View Model來滿足前端顯示的需求即可

回到幫Card貼Tag的需求,把Tag歸類為Board的屬性並不合理,因為Tag並不是要貼在Board上面,使用者只需要知道這個Board裡面建了多少種類的Tag,再拿這些Tag貼在卡片上面。也就是說,Tag只需要知道它自己屬於哪一個Board的單向關係即可,Board根本不需要知道Tag,如圖4所示。


▲圖4:Board, Card與Tag的關係


至於前端所需的所有資料,如圖5所示另外設計一個BoardContentViewModel。這個View Model是由Presenter為了前端所需而動態產生,Entity Layer並沒有一個這樣的靜態Model。

▲圖5:ezKanban管理Tag畫面


最後的領域模型,Tag物件不屬於Board,使用者可以獨立新增與修改Tag,也很容易直接將Tag指定給Card,請參考圖6與圖7。


▲圖6:ezKanban管理Tag畫面


▲圖7:ezKanban顯示Card上面多個Tag

***

友藏內心獨白:UI歸UI,Domain Model要分明。

2020年8月21日 星期五

三種團隊互動模式

August 21 18:15~19:01


前言

Teddy跟著ezKanban團隊兩年的時間,帶著幾位北科資工研究生開發ezKanban系統。當初是以DDD與Clean Architecture為研究主題當作切入點,這兩年來雖然有點進度,但軟體本身卻還沒達到Teddy認為可釋出的標準。

這個兩年的lead time真的有點長,今年七月初放暑假時,Teddy決定利用暑假時間和ezKanban團隊採用remote mobbing的方式一起開發。學生在學校實驗室,Teddy在家裡,透過Skype分享桌面的方式開發ezKanban,一個多月下來頗有進展。

***

幾個禮拜前在Amazon亂買書,買了一本《Team Topologies: Organizing Business and Technology Teams for Fast Flow》,有一天晚上睡不著翻了一下,沒想到還挺有趣的。書中提到四種團隊型態以及三種互動模式,特別適合軟體開發公司。今天用Teddy這兩年來和ezKanban團隊的合作模式,解釋書中提到的三種互動模式。

***

X-as-a-Service

將另一個團隊視為一種服務來使用,團隊之間只有最少的協作。這種情況發生在學生遇到問題詢問Teddy的時候,此時Teddy-as-a-Service,提供學生某種解答,這個過程中雙方並沒有很多協作關係。

***

Facilitating

協助另一個團隊釐清阻礙,這種情況發生在兩周一次的sprint review。Teddy會看學生的設計與程式碼,觀察有沒有什麼設計問題是學生沒有看出來的,並與他們討論並訂定後續修正計畫。下個sprint review會繼續相同步驟,經過迭代的過層,系統設計品質越來越好。

但是,因為兩周review一次兩小時,說真的能夠看到的程式碼真的不多,因此每個迭代的增量幅度相對來講就比較有限。另外一個問題是,因為學生還在學習的過程,很多Teddy沒有看到的地方,雖然程式可以動,但設計通常都存在一些大大小小的問題,長期而言阻礙系統的可維護性。

***

Collaboration

兩個團隊彼此緊密合作。在今年暑假之前,Teddy和ezKanban團隊的關係幾乎不存在Collaboration(協作)。之前也幾度思考過是不是跟學生一起寫程式,但又擔心如此一來學生偷懶,過於依賴Teddy,搞到後來變成Teddy幫他們做碩士論文研究 Orz,因此就作罷。

但隨著ezKanban慢慢成形,但又無法達到Teddy心中的釋出要求,有種恨鐵不成鋼的遺憾。幾經思考,加上今年因為因為疫情的緣故,泰迪軟體生意比較清淡,Teddy空閒時間也比較多,因此決定「撩落去」,利用暑假時間一起合作開發。

***

成本不同

這三種互動模式,適合解決的問題不同,成本也不同。對Teddy而言,開發軟體如果可以和團隊緊密合作當然是效果最好的。畢竟Teddy也比學生多活了20幾年,還是比他們更能夠看出設計不合適之處。

除了學生獲益,Teddy也從這個過程中獲得很多design bad smells的寶貴範例,平常可能想破頭都不一定生的出來這些例子。這是一個雙方都互相學習的過程。

很多時候,想要成事,還是需要走到前線,親自動手。

***

友藏內心獨白:If you stop coding, you stop learning, by Kent Beck.