領域驅(qū)動設計對軟件復雜度的應對_第1頁
領域驅(qū)動設計對軟件復雜度的應對_第2頁
領域驅(qū)動設計對軟件復雜度的應對_第3頁
領域驅(qū)動設計對軟件復雜度的應對_第4頁
已閱讀5頁,還剩15頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、 領域驅(qū)動設計對軟件復雜度的應對 不管是因為規(guī)模與結構制造的理解力障礙,還是因為變化帶來的預測能力問題,最終的決定因素還是需求。Eric Evans認為“很多應用程序最主要的復雜性并不在技術上,而是來自領域本身、用戶的活動或業(yè)務”。因而,領域驅(qū)動設計關注的焦點在于領域和領域邏輯,因為軟件系統(tǒng)的本質(zhì)其實是給客戶(用戶)提供具有業(yè)務價值的領域功能。需求引起的軟件復雜度需求分為業(yè)務需求與質(zhì)量屬性需求,因而需求引起的復雜度可以分為兩個方面:技術復雜度與業(yè)務復雜度。技術復雜度來自需求的質(zhì)量屬性,諸如安全、高性能、高并發(fā)、高可用性等需求,為軟件設計帶來了極大的挑戰(zhàn)。讓人難受的是這些因素彼此之間又可能互相矛

2、盾互相影響。例如,系統(tǒng)安全性要求對訪問進行控制,無論是增加防火墻,還是對傳遞的消息進行加密,又或者對訪問請求進行認證和授權,都需要為整個系統(tǒng)架構添加額外的間接層。這不可避免會對訪問的低延遲產(chǎn)生影響,拖慢了系統(tǒng)的整體性能。又例如為了滿足系統(tǒng)的高并發(fā)訪問,我們需要對應用服務進行物理分解,通過橫向增加更多的機器來分散訪問負載;同時,我們還可以將一個同步的訪問請求拆分為多級步驟的異步請求,再通過引入消息中間件對這些請求進行整合和分散處理。這種分離一方面增加了系統(tǒng)架構的復雜性,另一方面也因為引入了更多的資源,使得系統(tǒng)的高可用面臨挑戰(zhàn),并增加了維護數(shù)據(jù)一致性的難度。業(yè)務復雜度對應了客戶的業(yè)務需求,因而這種

3、復雜度往往會隨著需求規(guī)模的增大而增加。由于需求不可能做到完全獨立,一旦規(guī)模擴大到一定程度,不僅產(chǎn)生了功能數(shù)量的增加,還會因為功能互相之間的依賴與影響使得這種復雜度產(chǎn)生疊加,進而影響到整個系統(tǒng)的質(zhì)量屬性,例如系統(tǒng)的可維護性與可擴展性。在考慮系統(tǒng)的業(yè)務需求時,還會因為溝通不暢、客戶需求不清晰等多種局外因素帶來需求的變更和修改。如果不能很好地控制這種變更,就可能因為多次修改導致業(yè)務邏輯糾纏不清,系統(tǒng)可能開始慢慢腐爛,變得不可維護,最終形成一種如Brian Foote和Joseph Yoder所說的“大泥球”系統(tǒng)。以電商系統(tǒng)的促銷規(guī)則為例。針對不同類型的顧客與產(chǎn)品,商家會提供不同的促銷力度;促銷的形式

4、多種多樣,包括贈送積分、紅包、優(yōu)惠券、禮品;促銷的周期需要支持定制,既可以是特定的日期,例如雙十一促銷,也可以是節(jié)假日的固定促銷模式。如果我們在設計時沒有充分考慮促銷規(guī)則的復雜度,并處理好促銷規(guī)則與商品、顧客、賣家與支付乃至于物流、倉儲之間的關系,開發(fā)過程就會變得踉踉蹌蹌,舉步維艱。技術復雜度與業(yè)務復雜度并非完全獨立,二者混合在一起產(chǎn)生的化合作用更讓系統(tǒng)的復雜度變得不可預期,難以掌控。同時,技術的變化維度與業(yè)務的變化維度并不相同,產(chǎn)生變化的原因也不一致,倘若未能很好地界定二者之間的關系,系統(tǒng)架構缺乏清晰邊界,會變得難以梳理。復雜度一旦增加,團隊規(guī)模也將隨之擴大,再揉以嚴峻的交付周期、人員流動等

