超越DDD的創新思想:開發即設計思想

10月有幸參加ThoughtWorks組織的《領域驅動的微服務架構設計實戰工作坊》培訓課程,盡管課程時間只有短短一天,但老師們的熱情講解為我帶來了非常多的靈感和啟發,而這種靈感和啟發不是用錢可以買到的。結合最近兩年多來參與應用系統和應用架構開發實戰所積累的經驗,本著不迷信專家的立場,借鑒當前比較新的架構設計和開發理念,本文提出作者自身兩年來有關所從事領域技術發展的思考和方法論(以下僅代表個人觀點,與所在公司無關)。

言歸正傳,領域驅動設計(DDD)和微服務架構(MSA)近年來非常火熱,尤其是在互聯網公司的生產實踐過程中。微服務架構為互聯網公司自身業務的發展壯大提供了技術支撐,日漸顯現出解決復雜業務需求的威力。更多的IT公司無論是創業公司,還是傳統IT企業或非IT企業的技術支持部門,都將微服務改造視為系統架構發展的藍圖方向,趨之若鶩,意圖搶占技術高地,傳統的SOA改造思路日漸式微,微服務(MSA)化已成IT公司技術治理和架構轉型的公認趨勢。包括傳統金融部門的開發者們正在非常積極和熱情的擁抱新技術、新思想。

微服務模式.png

微服務東西雖好,盲目運用,缺乏配套,副作用會很大。自上而下,各級領導者一直在追問一個問題,銀行間市場如何進行微服務改造,按照怎樣的方法論來劃分微服務,如何指導設計開發實踐?是先開發試錯還是先定設計?銀行間市場系統眾多,非常龐雜,想要完全梳理清楚并非易事,甚至涉及到跨組織問題。本文認為尚需要結合實際,因地制宜的通盤考量,制定自上而下的整體改造方案。開源微服務Spring Cloud技術體系雖然非常成熟,Dubbo也被很多公司采用拿來主義,改造應用,但是不結合金融市場具體業務盲目運用,可能帶來巨大的潛在風險。金融設施以安全為先的策略在任何時候都不過時,尤其金融市場一般涉及巨額資金(以百萬甚至以億為單位交易)交易的情況。

領域模型.png

DDD領域驅動設計尚存在一些爭議,(基本判斷)個人認為DDD當前的核心問題是針對軟件開發導致軟件結構越來越混亂,越來越復雜,越來越難以維護的問題,開出的藥方超越了當前眾多公司的發展階段和實操水平。當下很多公司仍然是軟件修修補補或者推倒重新設計開發(而這正是DDD所判斷失準的地方),尚且能夠滿足業務需求,保證及時交付。廉頗老矣、尚能飯否,故在這個階段過分強調DDD,反而會使得整個開發設計體系無所適從,類似無頭蒼蠅到處亂撞。準確說,當前大多數傳統金融公司軟件開發經驗尚且薄淺,缺乏積累和人才積累,盲目運用DDD技術開發,會極大增加開發成本。DDD對開發者素質要求非常高,甚至達到了專家級要求,主要反映在三個方面:

  1. DDD的設計和開發成本非常高。首先需要雇傭領域專家和掌握DDD設計與開發方法的開發者,在開發之前必須前置領域設計流程,在項目建設工期非常緊張的情況下,DDD將導致開發效率降低,開發難度增大,開發工作量劇增的問題,不利于項目建設過程中的貫徹和堅持;
  2. DDD缺乏成熟的IT大廠實踐、分享和技術支持。目前社區資源(解道Jdon)不足,開源工程(例如:github/micro-company, github/ddd-leaven-akka-v2)和大型實踐項目比較少。了解和掌握DDD的軟件工程師鳳毛麟角(面試時問:你知道什么是DDD嗎?不知道)、不同公司業務場景各不相同、真正理解銀行間市場業務的領域專家和全面掌握DDD設計的專家就更少了;
  3. DDD的思想范式相對CRUD、MVC過于復雜,概念繁多,過于抽象,表述不夠清晰,難以理解,不易被初級開發者掌握。DDD的范式流派目前主要有EDA、CQRS、Event Sourcing、In-Memory、DCI、CSP等流派,它們之間互有沖突,不分伯仲,各自從某一個角度出發,解決部分問題,但也引出了其它新問題,例如數據一致性危機,事務危機和代碼復雜度問題,在當前絕大多數公司和業務場景下,必須混合使用,目前尚未出現大一統的理論架構。當前仍缺乏成熟的配套技術以降低開發者學習和運用其的難度和成本。主要體現在缺乏類似Spring這種現象級別的成熟框架,允許一個在校生學習一個月就基本可以進行生產級的代碼開發工作。當前尚且不夠成熟的DDD框架AxonFramework、AKKA或國產JdonFramework等學習成本比較高,不易被缺乏開發經驗的工程師快速掌握應用。

應用架構與技術突破


