領域驅動設計-讓程序員心中有碼(九)

1、易於腐化的軟件設計

猶記得剛剛參加工做時,是地圖廠商四維圖新集團旗下的一家子公司,主要從事規劃測繪相關軟件研發的公司。當時個人項目是爲勘測設計院提供相對應的應用軟件,對地理信息和規劃相關的圖紙信息,幾乎已經專業水平。事實上,規劃設計大概和軟件設計相似,有規劃的設計、或無規劃的設計,形成的結果幾乎是天壤之別。程序員

咱們或許很容易就能設想到一個毫無規劃設計的城市,縱橫交錯的路網、雜亂無章式的建築佈局、各類凌亂的棚戶區設計,剛好象徵着軟件設計的無序性,也剛好體現了軟件企業在經費不足、組織缺少管理、開發者能力不足、軟件隨時隨地想改就改時的行業現狀,只能說這樣的軟件是最能符合當時實際勞動生產力水平的產品。數據庫

巴西棚戶區

如圖一所示,巴西棚戶區,層層疊疊、風格迥異、密密麻麻,若是做爲一個外人貿然來到這樣的地方,大概很容易迷失期間、更不用說充斥在棚戶區的各種毒品和黑社會。雜亂無章的建築和街區,就像代碼中錯綜複雜的調用鏈;而藉助貧民區搞事的黑社會就像是代碼中的異味或者bug,表面上看起來如此平靜、與世無爭、可是你永遠也不知道啥時候會來一冷槍。編程

不要覺得離咱們很遠,咱們其實輕易就能寫出這樣的軟件工程項目。不必定是「大泥球」系統,也有可能只是一些看似簡單的業務系統,但內部代碼邏輯,可能會複雜到使人窒息的程度。也許那個時候有個別開發者也許會試圖靠本身的能力來改變局面,可是每每也會礙於屎山太大,難如下嚥。架構

大概只有最頂級的規劃設計師、耗費足夠多的資源,才能將這樣的軟件系統進行整改。然而,即使如此,若是之後沒有持續維護的手段、更好的設計、僅靠老程序員或個別架構師、盲目相信將單體服務拆分紅微服務,幾乎不太可能實現軟件將來的可持續發展。框架

一個良好的軟件產品的一輩子、或許實際上是一家企業一輩子的真實寫照。微服務

在特定組織架構下,缺少技術基因的組織有時候期待技術變革,卻會開啓新的泥坑。而那些渴望靠技術改變一切的技術專家,雖然擁有某些大廠微服務式架構、以及架構改造的經驗,他們也試圖經過本身的努力,爲企業業務騰飛助力。而在他們過去的經驗中,每每相信組織遇到的問題,用微服務必定能解決問題。而後大肆擴招,一年內從幾我的的規模、擴招到數百人的規模,將原來的系統從單體服務、改良成爲微服務。可是靠單槍匹馬根本無力拯救大勢,沒有更好的業務拆分策略,就只能按照數據庫的表名關係實現了最簡單的拆分。架構改造並不是每次都會百試百靈,有時甚至連原來的需求都包不住,畢竟只能看到用戶界面層外觀上的表面邏輯,而隱藏在業務中的那數十萬行代碼,哪怕包含了企業最有價值的經驗財富,也因爲代碼過於混亂,最終拋棄在源代碼管理器中,堪稱化神奇爲腐朽。佈局

2、易於腐化的面向過程開發

老系統改造也好、新系統開發也好,毫無疑問,咱們最容易相信的實際上是老程序員經驗,而程序員們掌控系統的方式,就是靠數據庫建模來驅動軟件開發的古老模式,並且幾乎都是面向過程式的代碼,這些代碼的流程幾乎如出一轍,只需簡單的按照步驟,一步步套模式,輕易就能學會。學習

一、查看用戶界面,定義須要綁定到界面的模型和層級結構。設計

二、設計數據庫,無論什麼類型的項目,先根據客戶提供的業務表單、將其轉化成實體關係(ER圖)、而後創建對應的代碼模型。有可能使用專業軟件設計ER圖,也有可能會使用Navicat軟件設計ER圖。3d

