序言: 張逸者,70年代生人。軟體開發生涯經曆了從程式員、項目經理、測試經理、開發部長、技術總監到架構師的一個循環,而後程式員,現在是創業公司cto。三大愛好是開發、寫作與閱讀。著譯作包括《軟體設計精要與模式》、《wcf服務程式設計》、《java設計模式》與《恰如其分的軟體架構》。張逸現居于錦官城,與中生代諸君多有往來。張君于設計模式、軟體架構、ddd以及clean code等方面研究尤深。開公衆号而不刷粉,則多數文字并不顯于街市,中生代編輯之以飨讀者,使佳作流傳,善莫大焉!
張逸-bigeye聯合創始人與架構師
從ddd的角度,領域邏輯的分析可以運用戰略方法bounded context。可是,一個問題是:如何獲得bounded context ?
我檢視了許多關于bounded context的書籍與文章,雖然都着重強調了它的重要性,也給出了一些執行個體,卻對如何從需求——>boundex context這一點上語焉不詳。
一個初步設想
我的初步設想是通過繪制場景圖(但并不成熟)。我認為有三種繪制場景圖的方式:商業畫布,體驗地圖和流程圖。我認為,商業畫布可以作為需求分析(尤其針對初創産品)的起點。商業畫布如下圖所示:
采用這種規範化的方式來推導商業模型,可以激發我們的靈感,理清我們的思路,以便我們思考為何要做這個産品,産品應該具備哪些功能。結合優點和缺點、成本等因素,我們可以藉此判斷和決策功能的優先級,進而得到mvp。這個過程需要大量運用即時貼,讓整個商業模型呈現。經過取舍後,就可以針對産品繪制場景圖。此時,場景圖可以采用experience map或流程圖來展現。experience map的例子如下圖所示:
由于商業畫布本身提供了“客戶”項,我們應該建立persona,找準人物角色的特征來“搜尋”需求。繪制了場景圖後,就能夠确定用例了,此時,可輔以atdd幫助确定story。在确定了用例後,可以識别bounded context,并通過context map确定上下文之間的關系。
就我個人感覺,體驗地圖還是從persona的角度設想系統如何使用,考慮它的使用者體驗。它其實符合“場景”的概念。這裡可能還是要考慮:在一個完整的場景中,需要哪些參與者?但是,即使從粗粒度的角度出發,場景都可能存在多個,可能需要繪制多個場景圖來逐漸提煉bounded context。
關于如何運用persona,熊子川在他的部落格《xd關鍵字5:persona》中已有詳細介紹,同樣在他的部落格《agile ux内容政策工作坊》中提出的“消費者模組化”實踐,指出:
為了更好的了解我們選擇的目标消費者,我們需要對消費者進行完整的模組化,即persona。越接近于真實的persona幫助我們更好的了解其使用者目标……persona的重要産出物是一系列使用者目标,對于同一個persona,使用者目标可能有不同,有些目标是基礎核心目标,有些則是衍生性的,例如一個通路網站潛在投資者的核心目标可能是了解成為投資者的過程,而衍生性目标可能是獲得一些關于公司曆史資訊增加信任度。
說明:本圖摘自熊子川部落格
獲得context并劃分領域
假設我們要開發一個電子商務網站,我們就可以通過商業畫布來驅動出這個産品應該具有哪些功能,它的客戶有哪些等,在繪制了場景圖後,可以初步得到這樣的bounded context:
然後,我利用context map得到了各個上下文之間的關系:
這樣,一個包圖的獲得就水到渠成了:
六邊形架構
在識别了bounded context以及context之間的關系後,我們可以運用hexagon架構(cockburn提出的六邊形架構)來展現系統的整體架構。hexagon架構并不深入關注内部邊界中領域部分,僅僅是簡單的劃分為application與domain兩層。但它有助于我們獲得基礎設施層以及相關內建點的包結構。我們要合理地運用六邊形架構。它更貼近應用邏輯架構,并可以驅動我們去發現諸多內建點,尋找內建模式。内外邊界的分離也有助于我們将業務邏輯與應用邏輯分離開。這實際上符合“關注點分離”的架構原則。下圖展現了六邊形架構中常見的port與adapter:
所謂“可視化架構”,是一種利用多種交流方式實作架構知識共享的方法,因而需要團隊成員均參與進來,并以workshop的形式,更多通過即時貼、白闆等工具實作可視化,而非通過繪圖。至少,繪圖不應該成為主要的驅動力,否則,開發人員很難接受。例如,下圖就是我運用hexagon架構,并結合可視化手段分析該電子商務系統得到的應用邏輯架構,它很好地一個展現了hexagon架構的可視化手法。
在這個圖中,直覺地展現了如何與外部的支付系統以及物流系統的內建。例如,圖中展現的port實際上為防腐層(acl)。為何要建立這樣的一個防腐層呢,原因在于:支付與物流常常存在多個供應商,因而需要解除對供應商的綁定,并避免供應商系統的變化造成對電子商務系統的腐蝕。這是切合實際的決策。
倉庫管理流程控制系統
這個電子商務系統需要與倉庫管理系統內建。恰好在《面向模式的軟體架構》卷四的第35頁,給出了一個倉庫管理流程控制系統的案例。書中描述的非功能性需求,即所謂品質屬性包括:
分布性。倉庫管理流程控制系統天生就是分布式的。
性能。倉庫管理流程控制系統不是一個“絕對的”實時系統,但性能仍與業務息息相關。對系統有整體的吞吐量要求,是以系統必須確定所有的運輸指令能夠被及時而有效地運作。
可伸縮性。不同倉庫其大小可能會有很大的不同,是以倉庫管理流程控制系統必須能既支援隻有幾千個箱子的小倉庫,又要支援超過一百萬個箱子的大倉庫。
可用性。許多倉庫操作采用三班倒的24/7模式工作,是以可用性是倉庫管理流程控制系統對業務案例支援的關鍵因素。
假設要設計這樣的系統以支援這些品質屬性。對于分布式而言,書中提出的解決方案是傳統的分布式系統解決方案,即引入broker模式,在本地建立對遠端對象的代理。而對于支援并發的領域對象通路而言,則采用了active object模式,并引入leader/followers并發模型來獲得可擴充。
我沒有打算引入這麼複雜的模式,而僅僅是通過引入消息隊列,并為消息隊列引入路由的方式,來實作系統的分布式。這其中當然會用到經典的publisher-subscriber模式。我對領域邏輯進行了識别,将整個倉庫管理流程控制系統的領域邏輯分為三個bounded context:
庫存管理
物流控制
拓撲管理
整個架構如下圖所示:
對于庫存管理而言,我認為它主要支援商品存放資訊的資料管理,即獲得商品數量、存放位置以及更新這些資訊。對于該上下文而言,操作本身比較簡單,且耗時較短。若出現大規模并發,其瓶頸也不在于擷取或更新倉庫資訊(當然需要通過測試資料驗證),而在于客戶下訂單後向倉庫管理流程控制系統發起的發貨請求。
我将發貨請求放到了物流控制上下文中,除此之外,它還包括收貨以及訂單管理等。同時,對于物流控制與拓撲管理功能,基本上與具體的倉庫形成了一一對應關系。此外,對于發貨請求(或收貨請求),并不要求很強的實時性,這使得對這些請求的異步處理成為可能。
物流控制由于牽涉到收貨和運貨,需要控制倉庫的相關裝置,并按照倉庫的拓撲結構設定裝置的路由。這說明物流控制與拓撲控制存在上下遊關系,拓撲控制是上遊。這兩個上下文可以是customer-provider的關系。但它們之間不應該存在實體邊界。是以,我将這兩個上下文放到了同一個六邊形中,而将庫存管理放到了另一個單獨的六邊形中,以便于它們各自獨立的可伸縮。
在庫存管理與物流控制六邊形之間,我引入消息隊列來應對從庫存管理子系統中轉發而來的發貨請求(發貨請求實則又來自于e-commerce的訂單請求)。原則上,我針對一個實體的倉庫建立一個單獨的消息隊列,是以庫存管理在發送發貨請求時,會根據商品的存放位置以及使用者請求的ip位址,獲得最優的倉庫資訊,然後通過router将消息轉發到正确的消息隊列中。
一旦收到消息,物流控制系統作為消息隊列的訂閱者(或偵聽器)就可以即使處理資訊,進行後續的處理。
針對庫存管理而言,我認為它是一個獨立的實體邊界,是以在可視化手段中,我展現為一個單獨的庫存管理六邊形,如下圖所示:
庫存管理建立了如下端口:
建立了針對rest服務的端口,對應的擴充卡為controller,其目的是支援e-commerce系統。事實上,我們對e-commerce系統進行過分析,獲得的六邊形架構正好與此對接。
建立了針對db的端口,對應的擴充卡為db gateway,它負責通路庫存管理自身的資料庫。資料庫持久化的消息包括商品的基本資訊如sku、商品名、數量等,以及商品存放的倉庫名。
建立了針對queue的端口,對應的擴充卡為message router,負責将發貨請求消息路由到正确的消息隊列。
物流控制與拓撲管理放在同一個邊界中,它是高度可伸縮的獨立系統,為展現它的可伸縮性以及它與庫存管理之間的內建,我在可視化手段中,展現出兩個獨立的六邊形,如下圖所示:
物流控制與拓撲管理建立了如下端口:
針對queue的偵聽器端口,對應的擴充卡為message handler。若有必要,如為了更好的支援并發,也可以在此引入active object甚至leader/followers。
針對rest的端口,對應擴充卡為controller。它主要是為了支援移動終端裝置、web應用,以便于相關人員直接發出發貨或收貨請求。
提供了db的端口。這個資料庫是對應倉庫的專有資料庫,與庫存管理資料庫無關。
提供了針對裝置(指倉庫的裝置,如叉車,箱子,運輸車等)的端口,對應擴充卡為south gateway。
提供了針對配置檔案的端口,對應擴充卡為configurer。此功能是為了支援拓撲資訊的動态配置。
提供了針對外部物流系統的端口,這裡為其建立了shipping的防腐層,使其能夠更好地支援各個不同的物流供應商。
目前,我針對可視化架構與設計的手段仍在完善之中,并已經嘗試在真實項目中實踐以進行驗證,并希望能夠找到足夠簡單的方法,為架構師與開發者提供直覺而又具有體驗價值的溝通方式,并能形成行之有效的設計手段。