微服務架構作為一種架構模式,它提倡將單一應用程序劃分成一組小的服務,服務之間互相協調,互相配合,為用戶提供最終價值。每個服務運行在其獨立的進程中,服務與服務之間采用輕量級的通信機制相互溝通(通常是基于HTTP的RESTful API)。每個服務都圍繞著具體業務進行構建,并且能夠被獨立地部署到生產環境、類生產環境等。此外,應盡量避免統一的、集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言、工具對其進行構建。無論是微服務、領域化還是之前的敏捷開發都強調微服務的核心要義是強調根據業務發展、持續調整現有架構,優化現有代碼,以保持架構和代碼的生命力,這也是當前傳統金融公司最缺乏的經驗積累,如果不從組織架構和開發方式上做調整,煙囪式的、系統林立的格局(現狀:近百個系統,數據搬來搬去)、推倒重建的生產方式就不會打破。從這個角度來說應用架構、數據架構和技術架構三分是好的開始,正如陸、海、空三軍的劃分一樣(目前中國有五大軍種還包括火箭軍和戰略支援部隊)、陸軍中有陸航和兩棲師,空軍中有防空部隊和空降兵,海軍中有海軍陸戰隊和海航,各個軍種又下轄自己的特種兵部隊,形成你中有我,我中有你的格局。應用架構不能只關注應用和領域設計、更應該在基礎業務、技術層面有所突破和積累、技術架構組不應僅僅提供技術支持,也要為各種應用場景提供標準的、規范的和穩健的技術解決方案,數據架構組不是只審審數據表設計、可視化當前的數據字段信息,吆喝項目組完成數據遷移工作,更重要的是從應用和技術角度厘清數據流,以數據可視化平臺提供數據的追蹤服務和數據基礎設施服務(統一的多租戶數據庫服務)識別和約束項目建設的非法活動,減少項目組的數據開發和管理成本(每個項目組都必須養一個數據專員)。當然架構三分是遠遠不夠的,也帶來了新問題和新挑戰,所以需要統一的中央軍事委員會聯席會議制度,現代戰爭不是某個軍種就可以打贏的,需要多兵種聯合作戰,軟件開發亦如是,必須將技術、設計和數據統一協調推進,以高效率、高質量推進系統建設工作。

傳統架構設計流程是先業務架構、再系統架構,后技術架構。其優點是關注點分離,劣勢是設計往往滯后開發,無法明確表達系統架構怎樣支撐業務架構,關注點分離導致信息割裂和障礙。DDD試圖解決業務架構和技術架構一致性問題,關注核心問題域。問題是對于技術實力雄厚,經驗積累豐富的團隊,業務非常成熟穩定的系統來說,這種優先奪取城市(核心業務),再奪取農村的方案無疑是最佳選擇,而對于技術基礎薄弱,業務系統缺乏梳理,如同亂麻,開發團隊經驗總體不足,在建系統尚未成熟的情況來說,優先土地革命,走農村包圍城市,武裝奪取政權的路線才是上策,也即是優先搭建基礎技術框架和平臺,逐步明確企業自身意圖在領域實踐中實現的技術和業務目標,通過一個城池一個城池,由點到面的方式消化邊緣業務,最終解決核心業務的領域化問題。

系統建設挑戰


對企業來說,業務線隨著時間的推移,大多數都會演變得越來越復雜(凡客是反例),以阿里為例,其業務從最初的1688(阿里巴巴)B2B到淘寶C2C,再到天貓、飛豬等B2C、以及阿里云、螞蟻金服和菜鳥等平臺,企業為了生存發展,其業務線越來越復雜;因為未來的不可知性,也會變得越來越不確定,以中金所的股指期權業務為例,其本身是一種應用成熟的風險管理工具,作為金融創新工具,期權的定價包含了對市場所有情況的預期,為市場參與者提供了一種更有效率的投資手段,但該業務因為2015年股災發生而停滯擱淺。在軟件開發過程中,我們不可能找到一個通用的準則,以解決軟件開發面臨的所有問題,尤其是引入未來這個不確定性變量之后,也就意味著根本上不會存在銀彈級解決方案。軟件工程的不確定性貫穿其整個生命周期(lifecycle),軟件的不確定性包括需求、產品、設計、開發和工程團隊自身的不確定性。當架構師已經無法完全掌控系統業務和代碼設計、軟件建設的危機的種子就埋下了。優秀的開發團隊在某種程度上能夠彌補架構師業務能力的不足,團隊之間的互信和溝通是最有競爭力的制勝武器,在制度和技術控制下將溝通結果顯式化的表達和記錄是避免失控的有效手段,最好這種表達是通過技術手段自動完成的,人工參與就會引入時滯問題,DDD劃分問題域和方案域的方法則是解決軟件業務復雜性的根本手段。

當前微服務和DDD非常強調軟件的復用,微服務重視服務復用、DDD重視領域模型的復用。過分強調代碼復用是一種走火入魔,試圖通過一成不變的模型套用不斷變化的業務需求是緣木求魚。試想想,現在很多公司尚做不到在舊有代碼基礎上修改、不斷升級維護支撐新業務需求,那軟件的復用意義又從何談起。盲目追求軟件代碼級別的復用,只會消耗開發者和設計者的精力,只要業務不斷發展,那么軟件復用終將是一場黃粱美夢。

用心思考,直達問題本質

面臨挑戰,技術進步是一個重要的突破口。僅僅只有技術進步,但思想認知不跟進,妄圖在舊有體制機制下修修補補或大刀破斧急功近利,就可以建立新中國,則只能是洋務運動和戊戌變法,徒勞無功。解決中國問題的道路千千萬萬,但只有共產主義一條道路走通了,就是因為它的領導者和踐行者抓住了不同階段中國問題的本質特征,對癥下藥。接下來,本文將重點介紹一種策略,一種思想,即開發者優先的策略,開發即設計思想;提出一種范式和多種方法,即新范式和新方法。

開發者優先策略


