傳統的分布式應用不會切入微服務、快速數據及傳感器網絡的響應式世界。為了捕獲這些應用的動態聯系及依賴,我們需要使用另外一種方式來進行領域建模。由純函數構成的領域模型是以一種更加自然的方式來反映一個響應式系統內的處理流程,同時它也直接映射到了相應的技術和模式,比如Akka、CQRS 以及事件溯源。本書講述了響應式系統中建立領域模型所需要的通用且可重用的技巧首先介紹了函數式編程和響應式架構的相關概念,然后逐步地在領域建模中引入這些新的方法,同時本書提供了大量的案例,當在項目中應用這些概念時,可作為參考。
推薦序
開發人員正淹沒在各種錯綜復雜的問題中,需要借助多核處理器以及分布式基
礎架構的優勢,來應對產生數據越來越多的高要求用戶規模的迅猛增長,以確保更
低的延遲以及更高的吞吐率。所以開發人員不得不在消費者日益苛刻的緊張截止時
間前按時交付。
開發人員的工作從來沒有輕松過。為了能保持多產的同時又能享受工作,需要
采用合適的工具集這些工具可以通過優化資源的使用來管理日益增長的復雜性
以及需求。通常,并不是簡單地追逐最新、最炫的東西盡管這很誘人。所以必
須要回顧總結,從過去艱難獲勝的經驗中學習,看是否可以將其應用到今天的場景
以及挑戰中。我認為開發人員開發的那些非常有用的工具中所包含的領域驅動設計
(domain-driven design,DDD)、函數式編程(FP)以及響應式原則,都可以幫助我
們管理復雜事務的某個方面。
y 領域復雜性:領域驅動設計幫助我們挖掘并理解領域的不同特性與語義。通
過跟利益相關方用他們的語言進行溝通,DDD 可以更容易地創建可擴展的領
域模型來映射真實世界,同時允許持續的變化。
y 解決方案復雜性:函數式編程可以幫助我們保持合理性及可組合性。通過可
重用的純函數并使用穩定(不可變)值,函數式編程提供了一個偉大的工具集,
通過不會撒謊的代碼來得出運行時間、并發性以及抽象過程。
系統復雜性:正如在The Reactive Manifesto(http://www.reactivemanifesto.org)中
所定義的,響應式原則能幫助我們管理日益復雜的世界,包括多核處理器、
云計算、移動設備以及物聯網。在這里,所有新系統本質上都是分布式系統。
要運作這個世界是非常困難而且很有挑戰的,但同樣,也擁有很多有趣的新
的機會。這種變化迫使我們的行業去反思過去一些圍繞系統架構以及設計方
面的最佳實踐。
我非常喜歡閱讀這本書,它完全體現了我在過去十幾年的自身經歷。我從OO
實習生開始白天埋頭于C 和Java,而晚上閱讀經典的Gang of Four1。2006 年
我開始閱讀Eric Evan 關于領域驅動建模的書2,它對我或多或少有所啟發。然后我就
變成一個DDD 狂熱愛好者,在所有可能的地方去應用它。多年后,我又開始使用
Erlang3,然后是Scala4,它們都讓我再次感受到了函數式編程的魅力并深深地愛上了
它。我在大學期間學過函數式編程,但當時并沒有真正意識到它的威力。在這段時
間里,我開始對Java 在并發性、適應性以及可伸縮性方面的最佳實踐逐漸失去
信仰。在Erlang方式,特別是actor模型(5 我認為這是一個更好的做事方式)的指引下,
我開始了Akka 項目,我相信這會有助于將響應式原則帶入主流。
這本書之所以能吸引我是因為它設立了一個更加宏大的目標,將3 個完全不同
的工具(領域驅動設計、函數式編程以及響應式原則)用可實踐的方式整合到了一起。
它教會你諸如邊界上下文、領域事件、函數、monad、applicative、future、actor、流
以及CQRS6 等內容是如何使復雜性保持可控的。如果內心不夠強大,那么這本書將
不適合你,閱讀它很費勁。但如果花上數小時,你就會收獲一些基礎概念。親愛的
讀者,幸運的你已經邁出了第一步,接下來所需要做的就是繼續讀下去。
JONAS BONéR
Lightbend 創始人兼CFO
Akka 創始人
序
在2014 年夏天,Manning 出版社希望出版DSLs in Action(https://www.manning.
com/books/dsls-in-action)的升級版本,因為DSL 的所有新特性都圍繞編程語言的
設計和實現。巧合的是正好在那個時間,我用函數模式對一個復雜的領域模型成功
地進行了重構。
跟一群剛畢業進入Scala 函數式編程世界的軟件工程師們一起,我將域行為建
模為純粹的函數,將域對象設計為代數數據類型,并開始意識到代數API 設計的價
值。團隊的每個成員人手一本Paul Chiusano 和Rúnar Bjarnason 剛完成的Functional
Programming in Scala(中文版為《Scalo 函數式編程》,由電子出版社出版)。
我們的域模型非常復雜,實現嚴格遵守Eric Evans 在他的著作Domain-Driven
Design: Tackling Complexity in the Heart of Software(Addison-Wesley,2003 年)中所
闡述的領域驅動設計(DDD)的原則。不過我們沒有用面向對象的方式,而是決定
采用函數式編程。一切的開始都像是一個實驗,但在最后證明這是一次非常成功并
且令人滿意的經歷,F在當我回頭看時,發現DDD 的內容與軟件工程的通用規則
非常協調一致。因此也不用擔心函數式、領域驅動設計會顯得像是領域建模的典型
范例。
這本書是我們成功運用函數式編程進行領域模型開發的證據。我決定跟讀者分
享我們遵守的實踐、采用的原則,以及在實現中所使用的Scala 風格。Manning 出
版社完全同意這個想法并決定繼續該項目。
不管你的領域模型是什么樣的,定義實現成功的一個關鍵標準是應用的響應能
力。沒有一個用戶喜歡盯著屏幕上的等待光標,根據我們的經驗來看,這通常是因
為架構師非必要地阻塞了主線程的執行。需要花費時間執行的昂貴的操作應該用異
步的方式來執行,把主線程空出來給其他用戶行為。The Reactive Manifesto(www.
reactivemanifesto.org)中定義了建模所需要使用的特性,以便保證應用程序是非阻塞、
響應及時的,并避免巨大延遲帶來的惡劣影響。這也是我要在書中寫的另一個方面。
在經過與Manning 團隊多次友好的商討后,我們決定在這本書中將函數與響應式編
程結合起來。
于是本書就誕生了。通過這個項目,我收獲了巨大的樂趣,也希望讀者能有類
似的體驗。我收到了無數讀者、評審者、良好祝愿者們的留言,他們陪著我一起提
升了這本書的質量。我也非常感謝來自Manning 出版社經驗豐富的編輯以及評審者
團隊的巨大支持。
致謝
我要感謝很多人,他們直接或間接地參與了這本書的創作。
首先,我要感謝Martin Odersky,Scala 編程語言的創建者,我用Scala 完成了
所有函數響應式領域建模的案例。同時也非常感謝你建立了Scalaz,這個有趣的庫
使我們在用Scala 語言進行純函數編程時充滿樂趣。
Twitter 是一個非?岬臏贤ǚ绞,承載了各種各樣的討論。我在上面和一些牛
人就函數式編程有過很多非常激烈的討論。感謝每一位牛人,是你們促使我完成了
這本書。
感謝所有的評審者:Barry Alexander、Cosimo Attanasi、Daniel Garcia、Jan Nonnen、
Jason Goodwin、Jaume Valls、Jean-Fran?ois Morin、John G. Schwitz、Ken Fricklas、
Lukasz Kupka、Michael Hamrah、Othman Doghri、Rintcius Blok、Robert Miller、
Saleem Shafi、Tarek Nabil,以及William E. Wheeler。時間可能是我們擁有的最寶貴
的資源,我非常感謝他們愿意在這本書上花費時間,每個評審者都給了我很棒的建
議,極大地提升了這本書的質量。
感謝所有購買了MEAP1 的讀者,在作者在線論壇里的定期溝通,一直鼓勵著我
完成這本書。特別要感謝Arya Irani,她貢獻的一個pull 請求幫助我更新了monad
代碼(從基于Scalaz 7.1 到7.2)。同樣要特別感謝Thomas Lockney 和Charles Feduke,
他們對每個不同的MEAP 版本做了徹底的技術評審。
我還要感謝Manning 出版社再次信任我。在我寫第一本書的時候,我們有過非
常美好的合作,而再次合作甚至更有樂趣。我要感謝以下Manning 員工的杰出工作。
感謝Michael Stephens和Christina Rudloff促使我啟動這個項目。
感謝Jennifer Stout 在10 個章節的漫長過程中不屈不撓地糾正了我所有的錯誤。
感謝Alain Gouniot在整個過程中提供了深入的技術評審。
感謝Gandace Gilhooley與Ana Romac幫助推動這本書。
感謝Mary Piergies、Kevin Sullivan、Maureen Spencer,以及所有幕后工作人員(包括Sharon Wilkey、Alyson Brener、April Milne,以及Dennis Dalinnik),他們幫助我把一個粗糙的草稿變成一本真正的書。
感謝Jonas Bonér 為我的書寫序。我很榮幸,我與Jonas 已經相識了很長時間,他也是我很多軟件開發項目的重要靈感來源。
最后,我要感謝我的妻子、母親以及我的兒子Aarush,他們給我提供了最完美的生態環境,在那里,寫一本關于函數式編程的書這種創造性任務才有可能完成。
關于本書
本書內容涉及如何使用函數式編程實現領域模型,以及如何通過使用響應式原則(諸如非阻塞計算和異步消息)來確保模型的響應性。領域模型都是針對問題領域的,可以通過很多方式實現一個解決方案框架
能提供與問題領域模型相同的函數性,通常會使用面向對象技術來設計領域模型。
本書中使用了一種正交方式用純函數對領域行為建模,用代數數據類型對領域
實體建模,并將不變性作為設計空間的一個主關注點。作為讀者,你能學到基于代
數技術的函數式設計模式,可以將其直接用于實現自己的領域模型。
這本書同樣還包括了響應式編程使用future、promi
Debasish Ghosh,軟件架構師,是使用Scala和Akka來進行響應式設計的先行者。他同時也是DSLs in Action的作者,該書由Manning出版社于2010年出版。
李源,曾在華為技術有限公司工作8 年,經歷過開發、SE、PM和PQA等多個崗位,目前在途牛旅游網擔任研發總經理一職,是美國質量協會(ASQ)注冊質量工程師(CQE);譯者有豐富的開發、架構設計及研發管理經驗,先后負責過多個大型項目的方案設計和系統規劃,對于C 、Java 以及設計模式等領域都有比較深入的研究;曾翻譯《Java 性能調優指南》一書。
1. 函數式領域建模:介紹.............................................................. 1
1.1 什么是領域模型 .............................................................................................. 2
1.2 領域驅動設計介紹 .......................................................................................... 4
1.2.1 邊界上下文 .......................................................................................... 4
1.2.2 領域模型元素 ...................................................................................... 5
1.2.3 領域對象的生命周期 .......................................................................... 8
1.2.4 通用語言 ............................................................................................ 13
1.3 函數化思想 .................................................................................................... 14
1.3.1 哈,純粹的樂趣 ................................................................................ 17
1.3.2 純函數組合 ........................................................................................ 21
1.4 管理副作用 .................................................................................................... 26
1.5 純模型元素的優點 ........................................................................................ 28
1.6 響應式領域模型 ............................................................................................ 31
1.6.1 響應式模型的3 1 視圖 .................................................................... 31
1.6.2 揭穿我的模型不能失敗的神話 ................................................ 32
1.6.3 伸縮性與消息驅動 ............................................................................ 34
1.7 事件驅動編程 ................................................................................................ 35
1.7.1 事件與命令 ........................................................................................ 37
1.7.2 領域事件 ............................................................................................ 38
1.8 函數式遇上響應式 ........................................................................................ 40
1.9 總結 ................................................................................................................ 41
2 Scala 與函數式領域模型..........................................................42
2.1 為什么是Scala ............................................................................................... 43
2.2 靜態類型與富領域模型 ................................................................................ 45
2.3 領域行為的純函數 ........................................................................................ 47
2.3.1 回顧抽象的純粹性 ............................................................................ 50
2.3.2 引用透明的其他好處 ........................................................................ 53
2.4 代數數據類型與不變性 ................................................................................ 53
2.4.1 基礎:和類型與乘積類型 ................................................................ 53
2.4.2 模型中的ADT 結構數據 .................................................................. 56
2.4.3 ADT 與模式匹配 ............................................................................... 56
2.4.4 ADT 鼓勵不變性 ............................................................................... 58
2.5 局部用函數,全局用OO.............................................................................. 59
2.5.1 Scala 中的模塊 .................................................................................. 60
2.6 用Scala 使模型具備響應性 .......................................................................... 64
2.6.1 管理作用 ............................................................................................ 65
2.6.2 管理失敗 ............................................................................................ 65
2.6.3 管理延遲 ............................................................................................ 67
2.7 總結 ................................................................................................................ 69
3 設計函數式領域模型...............................................................70
3.1 API 設計的代數 ............................................................................................. 71
3.1.1 為什么是代數方法 ............................................................................ 72
3.2 為領域服務定義代數 .................................................................................... 72
3.2.1 賦值抽象 ............................................................................................ 73
3.2.2 組合抽象 ............................................................................................ 74
3.2.3 類型的最終代數 ................................................................................ 76
3.2.4 代數法則 ............................................................................................ 77
3.2.5 代數解釋程序 .................................................................................... 79
3.3 領域模型生命周期中的模式 ........................................................................ 80
3.3.1 工廠對象從何處來..................................................................... 82
3.3.2 智能構造器 ........................................................................................ 82
3.3.3 通過更有表現力的類型進一步提升智能 ........................................ 84
3.3.4 用代數數據類型聚合 ........................................................................ 86
3.3.5 用透鏡更新聚合功能 ........................................................................ 88
3.3.6 倉儲與解耦的永恒藝術 .................................................................... 94
3.3.7 高效地使用生命周期模式結論............................................... 101
3.4 總結 .............................................................................................................. 102
4 領域模型的函數式模式.......................................................... 103
4.1 模式代數、函數、類型的聚合 ........................................................... 104
4.1.1 領域模型中的挖掘模式 .................................................................. 106
4.1.2 用函數式模式使領域模型參數化 .................................................. 107
4.2 強類型函數式編程中計算的基本模式 ...................................................... 112
4.2.1 函子建立模式........................................................................... 112
4.2.2 加強版函子模式 .............................................................................. 114
4.2.3 單子作用applicative 模式的變體 ............................................ 121
4.3 如何用模式對領域模型進行塑形 .............................................................. 130
4.4 用代數、類型和模式演進API ................................................................... 134
4.4.1 代數第一稿............................................................................... 136
4.4.2 改進代數 .......................................................................................... 137
4.4.3 最終組合采用類型................................................................... 138
4.5 用模式和類型增強領域的不變性 .............................................................. 139
4.5.1 貸款處理模型 .................................................................................. 139
4.5.2 使非法狀態不可表示 ...................................................................... 141
4.6 總結 ...............................