l

2020年8月4日 星期二

3C採雷記

August 03 15:19~16:00

螢幕截圖 2017-08-03 15.52.07



Teddy的指導教授曾經說過:「要害一個人,就送他一台電腦」,這真是一句至理名言。

***

成功經驗

去年底買了新MacBook Pro用來替換使用多年的MacBook Air。新電腦跑起來很快(廢話),同時開Chrome(四、五十個tabs)、Safari、Parallels跑Windows 10加PowerPoint加Word加Visual Studio 2017社群版、Eclipse、IntelliJ都還「hold得住」。用了一陣子之後開始想,是不是應該升級外接螢幕,從24吋HD解析度升級到27吋4K。

因為新MacBook Pro只剩下USB-C接口,想找一台有支援USB-C輸入的顯示器。找了好久前一陣子才決定買了ASUS MX27UC,用到目前為止很滿意。

後來想多買一個外出電源轉接器,原廠那個「白豆腐」太大也太重,加上長達兩公尺的USB-C電源線也不好收納。找來找去找到Innergie PowerGear 45瓦USB-C筆電充電器,瓦數只有原廠87瓦的一半多一點點,還好實際使用之後發現可以正常充電而且電池也都維持在100%狀態,外出使用應該是沒問題。

***

採雷經驗

七月到台中上「Scrum敏捷方法實作班」,到了之後才發現場地的投影機只有HDMI介面,而Teddy只有Apple原廠的USB-C轉VGA轉接器,臨時拿Erica的筆電來用。還好上課資料都放在Dropbox裡面,無縫接軌的順利上完兩天課程。

課程結束後開始尋找合適的USB-C轉HDMI轉接器。當初是趁著Apple原廠打五折的時候買了USB-C轉VGA和USB-C轉USB-A轉接器,現在恢復原價之後。





9月16、17、23(六、日、六)
***

友藏內心獨白:。

2020年7月17日 星期五

領域驅動設計學習筆記(9):Query和Read Model

July 17 12:40~13:48

▲圖1:解釋所有事情的一張圖,取自《Introducing EventStorming


背景介紹

在Alberto Brandolini的《Introducing EventStorming》書中,有一張圖用來解釋他對於採用event storming塑模問題的想法,如圖1。


▲圖2:解釋所有事情的一張圖,取自《Introducing EventStorming


把圖1用event storming展開,出現如圖2所示的流程:

  1. 使用者執行Aggregate身上的Command。例如,在看板系統中,使用者新增看板(Create Board)、移動卡片(Move Card)。
  2. Aggregate執行完Command後發出Domain Event,用來代表系統狀態發生改變。例如看板已新增(BoardCreated)、卡片已移動(CardMoved),
  3. Domain Event也可能由External System所產生,例如,收到銀行傳來的付款確認(PaymentConfirmed)事件。
  4. 剛剛上述行為屬於Write Model,也就是執行Command改變系統狀態並產生領域事件。但使用者不只會改變系統狀態,也需要讀取系統資訊,以決定要執行哪一個Command。表達讀取資訊的模型稱為Read Model,它可以從改變狀態的領域事件而轉換出Read Model。
  5. 將這個Read Model傳給UI,可以做出最後使用者看到的系統畫面。
  6. 使用者看到系統畫面,或更抽象的表示,看到Read Model,從而做出決定,執行下一個Command。
  7. 圖2中的Policy表示發生領域事件之後需要後續處理的事情。例如,當使用者註冊成功,則寄發啟動帳戶的email。

***

Event Storming + Clean Architecture

▲圖3:cleanKanban範例,將Read Model簡化成Use Case的Input


Teddy在上〈領域驅動設計與簡潔架構入門實作班〉教導學員用Event Storming來建立Domain Model與Ubiquitous Language。在實作面,套用Clean Architecture,並採用TDD/SBE的方式來撰寫程式。因為Clean Architecture最核心之處在於Entity Layer與Use Case Layer,把UI和DB都當成技術細節,因此在塑模時先不考慮。