5、諸多因素,就好似將各種不穩(wěn)定的易燃易爆氣體混合在一個不可逃逸的密閉容器中一般,隨時都可能爆炸:隨著業(yè)務需求的增加與變化,以及對質(zhì)量屬性的高標準要求,自然也引起了軟件系統(tǒng)規(guī)模的增大與結構的繁雜,至于變化,則是軟件開發(fā)繞不開的話題。因此,當我們面對一個相對復雜的軟件系統(tǒng)時,通常面臨的問題在于:問題域過于龐大而復雜,使得從問題域中尋求解決方案的挑戰(zhàn)增加。該問題與軟件系統(tǒng)的規(guī)模有關。開發(fā)人員將業(yè)務邏輯的復雜度與技術實現(xiàn)的復雜度混淆在一起。該問題與軟件系統(tǒng)的結構有關。隨著需求的增長和變化,無法控制業(yè)務復雜度和技術復雜度。該問題與軟件系統(tǒng)的變化有關。針對這三個問題,領域驅(qū)動設計都給出了自己的應對措施。領域

6、驅(qū)動設計的應對措施隔離業(yè)務復雜度與技術復雜度要避免業(yè)務邏輯的復雜度與技術實現(xiàn)的復雜度混淆在一起,首要任務就是確定業(yè)務邏輯與技術實現(xiàn)的邊界,從而隔離各自的復雜度。這種隔離也是題中應有之義,畢竟技術與業(yè)務的關注點完全不同。例如在電商的領域邏輯中,訂單業(yè)務關注的業(yè)務規(guī)則包括驗證訂單有效性,計算訂單總額,提交和審核訂單的流程等;技術關注點則從實現(xiàn)層面保障這些業(yè)務能夠正確地完成,包括確保分布式系統(tǒng)之間的數(shù)據(jù)一致性,確保服務之間通信的正確性等。業(yè)務邏輯并不關心技術是如何實現(xiàn)的。無論采用何種技術,只要業(yè)務需求不變,業(yè)務規(guī)則就不會變化。換言之,理想狀態(tài)下,我們應該保證業(yè)務規(guī)則與技術實現(xiàn)是正交的。領域驅(qū)動設計通

7、過分層架構與六邊形架構確保業(yè)務邏輯與技術實現(xiàn)的隔離。分層架構的關注點分離分層架構遵循了“關注點分離”原則,將屬于業(yè)務邏輯的關注點放到領域?qū)樱―omain Layer)中,而將支撐業(yè)務邏輯的技術實現(xiàn)放到基礎設施層(Infrastructure Layer)中。同時,領域驅(qū)動設計又頗具創(chuàng)見的引入了應用層(Application Layer)。應用層扮演了雙重角色。一方面它作為業(yè)務邏輯的外觀(Facade),暴露了能夠體現(xiàn)業(yè)務用例的應用服務接口;另一方面它又是業(yè)務邏輯與技術實現(xiàn)的粘合劑,實現(xiàn)二者之間的協(xié)作。下圖展現(xiàn)的就是一個典型的領域驅(qū)動設計分層架構。藍色區(qū)域的內(nèi)容與業(yè)務邏輯有關,灰色區(qū)域的內(nèi)容與技

8、術實現(xiàn)有關,二者涇渭分明,然后匯合在應用層。應用層確定了業(yè)務邏輯與技術實現(xiàn)的邊界,通過直接依賴或者依賴注入(DI,Dependency Injection)的方式將二者結合起來:六邊形架構的內(nèi)外分離由Cockburn提出的六邊形架構則以“內(nèi)外分離”的方式,更加清晰地勾勒出業(yè)務邏輯與技術實現(xiàn)的邊界,且將業(yè)務邏輯放在了架構的核心位置。這種架構模式改變了我們觀察系統(tǒng)架構的視角:體現(xiàn)業(yè)務邏輯的應用層與領域?qū)犹幱诹呅渭軜嫷膬?nèi)核,并通過內(nèi)部的六邊形邊界與基礎設施的模塊隔離開。當我們在進行軟件開發(fā)時,只要恪守架構上的六邊形邊界,就不會讓技術實現(xiàn)的復雜度污染到業(yè)務邏輯,保證了領域的整潔。邊界還隔離了變化產(chǎn)生

9、的影響。如果我們在領域?qū)踊驊脤映橄罅思夹g實現(xiàn)的接口,再通過依賴注入將控制的方向倒轉(zhuǎn),業(yè)務內(nèi)核就會變得更加的穩(wěn)定,不會因為技術選型或其他決策的變化而導致領域代碼的修改。案例:隔離數(shù)據(jù)庫與緩存的訪問領域驅(qū)動設計建議我們在領域?qū)咏①Y源庫(Repository)的抽象,它的實現(xiàn)則被放在基礎設施層,然后采用依賴注入在運行時為業(yè)務邏輯注入具體的資源庫實現(xiàn)。那么,對于處于內(nèi)核之外的Repositories模塊而言,即使選擇從MyBatis遷移到Sprint Data,領域代碼都不會受到牽連:package practiceddd.ecommerce.ordercontext.application;Tr