科技是第一生產力,那么在軟件這個工程領域中唯有軟件工程師是真正的第一生產力,軟件的設計可以不準確,軟件的目標可以不明確,但是沒有軟件開發者努力地完成代碼編寫每一行代碼,并在工作中不斷修復和糾正錯誤設計,矯正目標,優化代碼質量,解決代碼bug,就無法按時并保證高質量交付軟件產品。反觀架構師和領域專家更應作為支持者和服務者為項目的建設建言獻策、為項目未來發展提供思路,針對當前建設過程中暴露的問題進行集中會診,給出改造方案,并推動落實。架構師和領域專家不可能發現和解決所有問題,軟件開發人員是核心生產力,我們需要開發人員自查自糾,提高憂患意思,防微杜漸,并且不斷學習充電,保證整個團隊整體的知識進步水平保持線性。雇傭領域專家的成本非常高昂,并不符合傳統金融公司(瘦死的駱駝比馬大,有錢很摳,還要各種審批)目前的實際現狀,這些公司的開發主體仍然以開發商為主,開發商以5年經驗以內的初級開發人員為主,自有人員大多數是年輕潛力股,尚不足以主導復雜系統的設計與開發工作。在系統林立,項目眾多的現實面前,每個項目組擁有的自有人員極其有限,甚至就一個,項目之間的溝通成本極高,各種會議和瑣事雜事極度消耗自有人員精力。雖然項目建設投入很高,開發商團隊整體招募的質量比較堪憂,開發商中所謂十年工作經驗的高級開發工程師經常寫出不符合其經驗水準的代碼(開發商人員10年工作經驗需要大打折扣)。所以為了保質保量的完成系統建設,必須堅持以開發者(自有和外包)為中心,全心全力發揮開發人員的學習主動性、調動積極性、通過不定期的技術和業務培訓,增加開發人員的獲得感和技能,糾正開發過程中的各種問題,將缺陷遏制在開發階段,解決這一當前開發和交付過程中最主要的矛盾。

傳統瀑布模型形成職能部門:需求部門、設計部門、開發部門、測試部門、運維部門。原則上部門之間只需要在交付物傳遞時做很少的交流溝通工作,就可以完成系統的開發運行。這種方式極為經濟,也確保每個職能部門工作的確定性和內聚性,最大程度的減少溝通成本,如果知識傳遞過程中不存在變異,瀑布模型無疑是最高效的開發交付模式。現實情況往往是,交流溝通過程中經常形成雞同鴨講的局面。Scrum敏捷開發受制于部門組織架構難以有效實施(或效果不明顯,問題更多,畢竟傳統開發方法和工作思維已經固化),而且敏捷開發并不能從根本上解決知識傳遞過程中的變異問題。Scrum總體只適合于市場競爭劇烈,業務需求并不十分復雜,需要快速將產品推向市場,組織架構調整迅速的互聯網公司,金融系統的需求文檔可以做到幾百頁,上千頁,業務需求難以迭代,導致開發周期非常長,不適合Scrum的應用場景。領域驅動設計DDD則試圖通過通用語言(Ubiquitous Language)來避免信息失真,一個團隊保持一種聲音,但DDD的沒有解決的核心矛盾是由誰主導通用語言的確立?需求人員、設計人員、領域專家還是開發者?Eric Evans在其《領域驅動的設計》一書中強調領域模型是軟件項目通用語言的核心,通用語言的詞匯包括類和主要的操作名稱,開發人員應該使用基于模型的語言來描述系統中的工作、任務和功能。確保團隊內部所有交流以及代碼中堅持使用通用語言,在畫圖、寫東西,特別是講話時也使用通用語言,通過不斷的重構代碼保證代碼與通用語言和領域模型的一致(幾乎不可能完成的任務)。作者強調對話溝通是優化通用語言的最佳方式,簡言之溝通是建模的必要前提,模型最終以鮮活的代碼、文字和重點突出的簡化圖(UML是不必須的)共同表達。而實際工作過程中受制于公司的組織架構和參與人員的知識水平,導致溝通經常不充分,不愉悅,成本高昂,在沒有嚴格遵循DDD設計者初衷的前提下使用DDD最終只會生產出畸變的領域模型和不能真正理解的偽通用語言。現實中開發者的語言模型過于細碎和抽象,領域專家的模型偏重業務領域,開發人員難以理解和貫徹,需求人員往往基于知識慣性,濫用業務術語,無法分離和聚焦重點,沒有人主導通用語言的建設過程,簡直類似“壞的民主”,是一件非常糟糕的事情。在這種情形下,只有以開發人員主導的通用語言和領域模型才能保持生命力,開發人員必須學習使用業務術語建立代碼模型,畢竟開發人員開發的模型最如實的反應了當前的生產實際,其反映的信息失真度最低。

脫離具體業務和技術工作經驗的高級工程師不是好的領域專家。開發者優先強調以開發者為中心重新梳理和構建軟件開發過程,以及在保持原有組織架構體系下,維護業務需求人員、測試人員、架構設計師的職責和權威,使之緊緊圍繞開發人員開展設計和指導工作,不斷質詢開發者有關設計和開發的問題,確保他們能夠對開發模型負最終責任,而非開發人員。測試人員可以通過集成測試提出模型設計和業務表達的諸多問題,倒逼開發者采取TDD的開發方法,不斷優化設計、重構代碼和功能測試,最終實現理想的通用語言、領域設計模型以及應用系統。

開發者優先的策略,即以開發者為中心,強調開發者的貢獻和作用,也突出了開發者應該承擔的責任和義務。在項目實戰中,該策略能夠保證資源優先向前線開發人員提供,開發人員猶如伊拉克戰爭的美軍基層士兵,能夠直接呼叫炮火支援,降低溝通成本,提高開發效率。

開發即設計思想