三、設計接口,而後把數據拼湊成用戶界面層所需的對象。

四、代碼層次結構爲傳統的三層架構,嚴格按照用戶界面層、業務邏輯層、數據訪問層進行設計,有時候會引入依賴注入框架,實現不一樣層次間的解耦。

可是有時候程序員不會嚴格區分須要編寫的代碼,到底是屬於哪一個層次應該囊括的內容。因而毫無疑問,若是代碼是爲了實現用戶界面上某些數據綁定操做,代碼就往用戶界面層寫;或者代碼是爲了實現從數據庫中抽取某些複雜數據、並構形成知足用戶表現層邏輯的查詢對象,那麼就能夠看到數據訪問層代碼中那些臃腫的SQL語句或查詢方法。

正如「羅馬不是一天建成的」,屎山也一樣如此。這樣的寫法在代碼剛剛編寫之初並無問題,只是隨着業務變化、時間的積累、程序員的水平、方法重構、新技術新組件的引入,代碼將成爲屎山。

這時,高級程序員們的價值,就在於他如何可以在屎山中快速找到bug、並解決問題的能力,這大概是一種不能複用、不可再生的能力,由於永遠有讓人看不懂的垃圾代碼,並且每家企業都有本身的特色,不一樣企業間每每不能循環利用。我一位朋友常常吐槽,他感受本身的價值就是守住公司那份擁有8年曆史的古老代碼,以便其餘程序員在進行代碼修改時,不會引起莫名其妙的bug讓系統沒法運轉。

3、過程式開發和事務腳本模式

在現代軟件工程學的教科書中,都會指出面向對象是解決軟件複雜性的方法,但實際上掌握這種方法的開發者並很少。因爲開發者廣泛缺少抽象化思惟,因此面向數據庫、面向過程式的編程習慣可以成爲業界主流,並不是時代的倒退,而僅僅只是在短時間效率和長期維護性上,被迫作出的艱難選擇。

假設咱們設計出的符合三層架構的系統結構圖簡化後,以下圖所示:

咱們來看看這種數據庫建模的開發流程中的輸出成果:

一、會定義兩種對象,分別是是面向UI層的模型(DTO)和數據實體(Entity)。在領域驅動設計中,將這兩種稱爲所謂貧血模型,貧血模型,只有賦值器Set和取值器Get,(在Java裏面會使用POJO 這個名詞來定義)。貧血模型是爲了做爲保存狀態或傳遞對象而存在,他並不是按照實際用例場景對某類具體事務的抽象、也沒有與對象相關的行爲。

二、定義數據訪問層來實現數據的持久化、或者從持久層實現數據的建立過程。數據訪問層存在的目的是爲了構建上述貧血模型對象,這種訪問機制被成爲「事務腳本」。事務腳本與對象行爲割裂,並且容易致使異味產生。

三、與用戶行爲相關的操做割裂的存放在不一樣層。有的可能放在用戶界面層、有的可能放在數據訪問層、有的可能放在業務邏輯層,形成了領域知識的丟失。

四、用戶界面層使用接口做爲外觀或者一種行爲、開發者會使用本身獨立的風格習慣來定義這種行爲,就容易形成術語和規則不統一,也會爲後期產品的維護迭代形成問題。

五、如今的軟件設計,每每要求輸出一份高保真的原型圖、也會按照敏捷項目管理的流程對這份原型圖創建持續更新的機制,確保原型圖是需求的具體表達,可是產品語言並不是統一語言,也許產品語言具備業務含義,可是因爲不能指導開發者進行接口、類、持久層的設計,形成了代碼與需求的割裂。在張逸老師的《領域驅動戰術實踐》提到他曾經使用dimension和metric兩種不一樣的對象來定義一個維度對象,爲代碼形成了沒必要要的麻煩。我也曾經在一個項目,遇到過產品術語未能澄清,致使開發中使用style和theme兩種大相徑庭的定義來定義與「風格」相關術語,爲代碼引入了沒必要要的糾結。

4、領域驅動設計是什麼?