所以Teddy把原本Event Storming的綠色便利貼,由原本的Read Model,簡化為使用者看完Read Model之後,決定要執行哪一個Command所給予的參數。例如,顧客去麥當勞櫃台點餐,他看到Menu上面有1號餐、2號餐、N號餐等,這個Menu就是Read Model。最後使用者決定要點一份5號餐,外加一份大薯條,這些資料就是Input。

***

加上Query

如果只是實作後端,從Clean Architecture的角度來看,上面這些分別代表Command、Aggregate、Domain Event、Policy、Read Model(Input)以及UI 的便利貼,也就夠了。但實際開發完整的軟體需要接上前端,此時發現少了一種便利貼:Query

Command是會改變系統狀態不傳回值的操作,Query則相反,不會改變系統狀態但會回傳值。圖1與圖2中,Domain Event—>Read Model之間,應該還需要補上Query,這樣子參考Event Storming轉成程式碼的時候就可以有一對一的對應關係,如圖4所示。


▲圖4:ezKanban範例,由Query (Get Home Content) 產生Read Model (或稱為View Model),再將此Read Model顯示在UI上。


在圖4中,Query並不像Command需要傳給Aggregate,其實作方式可以透過撰寫Read Repository以及搭配Projection (將原本適合用來寫入的Write Model資料映射成適合讀取的Read Model資料)。

***

結論

加上Query之後,整個Event Storming的便利貼就更完整,也可以和UX/UI設計師討論人機互動的問題。

但是,加上Query與UI,整個模型也變得更加複雜。從軟體架構的角度來看,是否在軟體開發初期就需要增加這樣的複雜度,也是一個可以討論的議題。當然可以只針對Core Domain以及支援商業流程的主要Read Model優先設計畫面,其他次要部份採用迭代與增量的方式來實作,也是一種選擇。

***

友藏內心獨白:Model 不是越詳細越好,只要能表達problem domain我們所關心的問題就好。

2020年7月7日 星期二

領域驅動設計學習筆記(8):幫Event Handler取個好命字

July 07 13:32~14:50

▲幫Event Handler取個好名字


最終一致性(Eventual Consistency)

領域驅動設計(Domain-Driven Design;DDD)引入Aggregate,更新同一個Aggregate需要保證交易處裡的一致性達到ACID要求。跨Aggregate之間則是透過領域事件通知,達到最終一致性(eventual consistency)即可。


▲圖1:Board與Workflow兩個Aggregate


以cleanKanban系統為例,圖1表示兩個Aggregate—Board與Workflow的關係,一個Board擁有零到多個Workflow。因為兩者是獨立的Aggregate,當新增Workflow之後,Board需要收到WorkflowCreated領域事件通知,才能夠建立它與Workflow之間的關係,如圖2所示。


▲圖2:Aggregate之間透過領域事件更新狀態


***

取名字

圖2是Event Storming(事件風暴)產出圖表,Event Storming定義了Domain Event、Command、Aggregate、Read Model、Policy、External System等便利貼,但是沒有代表處裡事件的Event Handler。

基本上Event Handler是一種程式實作細節,是落實Policy的實作方式。為了落實DDD的ubiquitous language in code,Teddy希望Event Storming也可以用表達Event Handler的便利貼,以便於實作時可以看著Event Storming圖表來寫程式碼。


▲圖3:新增不同顏色便利貼並直接用EventHandler當名字


Teddy一開始嘗試增加一種新顏色的便利貼,並把它貼在要透過Domain Event達到最終一致性的兩個Aggregate之間。如圖3所示,WorkflowEventHandler負責聽取WorkflowCreated領域事件,然後呼叫Commit Workflow使用案例建立起Workflow與Board之間的關係。

現在試著讀一下這個模型的通用語言:


Teddy執行Create Workflow使用案例,執行完畢後產生Workflow Created領域事件。WorkflowEventHandler一收到Workflow Created,執行Commit Workflow建立剛剛新增的Workflow與它所屬Board的關聯。