開發和設計在邏輯上是非常和諧的兩個過程,但在實際工作中經常產生矛盾。在建筑領域中,也有實施和設計之間的矛盾,但是在軟件業,這種矛盾體現的更為激烈。因為,建筑業和軟件業有一個關鍵的區別,設計的結果90%可以映射建筑的結果。建筑業中,設計人員和實施人員的技能領域界限很明確,語言統一,分工明確。一座別墅可以邊蓋邊設計,對于上海中心這樣的摩天大樓必須先設計再建設。如果事先不做事無巨細的設計,經過無數次討論和重繪建筑設計圖紙,那項目注定只有一個結局就是失敗。但是軟件業中則不是如此,設計的結果經常無法正確的指導實踐,在實際開發的過程中,為了解決各種問題,完全可能把先前的設計推翻。所以說在高度設計的環境中,開發人員需要很優秀的技能,優秀到足以質疑設計人員的設計,特別是設計人員一天不如一天知道開發平臺的具體細節的情況下,開發和設計就會脫節。總體來說規劃式(Planed design)和演進式設計(Evolutionary design)是相輔相成,規劃式不應太偏重細節,而是將整個架構脈絡梳理清楚,使用的技術做好預研工作,針對核心業務優先劃分清楚,具體開發過程則更依賴于開發人員的技術和業務能力。

極限編程強調任何一個軟件項目都可以從四個方面入手進行改善:加強交流;從簡單做起;尋求反饋;勇于實事求是。業務人員應該與開發人員坐在一起開發,使用TDD,結對編程,代碼歸集體所有,任何人都可以修改它人代碼,像我們這樣的公司這幾乎是不可能完成的事情。當然極限編程并不是一定不可借鑒。不要過多考慮擴展性,以實現需求即可,統一編碼標準和規范,不要復雜無用的設計文檔的確為開發即設計思想指明了方向。

開發即設計思想(Program For Design),是本人針對近年來的項目開發實際經驗提出的一種新思想框架。這種思想強調開發過程與設計過程的協調統一,強調從重視設計、依賴設計回歸到關注開發本身,畢竟開發是第一現場,開發的質量直接影響最終軟件系統的質量,開發的結果就是設計的目標,只有保證了開發的質量才能降低測試維護的成本。只有通過開發結果才能還原最真實的設計信息,減少設計信息傳遞過程中的變異失真,有利于形成真正有效的通用語言,減少領域模型本身的歧義(開發者、設計者和需求人員依據自身知識體系各自理解領域模型的含義、最后結果必然是似是而非)。新思想不是否定設計的作用,也不是否定先設計后開發的過程,而是強調通過技術手段實現設計和開發互為一體,同步實施,相互反饋。瀑布模型和敏捷開發更重視設計工作,仿佛好的軟件是設計出來的,過分強調有經驗的軟件工程師的作用,而難以招聘有經驗的、合適的軟件工程師恰恰是外包開發商面臨的突出問題。當初項目團隊面試了10位高級架構師,也沒有找到一位合適的新系統建設的,就是很好的說明。PFD對設計工作沒有硬性要求,在底層基礎開發框架的約束和規范下,通過必要的培訓和技術約束,確保開發人員使用規范的方式編寫程序,通過聲明化技術將這些設計與開發信息直接從提交的代碼模塊中提取出來并存儲到數據庫中,將開發成果和歷史軌跡以實時可視化形式展示出來,通過圖形化技術手段、形成通用語言,圖形化的展示方式有利于業務需求人員、測試人員和設計人員理解業務和開發現狀,這樣就可以將所有角色統一在通用語言的石榴裙之下。

敘利亞內戰期間俄羅斯國防指揮中心內部通過俄軍規模龐大的現代化信息設施實時采集戰場動態信息,通過計算分析戰場動態情勢,直接分配給前線每個作戰人員具體的作戰任務,指揮作戰,并通過大屏幕和各種現實設備監控作戰人員的執行狀態和當前位置。PFD是這種理念的直接體現,充分發揮了信息技術的價值和魅力,將開發人員的開發信息直接轉化輸出為設計信息,根據分析和監控結果,近實時的感知問題,就各項問題進行及時的討論和決定(負反饋),實現更快速的設計和開發響應。統一的設計和開發管理平臺也為業務需求人員主動融入開發過程,便于需求人員及時發現需求的疏忽和邏輯缺陷,了解設計和開發全過程(正反饋)。讀者可能會問,既然都已經開發完成了,直接使用系統發現問題豈不是更好,為啥還要通過設計發現問題?對于簡單需求來說,譬如一個月一兩個開發人員就可以搞定的項目來說,PFD完全是多余的,這樣的項目最終實現的結果就是設計的結果。但是對于近百人的團隊,用時一年以上的大型項目(需要軟件生命周期內持續的升級維護)來說,PFD非常必要,外匯市場一個節假日邏輯的微小調整,都可能引發一系列的功能缺陷,因為其它功能模塊可能未顯示化的聲明節假日業務規則。如果沒有準確的設計信息來支撐判斷節假日功能的影響到的其它業務功能,則對于需求提出者和設計者都是巨大的災難。

新范式


新范式是一種反范式。北大俞孔堅教授提出“反規劃理念”,獲得美國藝術與科學院院士殊榮。至今印象深刻的是,它認為傳統的城市規劃強調人口規模、城市土地性質,功能分區和結構布局,而反規劃以土地生命系統的內在聯系為依據,優先進行非建設用地規劃,包括生態景觀、河網濕地、生物保護、歷史文化遺產保護等。使規劃路徑建立在自然過程、生物過程和人文過程分析基礎上,以維護這些過程的連續性和完整性為前提。新范式認為數據結構-過程算法在應對解決復雜業務問題時往往力不從心,算法在應對確定性問題時十分有效,但業務不確定性決定不同時期建設的業務功能容易產生沖突,導致應用開發建設過程缺乏連續性、一致性和穩定性。而基于應用場景的對象-事件-消息三位一體分析和設計方法,能夠更好的適應業務需求的復雜變化,該方法天然內含了時間維度信息,非常適合事件溯源。新范式強調在設計開發過程中如何實現真正的OOP,對象包含了時間維度上的狀態變化信息和基于事件消息的行為定義,數據結構和算法過程被內化于三位一體的分析框架中,為應用場景串聯提供服務。