10、ansactionpublic class OrderAppService Service private PlaceOrderService placeOrder; public void placeOrder(Identity buyerId, List items, ShippingAddress shipping, BillingAddress billing) try palceOrder.execute(buyerId, items, shipping, billing); catch (OrderRepositoryException | InvalidOrderExceptio

11、n | Exception ex) ex.printStackTrace(); logger.error(ex.getMessage(); package practiceddd.ecommerce.ordercontext.domain;public interface OrderRepository List forBuyerId(Identity buyerId); void add(Order order); public class PlaceOrderService Repository private OrderRepository orderRepository; Servic

12、e private OrderValidator orderValidator; public void execute(Identity buyerId, List items, ShippingAddress shipping, BillingAddress billing) Order order = Order.create(buyerId, items, shipping, billing); if (orderValidator.isValid(order) orderRepository.add(order); else throw new InvalidOrderExcepti

13、on(String.format(the order which placed by buyer with %s is invalid., buyerId); package practiceddd.ecommerce.ordercontext.infrastructure.db;public class OrderMybatisRepository implements OrderRepository public class OrderSprintDataRepository implements OrderRepository 對緩存的處理可以如法炮制,但它與資源庫稍有不同之處。資源庫作

14、為訪問領域模型對象的入口,其本身提供的增刪改查功能,在抽象層面上是對領域資源的訪問。因此在領域驅(qū)動設計中,我們通常將資源庫的抽象歸屬到領域?qū)?。對緩存的訪問則不相同,它的邏輯就是對key和value的操作,與具體的領域無關。倘若要為緩存的訪問方法定義抽象接口,在分層的歸屬上應該屬于應用層,至于實現(xiàn)則屬于技術范疇,應該放在基礎設施層:package practiceddd.ecommerce.ordercontext.application;Transactionpublic class OrderAppService Repository private OrderRepository orde

15、rRepository; Service private CacheClientList cacheClient; public List findBy(Identity buyerId) OptionalList cachedOrders = cacheClient.get(buyerId.value(); if (cachedOrders.isPresent() return orders.get(); List orders = orderRepository.forBuyerId(buyerId); if (!orders.isEmpty() cacheClient.put(buyer

16、Id.value(), orders); return orders; package practiceddd.ecommerce.ordercontext.application.cache;public interface CacheClient Optional get(String key); void put(String key, T value);package practiceddd.ecommerce.ordercontext.infrastructure.cache;public class RedisCacheClient implements CacheClient 限

17、界上下文的分而治之在前面分析緩存訪問接口的歸屬時,我們將接口放在了系統(tǒng)的應用層。從層次的職責來看,這樣的設計是合理的,但它卻使得系統(tǒng)的應用層變得更加臃腫,職責也變得不夠單一了。這是分層架構與六邊形架構的局限所在,因為這兩種架構模式僅僅體現(xiàn)了一個軟件系統(tǒng)的邏輯劃分。倘若我們將一個軟件系統(tǒng)視為一個縱橫交錯的魔方,前述的邏輯劃分僅僅是一種水平方向的劃分。至于垂直方向的劃分,則是面向垂直業(yè)務的切割。這種方式更利于控制軟件系統(tǒng)的規(guī)模,將一個龐大的軟件系統(tǒng)劃分為松散耦合的多個小系統(tǒng)的組合。針對前述案例,我們可以將緩存視為一個獨立的子系統(tǒng)。它同樣擁有自己的業(yè)務邏輯和技術實現(xiàn),因而也可以為其建立屬于緩存領域的

18、分層架構。在架構的宏觀視角,這個緩存子系統(tǒng)與訂單子系統(tǒng)處于同一個抽象層次,這一概念在領域驅(qū)動設計中,被稱之為限界上下文(Bounded Context)。針對龐大而復雜的問題域,限界上下文采用了“分而治之”的思想對問題域進行了分解,有效地控制了問題域的規(guī)模,進而控制了整個系統(tǒng)的規(guī)模。一旦規(guī)模減小,無論業(yè)務復雜度還是技術復雜度,都會得到顯著的降低,在對領域進行分析以及建模時,也能變得更容易。如果說分層架構與六邊形架構確保了業(yè)務邏輯與技術實現(xiàn)的隔離,則限界上下文對整個系統(tǒng)進行了劃分,將一個大系統(tǒng)拆分為一個個小系統(tǒng)后,我們再利用分層架構與六邊形架構思想對其進行邏輯分層,設計會變得更易于把控,系統(tǒng)的架

19、構也會變得更加的清晰。案例:限界上下文幫助架構的演進國際報稅系統(tǒng)是為跨國公司的駐外出差雇員(系統(tǒng)中被稱之為Assignee)提供方便一體化的稅收信息填報平臺??蛻羰且患視嫀熓聞账撌聞账膶T(Admin)通過該平臺可以收集雇員提交的報稅信息,然后對這些信息進行稅務評審。如果Admin評審出信息有問題,則返回給Assignee重新修改和填報。一旦信息確認無誤,則進行稅收分析和計算,并獲得最終的稅務報告提交給當?shù)卣约肮蛦T本人。系統(tǒng)主要涉及的功能包括:駐外出差雇員的薪酬與福利稅收計劃與合規(guī)評審對稅收評審的分配管理稅收策略設計與評審對駐外出差雇員的稅收合規(guī)評審全球的Visa服務主要涉及的用戶

20、角色包括:Assignee:駐外出差雇員Admin:稅務專員Client:出差雇員的雇主在早期的架構設計時,架構師并沒有對整個系統(tǒng)的問題域進行拆分,而是基于用戶角色對系統(tǒng)進行了簡單粗暴的劃分,分為兩個相對獨立的子系統(tǒng):Frond End與Office End。這兩個子系統(tǒng)單獨部署,分別面向Assignee與Admin。系統(tǒng)之間的集成則通過消息和Web Service進行通信。兩個子系統(tǒng)的開發(fā)分屬不同的團隊,F(xiàn)rond End由美國的團隊負責開發(fā)與維護,而Office End則由印度的團隊負責。整個架構如下圖所示:采用這種架構面臨如下問題:龐大的代碼庫:整個Front End和Office En

21、d都沒有做物理分解,隨著需求的增多,代碼庫變得格外龐大分散的邏輯:系統(tǒng)分解的邊界是不合理的,沒有按照業(yè)務分解,而是按照用戶的角色進行分解,導致大量相似的邏輯分散在兩個不同的子系統(tǒng)中重復的數(shù)據(jù):兩個子系統(tǒng)中存在業(yè)務重疊,因而也導致了部分數(shù)據(jù)的重復復雜的集成:Front End與Office End因為某些相關的業(yè)務需要彼此通信,這種集成關系是雙向的,且由兩個不同的團隊開發(fā),導致集成的接口混亂,消息協(xié)議多樣化知識未形成共享:兩個團隊完全獨立開發(fā),沒有掌握端對端的整體流程,團隊之間沒有形成知識的共享無法應對需求變化: 新增需求包括對國際旅游、Visa的支持,現(xiàn)有系統(tǒng)的架構無法很好地支持這些變化采用領

22、域驅(qū)動設計,我們將架構的主要關注點放在了“領域”,與客戶進行了充分的需求溝通和交流。通過分析已有系統(tǒng)的問題域,結合客戶提出的新需求,對整個問題域進行了梳理,并利用限界上下文對問題域進行了分解,獲得了如下限界上下文:Account Management:管理用戶的身份與配置信息Calendar Management:管理用戶的日程與旅行足跡之后,客戶希望能改進需求,做到全球范圍內(nèi)的工作指派與管理,目的在于提高公司的運營效率。通過對領域的分析,我們又識別出兩個限界上下文。在原有的系統(tǒng)架構中,這兩個限界上下文同時處于Front End與Office End之中,屬于重復開發(fā)的業(yè)務邏輯:Work Re

23、cord Management:實現(xiàn)工作的分配與任務的跟蹤File Sharing:目的是實現(xiàn)客戶與會計師事務所之間的文件交換隨著我們對領域知識的逐漸深入理解與分析,又隨之識別出如下限界上下文:Consent:管理合法的遵守法規(guī)的狀態(tài)Notification:管理系統(tǒng)與客戶之間的交流Questionnaire:對問卷調(diào)查的數(shù)據(jù)收集這個領域分析的過程實際上就是通過對領域的分析,引入限界上下文對問題域進行分解,通過降低規(guī)模的方式降低問題域的復雜度;同時,通過為模型確定清晰的邊界,使得系統(tǒng)的結構變得更加的清晰,保證了領域邏輯的一致性。一旦確定了清晰的領域模型,就能夠幫助我們更加容易地發(fā)現(xiàn)系統(tǒng)的可重用

24、點與可擴展點,并遵循“高內(nèi)聚松耦合”原則對系統(tǒng)職責進行合理分配,再輔以分層架構劃分邏輯邊界,如下圖所示:我們將識別出來的限界上下文定義為微服務,并對外公開REST服務接口。UI Applications是一個薄薄的展現(xiàn)層,它會調(diào)用后端的RESTful服務,也使得服務在保證接口不變的前提下能夠單獨演化。每個服務都是獨立的,可以單獨部署,因而可以針對服務建立單獨的代碼庫和對應的特性團隊(Feature Team)。服務的重用性和可擴展性也有了更好的保障,服務與UI之間的集成變得更簡單,整個架構更加清晰了。領域模型對領域知識的抽象領域模型是對業(yè)務需求的一種抽象,表達了領域概念、領域規(guī)則以及領域概念之

25、間的關系。一個好的領域模型是對統(tǒng)一語言的可視化表示,通過它可以減少需求溝通可能出現(xiàn)的歧義;通過提煉領域知識,并運用抽象的領域模型去表達,就可以達到對領域邏輯的化繁為簡。模型是封裝,實現(xiàn)了對業(yè)務細節(jié)的隱藏;模型是抽象,提取了領域知識的共同特征,保留了面對變化時能夠良好擴展的可能性。案例:項目管理系統(tǒng)的領域模型我們開發(fā)的項目管理系統(tǒng)需要支持多種軟件項目管理流程,例如瀑布、RUP、XP或者Scrum。這些項目管理流程是迥然不同的,如果需要各自提供不同的解決方案,就會使得系統(tǒng)的模型變得非常復雜,也可能引入許多不必要的重復。通過領域建模,我們可以對項目管理領域的知識進行抽象,尋找具有共同特征的領域概念。

26、這就需要分析各種項目管理流程的主要特征與表現(xiàn),才能從中提煉出領域模型。瀑布式軟件開發(fā)由需求、分析、設計、編碼、測試、驗收六個階段構成,每個階段都由不同的活動構成,這些活動可能是設計或開發(fā)任務,也可能是召開評審會。流程如下圖所示:RUP清晰地劃分了四個階段:先啟階段、細化階段、構造階段與交付階段。每個階段可以包含一到多個迭代,每個迭代有不同的工作,例如業(yè)務建模、分析設計、配置與變更管理等。RUP的流程如下圖所示:XP作為一種敏捷方法,采用了迭代的增量式開發(fā),提倡為客戶交付具有業(yè)務價值的可運行軟件。在執(zhí)行交付計劃之前,XP要求團隊對系統(tǒng)的架構做一次預研(Architectual Spike,又被譯

27、為架構穿刺)。當架構的初始方案確定后,就可以進入每次小版本的交付。每個小版本交付又被劃分為多個周期相同的迭代。在迭代過程中,要求執(zhí)行一些必須的活動,如編寫用戶故事、故事點估算、驗收測試等。XP的流程如下圖所示:Scrum同樣是迭代的增量開發(fā)過程。項目在開始之初,需要在準備階段確定系統(tǒng)愿景、梳理業(yè)務用例、確定產(chǎn)品待辦項(product backlog)、制定發(fā)布計劃以及組建團隊。一旦在確定了產(chǎn)品待辦項以及發(fā)布計劃之后,就進入sprint迭代階段。sprint迭代過程是一個固定時長的項目過程,在這個過程中,整個團隊需要召開計劃會議、每日站會、評審會議和回顧會議。Scrum的流程如下圖所示:不同的項

28、目管理流程具有不同的業(yè)務概念。例如瀑布式開發(fā)分為了六個階段,但卻沒有發(fā)布和迭代的概念。RUP沒有發(fā)布的概念,而Scrum又為迭代引入了sprint的概念。不同的項目管理流程具有不同的業(yè)務規(guī)則。例如RUP的四個階段會包含多個迭代周期,每個迭代周期都需要完成對應的工作,只是不同的工作在不同階段所占的比重不同。XP需要在進入發(fā)布階段之前,進行架構預研,而在每次小版本發(fā)布之前,都需要進行驗收測試和客戶驗收。Scrum的sprint是一個基本固定的流程,每個迭代召開的四會(計劃會議、評審會議、回顧會議與每日站會)都有明確的目標。領域建模就是要從這些紛繁復雜的領域邏輯中尋找到能夠表示項目管理領域的概念,并利用面向?qū)ο蠼7妒交蚱渌妒?/p>

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論