2020年6月22日 星期一

UML Class Diagram小抄

June 22 10:12~10:38

▲小抄第一頁


明天要上第十梯次【敏捷開發懶人包:物件導向技能】課程。這門課主要是介紹物件導向觀念、SOLID原則、DBC,以及OOAD流程快速導覽。以往在課堂上Teddy並沒有講UML,當課程進行時如果有學員不清楚再補充說明。

前陣子參加一場碩士班口試,發生學生有一張基本的UML class diagram畫錯,這件事一直放在Teddy心中。UML 2.5.1版規格書一大本754頁,一般軟體工程師並不用去記住所有細節,但一些常用的基本符號大致上還是需要知道,至少做到團隊溝通時不要造成誤會。

這兩天花了點時間整了兩頁的UML class diagram常用符號,想說明天上課還是稍微補充說明一下,對學員應該還是有幫助。

▲小抄第二頁


兩頁的UML Class Diagram小抄可在此下載,有需要修正或加強之處歡迎提供建議。

***

友藏內心獨白:有點重要又不會太重要。

2020年6月20日 星期六

為什麼不反駁?

June 01 09:20~10:26

▲學生發問的問題,圖由學生所繪製


好問題

Teddy這學期在北科教軟體架構課程,設計一個專案給學生練習,題目是:「用DDD + Clean Architecture + TDD,開發看板系統的後端。」這禮拜四在北科上課,學生DD問:「Calculate cycle time這個Command要傳送給哪一個Aggregate?還是應該把這個Command實作成一個Application service?」

由於這整學期Teddy都在教學生如何把Event Storming的產出透過一致性、固定的步驟,在套用Clean Architecture的前提之下,採用TDD方式將設計轉成程式碼,以達到Ubiquitous Language in Code的目的。因此當學生認為他們找不到合適的Aggregate來接收Command,Teddy直覺反應就是:「因為這個Aggregate還沒有存在你們的domain model裡面,是你們還沒開始找,而不是沒有合適的Aggregate。

後來Teddy詢問每一組,得到的回答大都是「沒有合適的Aggregate,直接把這個Command做成Application service。」只有一組將這個Command送給Workflow Aggregate,但Teddy覺得送給Workflow並不合適,因為Workflow沒有Card移動的資料,無法計算cycle time。

***

公堂之上大膽假設

最後Teddy提議增加Cycle Time Calculator這個Class,由它負責計算cycle time,並寫了一段程式碼給學生參考。

Teddy:Cycle Time Calculator是一個沒有狀態的service類別,service有兩種:domain service與application service,它應該屬於domain service,放在clean architecture的entity層。

學生DD:可是老師你說接收Command的對象是Aggregate,而Aggregate是有ID與狀態的類別。但是Cycle Time Calculator是service,它並沒有狀態,這樣不是違反了之前design level event storming的規範?

討論到這裡Teddy不得不稱讚一下這位同學,因為這個問題Teddy之前也沒想過…

Teddy:將design level event storming轉成clean architecture的程式碼的方法是我們自己定義的,我們可以加上一個規則「Aggregate與Service都可以接受Command」。

這種說法感覺有點硬坳,但乍看之下也還說得過去。

***

忙到忘記

回家之後過了一會Teddy才想起來:「這個問題之前和ezKanban團隊開會時曾經討論過!」當時Teddy告訴團隊:「計算cycle time是在Reporting bounded context,它不是看板系統的core domain。因為它是產生報表的domain,是唯讀的,所以我們不一定要套用DDD或是OOAD的方式來建這個reporting domain,也許可以用transaction script就可以產生報表。

但後來Teddy沒有進一步與ezKanban團隊review他們實作reporting domain的程式碼,所以細節上他們到底如何實作Teddy也不知道。

***

討論無身分高低