認識世界的正確姿勢

事件是一種行為、動作(或命令),對象自身具有所持的狀態和行為能力、對象之間通過消息進行信息傳遞、不能夠直接進行行為傳遞、行為和狀態對于對象是私有的、對象只有自身根據相應的消息刺激做出對應的行為動作(應激性),并產生對應的狀態結果,這一點很好理解,傳統的領域模型在設計和開發時似乎有一只上帝的手,在白頭鷹飛翔的場景中,白頭鷹作為鳥類繼承飛行的動作行為(天賦),一頭實例化之后的白頭鷹猶如提線木偶一樣被它的應用主宰,只要應用調用飛的動作邏輯之后,白頭鷹就猶如一道閃電翱翔于空,這一面向對象分析框架顯然不夠完整,似乎總有哪里不對。新范式針對某一具體的業務場景,從以下分析框架入手:

  1. given for object 假設特定的業務對象(一個或多個)處于某種特定的狀態、具有某些特定的行為;
  2. when fired by event 當某一事件發生之時;
  3. then what changed 這些特定的業務對象發生了某些可觀察到的狀態變化,執行了某些可預見的行為;
  4. spread the event messages 發出消息,通知外界或特定對象,自己狀態已發生變化。

領域分析離不開具體的業務場景,我們以聊天室應用為例進行具體分析。對于一個聊天室應用來說,主要場景有:

  1. 用戶加入某一聊天室;
  2. 上線的用戶可以在該聊天室里發布消息,所有該聊天室在線用戶接收該消息;
  3. 用戶離開該聊天室,該用戶下線,不再接收該聊天室的新消息;

按照領域通用語言,我們來剖析建立領域模型。

業務對象:聊天室、用戶和聊天消息(注意:這里的聊天消息是業務對象,而非范式)。業務對象的分析仍然遵循聚合根-實體-值對象的分析方法。聊天室作為聚合根,聊天消息和用戶是實體。根據業務分析聊天室必然存在一個用戶列表和消息發布列表兩個子實體,用戶必然存在一個已接收消息列表,并且有上線和離線兩種狀態,以及記錄當前所處聊天室的位置信息,用戶名對于用戶來說是值對象。對于微信應用的場景,則該模型不能適用,無法滿足需求。領域通用語言的建立不可能脫離具體業務需求和業務場景,雖然都是聊天業務,但卻可能天差萬別。

  1. 在用戶加入聊天室場景中,given for object 聊天室中當前沒有登錄用戶when fired by event聊天室收到一個用戶加入請求then what changed用戶加入該聊天室的用戶列表spread the event messages 某個用戶已經加入聊天室。

  2. 在用戶發布消息場景中,given for object 發送用戶已經在線when fired by event聊天室收到用戶發布消息請求then what changed聊天室將該消息通知給所有在線用戶spread the event messages 用戶與某一時間向某一聊天室室成功或失敗的發布了一條消息。

  3. 在用戶主動離開該聊天室場景中,given for object 某聊天室中該用戶在線when fired by event 用戶離開聊天室請求 then what changed該聊天室從用戶列表中將該用戶置為離線spread the event messages 用戶與某一時間已成功離開某一聊天室。

經過這一范式簡單分析之后,我們通過建立是三個場景模型,幫助我們明確了設計應關注的重點信息,基于場景的分析,讓設計和編寫的代碼具有內聚性,結構更加清晰,也方便TDD測試用例和測試程序的編寫。等等我們似乎遺漏了什么。對,這些場景是如何串聯在一起的,如果新需求加入,會否破壞原有模型的完整性和內聚性。消息機制為模型擴展提供了可能,也為領域模型之間、問題域之間解耦提供了真正的可能,領域模型之間通過消息實現弱的聯系。在第一個場景中,發出用戶已經加入某一聊天室的消息之后,設計者可能根據目前的應用需求可能并不需要關注是否有人訂閱該消息。但是根據開發者的分析,存在潛在需求需要將用戶的狀態置為在線狀態,如果用戶之前沒有已接收消息列表,需要為該用戶初始化一個空的已接收消息列表。這一個邏輯過程完全可以在另一個業務場景中實現,并在另一問題域中進行解決,我們自此又通過分析識別建立起第四個業務場景。而需求可能沒有提及。

  1. 在用戶上線的場景中,given for object 某聊天室中該用戶離線when fired by event 用戶發出上線請求 then what changed 該聊天室從用戶列表中將該用戶置為上線,用戶當前所處聊天室置為該聊天室spread the event messages 用戶與某一時間已成功在某聊天室上線。

自此我們沒有必要修改第一個場景對應的方案域。接下來如果一個監控需求需要追蹤用戶活動記錄,我們也可以迎刃有余的通過訂閱用戶已經在線和離線消息來完成擴展支持工作,而不需要改動到原有模型的任何一行代碼。在滿足性能需求的前提下,開發者和設計者應該努力通過新范式設計領域模型,保持信息的完整性,通過信息的完整性保證模型的穩定性,例如在頭寸現金流場景中,通過簿記原理在本方額度下扣掉對應貸記金額,增加借記金額,在對手方則增加借記金額,減少貸記金額,設計者和開發者很可能因為無需求和難以融入新過程的原因,而有意丟棄交易相關的時間信息、位置信息和交易前后的賬戶狀態信息,如果后期新需求需要關聯這些信息,必然涉及到代碼邏輯的調整,但由于先前邏輯固化在多個關聯類的內部,改動起來必然傷筋動骨。傳統分析方法難以實現模型的完整性,數據和算法過程過于關注問題本身,新范式的優勢在于它能通過針對對象-事件-消息分析,天然解決了狀態問題和擴展問題,當然前提是只要你愿意在初始設計時把領域模型考慮的更完整和清晰。