領域驅動設計引入瞭如下概念,可是咱們無需在這篇文章中深入理解這些概念的具體含義,咱們只需知道,有這個東西。當咱們開始按照領域驅動設計的方法設計一個系統時,按照前人整理的領域驅動的sample,每每就會將概念融匯貫通,達到更好的理解效果。

一、統一語言:定義好產品原型,須要創建統一語言。這是一種在內部和外部都能使用的規範化用語,包括UML、適當的圖、一致性的描述、以及專業術語和術語對應的英文描述。

二、實體:在領域中能夠經過標識進行惟一值定位的對象。

三、值對象:在領域中,從其餘領域或某個實體中分離出只包含某些特定屬性的對象。因爲不具有惟一性特徵,每每無需用於數據持久化。

四、聚合、聚合根:將具備相關性的對象聚合在一塊兒,並以聚合根的形式統一對外提供訪問方法和屬性字段成員。

五、限界上下文:領域包含核心領域、子域和通用子域,而限界上下文則是一個具體業務的流程。每一個限界上下文獨立於其餘限界上下文而存在,獨立演進、功能完備。限界上下文的識別充滿技術含量。

六、領域服務:包括倉儲服務和工廠服務,前者負責實現對象與數據庫的操做過程、封裝了一系列數據庫操做的方法;後者則側重於對象的建立過程。我的認爲從三層架構演進到領域驅動架構過程當中,倉儲服務是最接近於數據訪問層的邏輯,也是讓大部分領域驅動架構最終又迴歸到三層架構的一種通病。從對數據訪問層中抽出對象、行爲、數據訪問,是戰術設計的關鍵步驟。

領域驅動設計引入了一堆新的架構形式,包括經典的四層架構、EDA(事件驅動架構)、CQRS架構(命令查詢職責分離)。而因爲Evans的原書沒有過度討論如何識別領域,後來又有許多大佬在他的基礎上進行了完善,提出了許多方法,包括名詞、形容詞、動詞建模法、事件風暴、四色建模等方法,限於篇幅,且聽下回分解。

領域驅動的結構

5、思惟的轉變,纔是最大的困難

領域驅動設計,或許是解決這些問題的一劑良方,但也或許是開啓了暗黑世界的大門。

概念晦澀難懂、程序員們不肯意開始思惟變革、技術上可能存在不預期的坑、均可能讓新方法的實踐陷入一灘爛泥。還有許多人覺得本身看懂了領域驅動設計(包括筆者),在往項目中運用時,老是有意無心的會被過程式代碼的思惟定式控制,讓架構回退到三層架構。

因爲微服務架構的興起,讓複雜系統的開發維護成爲你們廣泛關心的問題,使得Eric Evans於十五年前提出的這套理論,在今天綻開出了新的光芒。固然領域驅動設計僅僅只是衆多面向對象編程的一種實踐,經過領域驅動設計將UML等方法靈活的運用其中,經過打破原有數據庫關係建模給代碼形成的桎梏,讓開發者可以真正的實現面向對象編程。

然而思惟模式的轉換並不是易事,從過程式代碼中,抽離出與對象有關的行爲,遠比理解這幾個概念要複雜,這須要大量經驗的積累。

領域的複雜性
毋庸置疑,數據庫建模驅動軟件開發具備速度快、學習成本低的顯著特色,在許多項目中,能在短時間內能夠給開發者帶來許多便利;而應用領域驅動設計,則能夠在更長的維護週期內,給軟件維護帶來實質性好處。

兩種不一樣類型的開發模式,根據企業實際出發進行選擇,還只是開始,但能真正運用好領域驅動設計或者UML、面向對象設計這種軟件工程的美學思惟來改造咱們的系統,讓系統綻開出更加璀璨的光芒,這纔是軟件設計的樂趣所在。

如何畫馬


本文版權歸原做者和博客園共同擁有。做品採用知識共享署名-非商業性使用-相同方式共享4.0 國際許可協議進行許可。 本文來自: 溪源 | 長沙.NET技術社區。閱讀更多精彩好文,歡迎關注長沙.NET技術社區公衆號【DotNET技術圈】。 首發於溪源的我的博客www.techq.xyz

相關文章
相關標籤/搜索