在這禮拜四上課時Teddy曾詢問ezKanban的人如何實作Calculate cycle time?只得到「設計成service」的答案。後來Teddy發表一堆「公堂之上大膽假設」的言論,ezKanban的人也沒跳出來 打槍 提醒Teddy,這個問題之前曾經討論過。

可能學生預設認為上課只是來聽老師開講,把所謂的「正確答案」記下來就好,不要質疑老師,也儘量不要跟老師對上眼、說上話。

但Teddy的課程不一樣,第一個禮拜上課Teddy就告訴學生:「叫我Teddy就好,不用叫我老師。別的課我不知道,但在我的課堂上我們的關係是平等的,不是上下階級。我只是在課題上扮演老師的角色,你們扮演學生的角色,僅此而已。修這門課我會問很多問題,你要願意與我互動,再來修這門課,不然就請退選。

▲課堂上老師與學生的關係是A還是B?


雖然第一周上課Teddy就把話說得很清楚,但學生心裡還是千百個不願意,可能還是怕被老師擺一道,萬一多說話得罪「方丈」日子就不好過了。但專業討論不應有身分高低,如果學生心中有看法,但礙於身分不敢說,這其實是陷老師於不義,阻礙了課堂上探索知識的機會

***

反思

敏捷開發強調要透明,唯有透明,探索調適才可能發生作用。很顯然Teddy與學生之間的互信可能還不夠,導致學生有看法不好意思公開表達。

不過,學生當下沒提出來也不能排除另一個原因,就是學生自己也忘了,根本沒想那麼多。

***

友藏內心獨白:腦補得太厲害。

2020年5月13日 星期三

學習總在下課後

May 13 18:11~18:59

▲別人的水總是比較甜,江山易改,喵性難移。


行為是否改變

成立泰迪軟體這幾年,遇到企業內訓課程,有些HR問Teddy:「能不能幫忙在課堂上看看誰比較認真,或是出考卷作為訓練成果評量,這樣學生才比較會專心上課。」

這些事當然都可以配合,但我總是跟對方說:「我覺得最好的評量就是同仁上完課之後行為有沒有改變,考試、打上課成績,頂多就是因應公司規定滿足交差的形式要求。」

上完課之後行為有沒有改變,是一件需要後續追蹤的工作,也是一件耗費時間與金錢的工作,絕大多數的公司都覺得:「公司已經出錢辦教育訓練,上完課之後同仁應該就具備那種能力才對。」

除非課程內容難易度不高,否則要求學員上完課之後立刻變成高手是不太可能的事情。不可諱言,有些天賦異稟或是努力很久終於時機成熟的同仁可以在上課當下「開悟」,課程結束之後行為為隨之改變,但大部分的同仁可以藉由上課「獲取新知」就已經很不容易了,要求他們上完課後立刻可以排除萬難做出有意義且持久的改變,實屬不易。就好像看了場精采的電影,觀眾(同仁)在心中對劇情與演員的表現留下深刻印象。但要讓觀眾看完電影之後就變身為導演,可以開工拍戲,那還需要長時間的努力。

下課之後回到工作上,面對的又是現實問題,被一大堆代辦事項追著跑,如果沒有外力協助,光靠員工自己很難有辦法靜下來思考如何改善。與其花錢辦大量的訓練課程,還不如思考是否有可能把部分上課費用拿來找顧問、教練,在課程結束之後和團隊一起,協助團隊改變行為,並且將改變後的行為固定下來,變成新的工作方式

***

終身學習

奧修在《草木自己生長》提到:「知識是借來的,而學習是你自己的;知識是透過文字、語言、和觀念,而學習是透過經驗;知識總是一個結束,你知道了它,它就結束了;學習永遠沒有結束,它總是在途中;學習是一個過程…」

上課可以獲得知識(收集名詞),但學習必須靠自己,它是一個終身的過程。很多教育訓練成效不彰,因為來上課的人頂多只想獲得(收集)知識,而不想學習。教的人也只是講述知識,沒有鼓勵學習。