通過新范式的實例分析之后,我們可以慢慢體會到新范式的威力。配合新范式必然需要新方法、新手段支撐這一美妙的大廈,使之勿要成為空中樓閣。最后我們簡單分析一下TDD基于行為的測試,實際上只要我們在開發時稍加使點小技巧,在第一個場景的領域模塊中針對用戶和聊天室分別采用觀察者模式,監聽用戶所在聊天室位置的改變,監聽聊天室上線和下線用戶的改變,并在單元測試中通過匿名函數實現這些監聽器,在監聽器中增加斷言信息,斷言輸入行為與斷言匹配,最后將監聽器注冊到用戶和聊天室的擴展接口中,執行針對場景一的單元測試用例,我們就會得到是否斷言是否匹配的結果,基于結果決定測試成敗。這些單元測試用例總是保持在實現該模型的測試代碼中,以方便代碼演進過程中的回歸測試。事實上令人更加興奮的是,我們為測試實現的狀態監聽接口也為后期應用模型和業務擴展提供了想象空間。

新方法


領域驅動設計與面向對象設計區別是什么?領域驅動設計是正確的OO,也是實施正確的SOA(ThoughtWorks)。傳統面向對象的設計重視UML的作用,諷刺的是UML的類圖和流程圖幾乎就是面向過程開發代碼的數據結構和算法的高級版本,新瓶裝舊酒。類之間的關聯(association)、依賴(dependency)、聚合(Aggregation,也有的稱聚集)、組合(Composition)、泛化(generalization,也有的稱繼承)、實現(Realization)關系在三、四個類時尚能正常工作,但是當若干模型整合成一個更大的模型時,則變得龐雜而難以準確表達,設計者可能認為自己的設計沒有問題,但沒有足夠經驗的開發者大抵會碰到艱難取舍的局面。通用語言關注對象是聚合根、實體還是值對象,它們定義都非常清晰,邊界明確,幾乎不會混淆,聚合之間通過事件-消息進行交互,即保證了整體的有效和完整,又維護了單個聚合的準確和凝聚,實體和值對象依賴于業務上下文。因此新的設計方法原則上我們只需要關注這些信息:

  1. 聚合及其中的聚合根、實體和值對象,包括聚合完成的業務功能和FSM;
  2. 事件和消息,以及聚合之間通過哪些事件和消息進行有向關聯;
  3. 領域邊界;

從這個角度來講,理解實體之間一對一、一對多、多對多的關系就變成技術問題,而不再是戰略問題。而對于應用開發者,在實現的必經之路上關注到的概念和信息越少越好。

  1. 實現對象包括接口對象(RPC、REST、Handler),數據模型轉換和存儲對象(絕大多數應用,離不開數據庫的支持,盡管數據庫可能是NoSQL,甚至區塊鏈),擴展對象(針對接口的擴展配置和實現);
  2. 數據模型,實現數據模型在應用內的路由和流轉;
  3. 業務邊界,業務邊界應該依賴于領域邊界;

設計系統(不局限于軟件系統)的組織,其產生的設計等同于組織之內、組織之間的溝通結構。這就是康威定律。系統如果按照前臺團隊、接口團隊、后臺應用開發團隊、領域開發團隊、核心技術開發團隊、配置部署團隊、DBA團隊來劃分,系統整體大概率就會形成傳統MVC的分層開發模式;系統如果按照業務邊界劃分,開發者按照一個個業務目標把自己負責的業務做成小的功能模塊、小的系統的話,系統整體就會逐步形成微服務的架構特征。分層開發模式的弊端是比較明顯的。很幸運的是,輔助交易系統從開始進行技術和組織規劃就基本貫徹和堅持了按照業務垂直劃分的細胞式分工體系模式。每個開發人員負責一個代碼庫,一個代碼庫對應一個功能模塊,一個功能模塊對應一個主要的業務領域同時對應一個需求和開發負責人。橫向集成由設計與質量團隊總體負責,通過CWAP技術框架實現若干模塊的集成部署和啟動,確保集成遵循設計規范,部署不出紕繆。在如此分布式開發體系之下,雖然增加了成本,但也帶來了巨大的收益,組件模塊能夠保持獨立演化,將代碼沖突扼殺在搖籃里,在發生必要的需求調整時,能夠借助技術手段快速的評估影響范圍,并通過最小的代碼改動屏蔽對整體的沖擊(前提條件是所有開發人員嚴格遵守這一開發體系,不開發與自己功能無關的業務代碼,嚴格使用API獲取依賴的業務數據)。架構體系中的橫切關注點也就是支撐域,例如DEP交互、IMT交互、數據庫交互、日志交互等宜交由技術能力強的開發人員單獨負責,優先完成通用域的開發工作,例如:用戶、機構和權限等。在這個分布式演進的架構體系中,組件模塊的融合、重組和分裂是必然的,目標是最終減少組件間的兩兩依賴關系,保證業務功能模塊的獨立演進能力。優秀的架構恰恰體現在人員分工與領域驅動的組件劃分完全匹配。通過化整為零,分而治之的方法,確保開發人員權責明確,充分調動大家的積極性,保證開發的進度和質量。架構是不斷演進的、必須不斷適應和滿足業務需求,演進的最終目標是幾乎消除不必要的組件物理上的兩兩依賴關系,組件能夠完全獨立部署,這不就是我們希冀的微服務嘛。從這個角度來說,微服務其實不必刻意強求,只要按照這套模式發展,在技術條件允許的情況下(技術管理委員會授權),任何一個系統最終都會演化成微服務的架構體系。從這個意義上說,貿然直接嘗試微服務是危險的,進程數會爆炸,對部署提出了嚴峻的考驗,NTPII就存在典型的問題,這會讓初次部署變得非常痛苦。不斷演化,小版本快迭代,才會將壓力通過時間換空間的方式加以釋放。

城市規劃的核心內容是對土地使用的規劃和管理,建筑和城市設計的核心概念是建筑的尺度和街區的尺度。宜人的尺度,靈巧的空間變化會讓辦公人員身心愉悅,居住舒適宜人。良好的街區讓漫步者能夠呼吸新鮮空氣,感覺輕松,孩童能夠安全快樂的嘻嘻玩耍。土地的合理規劃使得職住平衡,商業優良,工作者不會為擁堵而煩惱,不會為生活品質而妥協。尺度對于軟件工程來說就是指軟件需求和設計的粒度。如果說架構設計師負責土地的規劃和管理,那么設計開發者就可以以建筑和城市設計者自居。BA幫助整理和分解需求條目,使之能夠以一個較為合理的輪廓展現給相應的開發者,按照新范式的思想,需求最佳粒度就是系統業務(產品)的每個具體應用場景,例如用戶消息中心(盒子)接受消息場景、消息中心發布消息場景、清算關系維護場景、成交明細查詢場景和成交行情實時展示場景等。每個場景配套展示若干主要業務規則,以方便開發人員更好的理解交互需求。開發團隊的規模必須是合理的,一個開發小組適合以3人為宜。3個人就可以相互輔助,一個組件模塊上分飾開發者、審核人和測試者角色。3個人所負責的業務領域最好是緊密聯系的,以減少與其它小組的溝通成本。合適的粒度不僅僅要求開發,也制約設計,要求領域設計提出的問題子域最好能夠匹配單人開發,3人協作的模式,不宜將問題復雜化。

軟件的復雜性往往是業務復雜性的直接體現,所以結合企業發展脈絡,實際業務情景,組織領域設計與微服務實踐是必由之路。領域驅動的微服務設計不可能減少開發者的代碼工作量,領域驅動設計的核心目標是允許在系統建設過程中,以更快的速度,以更高的質量和穩定性響應業務需求的復雜變化,讓系統演進的曲線變得平緩可控。將業務的復雜性盡量的封裝在領域模型之內。這就為具體的實踐提出了如下要求:

  1. 落實技術實現和業務實現的分離,將在領域的設計與開發過程中不要考慮物理意義上的數據持久化;
  2. 盡量少的提煉技術和業務概念,作為開發者并不需要向其它開發者秀自己的知識容量;
  3. 正確劃分通用域、支撐域和核心域。集中優秀的資源重點解決與業務有關的核心域;
  4. 領域模型核心接口的設計理應基于業務領域來識別,拒絕基于UI交互來設計,在應用層為UI定制特殊的適配層,以提高系統的擴展性,增強系統適應變化的能力;
  5. 應用層通過命令模式與事件行為進行適配,通過消息監聽器將行為結果以報表概念持久化,以便于業務查詢需求設計與開發變得簡潔和高效(CQRS原理)。命令可能來自UI觸發、消息事件觸發或者定時任務觸發,一個命令只觸發一個事件,當一個事件能被多個命令觸發時,說明事件本身需要拆分或者重新梳理。

如此復雜的分布式系統,如此復雜的分工協作體系,當期的DevOps是遠遠不能滿足項目建設要求的,當前的DevOps重視構建和運維,理念上與交易中心等金融機構的組織架構背道而馳。金融機構強調穩妥管控,DevOps強調自治自助。金融機構強調權責明確,DevOps強調使命協作。開發管控和設計監督是被忽視了的重要環節,所以輔助交易系統建設過程中也在未雨綢繆,穩妥推進,目標在保質保量完成交付輔助交易系統同時最終實現一個強大自主,能夠滿足項目組實際需求的設計與開發管理平臺,搭建起一套集持續集成、設計與開發監控的開發環境,降低開發總體成本,保證系統交付質量。傳統的DevOps以CI為核心,設計與開發管理平臺以Component為核心。主要實現如下功能:

1. 版本管理:每日組件迭代信息、組件發布歷史記錄,以便于追蹤歷史和控制風險,確保缺陷修復與發布版本一致,自動更新構件依賴,盡早發現依賴問題;監控組件發布對進程的影響,自動審核發布內容是否規范,通過掃描組件,控制代碼質量缺陷,不合規版本自動拒發,同時評估開發工作量,為項目管理提供切實依據;
2. 組件分析:分析組件的依賴關系,識別組件與數據庫和數據模型的關系,分析組件與接口的關系,盡早發現不合理的依賴設計,圖示展示組件的實時設計模型,關聯組件與需求條目的關系,展示接口的使用教程,方便需求影響范圍和影響細節評估,為技術管理提供切實依據;
3. 領域設計:為領域設計和應用提供資源和技術支持,按照新范式展示領域模型和領域組件,以方便領域構件使用者了解構件相關信息,自動連接領域資產庫,為領域資產管理提供可行手段;
4. 應用設計:根據CWAP技術框架原語,自動繪制業務架構圖和業務流程圖,為概要設計提供實時可視化替代技術方案;
5. 開發激勵:通過一些巧妙的人性化的激勵機制設計,以活躍開發者,獎懲并舉,提高開發效率、落實審查制度,保證組件開發的質量。