Teddy相信:「學習在下課之後才真正開始。」所以教學的主要目的,應該是讓學習者具備下課後持續學習的能力。為了達到這個能力,需要介紹基本的領域知識以及設計適合的體驗活動,讓學習者在一個比較輕鬆的情境下獲得這些知識與體驗。

***

友藏內心獨白:學習是軟體開發的瓶頸。

2020年5月12日 星期二

等一個人春天

May 12 14:25~15:50

▲浪浪也會自己生長。


緣起

Domain-Driven Design: Tacking Complexity in the Heart of Software》這本書出版於2004年,當時Teddy正在念博士班,忙著準備資格考與論文,根本不知道有這本書。後來好像曾經聽過DDD這個名詞,當時自己一直把它腦補成MDA(Model-Driven Architecture)。因為自己對MDA沒什麼好感,當然對DDD也就沒興趣 Orz。

***

一面之緣

2013年,在這本書出版9年之後,Teddy忘了在什麼機緣之下買了它。翻了翻書的內容,覺得:

  • Model-Driven Design和以前學過的OOAD(物件導向分析與設計)建domain model的內容大同小異。
  • Ubiquitous Language是一個新名詞,但是它目的和Alexander的Pattern Language很像—A common ground for communication,而且比Pattern Language還要簡單。

以上這兩個模式(Patterns)算是DDD的核心,先被Teddy給「鄙視」一番。繼續往下看,覺得比較「有用」的模式是:

  • Aggregate,將一群緊密相依的物件放在一起,這是一種人為的模組化單位,可以彌補一般程式語言在Package與Class之間的空白。
  • Context Map,表達bounded context之間的關係。

然後呢?就沒有然後了,書就被Teddy丟在一邊。

***

再續前緣

三年前指導教授想把他手邊的軟體架構這門課交給Teddy來教,Teddy參考了近10本軟體架構的書,最後選了當時剛出版的《Clean Architecture: A Craftsman's Guide to Software Structure and Design》。教軟體架構和DDD原本沒什麼直接的關係,因為實作Clean Architecture核心層(Entity Layer)需要建立domain model,而Teddy又不想只是使用原本OOAD的方法,想起DDD關於建立domain model的一些設計模式,於是又回頭探索DDD。

又過了一年,有一天Teddy在YouTube聽一個DDD演講,內容和講者是誰已經忘了,但是聽到一句ubiquitous language in code,好像被雷打到一樣,突然醒過來。這種感覺,就好像當年六祖慧能聽到金剛經的「應無所住而生其心」一樣,當下頓悟XD。

Alexander的pattern language使用對象是,人用它來設計住宅以及都市規劃。如果將pattern language應用在軟體開發上,則可以想像成套用了一群(數個)設計模式來解決一個大的設計問題,這也是原本Teddy對於ubiquitous language的看法。

但是,ubiquitous language不只是這樣。DDD的Model-Driven Design與Ubiquitous Language這兩個模式是一體兩面,如果只把它們應用在「概念層次」,只用來作為領域專家與開發團隊溝通的工具,那就只發揮了DDD一半的功力。還有另一半,而且是打通任督二脈的另一半,就是要將Ubiquitous Language表達在程式碼裡面。所以光是只談ubiquitous language還不夠,加上in code,才是畫龍點睛,才是幫佛像開光。

***

自然生長

Ubiquitous language in code,同樣一句話,當時的Teddy聽了有感覺,換成別人就不一定。就算是Teddy本人,如果早個幾年聽到這句話,也不一定有感覺。

奧修在《草木自己生長:禪的真隨》引用禪師齊內林的話:「靜靜地坐著,什麼事都不做,當春天來臨,草木就自己生長。」雖說「什麼事都不做」,實際上並不是「什麼事都不做」。而是以平常心,認真去做好每一件該做的事情

等到時機成熟,春天到來,草木自然生長。

***

友藏內心獨白:不能揠苗助長啊。