只強調自治不強調管控是軟件業自欺欺人的表現,正如政治領域只強調民主不強調集中一樣。當今的軟件領域過分強調知識分享和軟件開源,如果開源分享無利可圖,誰還愿意持續開源分享?到現在為止,我們都沒有談到具體的技術細節。實現通用語言的技術手段和實現新范式的技術手段是一致的。這個世界到處都是套路,領域模型本身就是尋找一種套路,將業務發展構建在套路之上。構建特定問題域下的領域模型并不依賴于Actor模式、如果使用普通類能夠解決問題,那就應該使用普通類、但是當業務對象中存在狀態和并發的問題子域時,Actor實際上是目前最簡單、最優秀的解決方案,限界上下文之間、領域模型之間按照新范式設計,那么Actor無疑也是最佳解決方案。這里我們推薦一種架構方案:AKKA+AxonFramework+Disruptor+Guice。AkKA的開發原語過于靈活,如使用編程語言一般靈活簡單,Actor建立在更高的抽象層次,開發人員幾乎不用關心集群和分布式有關的知識,就可以完成完全分布式并行應用的開發工作。允許應用開發者直接使用AKKA提供API可能是一件十分糟糕的事情,結合設計與開發協調一致,開發與模型實時互相映射的需求,基于AKKA的架構再造是必不可少的。當前商業和開源環境沒有先例,AKKA猶如一顆閃耀的新星,埋頭許久,剛剛崛起。

最后回到開始的培訓課程。在這一天緊張而豐富的課程中,我學習了如何讓一個5-6人的團隊精誠團結,積極溝通,通過在白板上使用便利貼的方式,共同剖析共享電動車的產品需求,利用事件風暴方法逐步建立基于DDD的領域模型。事件風暴(Event Storming)是快速識別領域模型的有效方法,它是一種結果導向型的設計方法,重點關注將來的結果。傳統的建模方法則基于行為和對象的靜態數據模型建立模型,在事件風暴面前簡直弱爆了。

Event Storming is a useful way to do rapid "outside-in" domain modeling: starting with the events that occur in the domain rather than a static data model. Run as a facilitated workshop, it focuses on discovering key domain events, placing them along a timeline, identifying their triggers and then exploring their relationships.

透過事件風暴的形式看本質,從系統業務角度出發,其主要過程包括:

  1. 梳理出系統的主要業務功能;
  2. 提取系統的用戶、主要業務場景和業務流程;
  3. 根據業務流程識別出領域事件并按照時間序列進行排序;
  4. 針對領域事件發掘命令;
  5. 針對領域事件和命令進行聚合和子域(subdomain)的識別;
  6. 識別的實體、值對象及實體關系;
  7. 針對領域模型識別限界上下文(Bounded Context);
  8. 持續探索,不斷優化設計結果。

希望本文能夠為系統的設計工作提供助益和借鑒。

意義和總結


好的架構和設計都是慢慢迭代演化出來的,領域資產也是逐步積累起來的,好的領域構件重點在于用,而不在于量,好的架構的設計必須用一切手段提升溝通效率,比如WIKI、比如TEST,能2個人講清楚的事情,就不要拉更多人,每個人每個模塊都分工明確,出了問題知道馬上找誰,避免互踢皮球。技術工程師追求熱門的技術潮流并沒有錯,但脫離公司業務實際,運用新技術,無疑會火上澆油。設計即編程這條老路已死,我們需要新的思想、新的技術、新的架構和新的血液以追尋偉大的理想。在面向對象的道路上,我們必然會走很多彎路,我們本以為運用設計模式,使用C + + 和Java語言編寫出的程序就是面向對象的程序,我們也曾以為使用了Spring MVC框架編寫的程序就是面向對象,今天回頭來看,只怪當初是多么膚淺和輕狂。

磨刀不誤砍柴工,好的士兵需要有好的兵器,PFD的核心宗旨是以開發者為中心。只要堅持開發即設計思想,以建設設計與開發管理平臺為依托,DDD、MSA和CI為手段,遵循新范式分析框架,走新方法這條路,我相信這條路我們一定能夠走得通,并且建設出偉大的軟件系統。

參考文獻

ThoughtWorks技術雷達
AKKA入門與實踐
領域驅動設計-軟件復雜性應對之道
微服務的理論基礎-康威定律
微服務設計
響應式架構
設計已死


周末補上插圖哈哈哈~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容

  • Evans DDD 2004年Eric Evans 發表《Domain-Driven Design –Tackli...
    Bobby0322閱讀 14,527評論 3 54
  • 有位朋友最近在為企業做領域驅動設計(Domain Driven Design)內訓時,遇到一位資深學員向他抱怨該技...
    MagicBowen閱讀 18,697評論 8 66
  • ?大年初九 晚上九點 坐在開往大城市的汽車上 給大家分享一個發生在我身邊的真實故事 : 早上媽媽起來做好早餐, 給...
    籃球小子閱讀 338評論 0 2
  • 篡位登基的朱棣,為了證明自己是大明的合法繼承人,努力要將自己治理下的大明打造成傳統社會理想的典范、“盛世”中的極品...
    一夕厘閱讀 1,253評論 2 6
  • 街道空寂荒涼 讓人想不到昔日繁華 只有落葉在風中貼著地面飛行 呼呼的風聲在耳邊回旋 讓人想到故鄉的秋 遠處孩子在風...
    小紅紅哥閱讀 159評論 1 1