自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

領(lǐng)域/驅(qū)動(dòng)設(shè)計(jì)對(duì)軟件復(fù)雜度的應(yīng)對(duì)

開(kāi)發(fā) 開(kāi)發(fā)工具
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)關(guān)注的焦點(diǎn)在于領(lǐng)域和領(lǐng)域邏輯,因?yàn)檐浖到y(tǒng)的本質(zhì)其實(shí)是給客戶(用戶)提供具有業(yè)務(wù)價(jià)值的領(lǐng)域功能。

不管是因?yàn)橐?guī)模與結(jié)構(gòu)制造的理解力障礙,還是因?yàn)樽兓瘞?lái)的預(yù)測(cè)能力問(wèn)題,最終的決定因素還是因?yàn)樾枨蟆ric Evans認(rèn)為“很多應(yīng)用程序最主要的復(fù)雜性并不在技術(shù)上,而是來(lái)自領(lǐng)域本身、用戶的活動(dòng)或業(yè)務(wù)”。因而,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)關(guān)注的焦點(diǎn)在于領(lǐng)域和領(lǐng)域邏輯,因?yàn)檐浖到y(tǒng)的本質(zhì)其實(shí)是給客戶(用戶)提供具有業(yè)務(wù)價(jià)值的領(lǐng)域功能。

軟件復(fù)雜度

一、需求引起的軟件復(fù)雜度

需求分為業(yè)務(wù)需求與質(zhì)量屬性需求,因而需求引起的復(fù)雜度可以分為兩個(gè)方面:技術(shù)復(fù)雜度與業(yè)務(wù)復(fù)雜度。

技術(shù)復(fù)雜度來(lái)自需求的質(zhì)量屬性,諸如安全、高性能、高并發(fā)、高可用性等需求,為軟件設(shè)計(jì)帶來(lái)了極大的挑戰(zhàn)。讓人難受的是這些因素彼此之間又可能互相矛盾互相影響。例如,系統(tǒng)安全性要求對(duì)訪問(wèn)進(jìn)行控制,無(wú)論是增加防火墻,還是對(duì)傳遞的消息進(jìn)行加密,又或者對(duì)訪問(wèn)請(qǐng)求進(jìn)行認(rèn)證和授權(quán),都需要為整個(gè)系統(tǒng)架構(gòu)添加額外的間接層。這不可避免會(huì)對(duì)訪問(wèn)的低延遲產(chǎn)生影響,拖慢了系統(tǒng)的整體性能。又例如為了滿足系統(tǒng)的高并發(fā)訪問(wèn),我們需要對(duì)應(yīng)用服務(wù)進(jìn)行物理分解,通過(guò)橫向增加更多的機(jī)器來(lái)分散訪問(wèn)負(fù)載;同時(shí),我們還可以將一個(gè)同步的訪問(wèn)請(qǐng)求拆分為多級(jí)步驟的異步請(qǐng)求,再通過(guò)引入消息中間件對(duì)這些請(qǐng)求進(jìn)行整合和分散處理。這種分離一方面增加了系統(tǒng)架構(gòu)的復(fù)雜性,另一方面也因?yàn)橐肓烁嗟馁Y源,使得系統(tǒng)的高可用面臨挑戰(zhàn),并增加了維護(hù)數(shù)據(jù)一致性的難度。

業(yè)務(wù)復(fù)雜度對(duì)應(yīng)了客戶的業(yè)務(wù)需求,因而這種復(fù)雜度往往會(huì)隨著需求規(guī)模的增大而增加。由于需求不可能做到完全獨(dú)立,一旦規(guī)模擴(kuò)大到一定程度,不僅產(chǎn)生了功能數(shù)量的增加,還會(huì)因?yàn)楣δ芑ハ嘀g的依賴與影響使得這種復(fù)雜度產(chǎn)生疊加,進(jìn)而影響到整個(gè)系統(tǒng)的質(zhì)量屬性,例如系統(tǒng)的可維護(hù)性與可擴(kuò)展性。在考慮系統(tǒng)的業(yè)務(wù)需求時(shí),還會(huì)因?yàn)闇贤ú粫?、客戶需求不清晰等多種局外因素帶來(lái)需求的變更和修改。如果不能很好地控制這種變更,就可能因?yàn)槎啻涡薷膶?dǎo)致業(yè)務(wù)邏輯糾纏不清,系統(tǒng)可能開(kāi)始慢慢腐爛,變得不可維護(hù),最終形成一種如Brian Foote和Joseph Yoder所說(shuō)的“大泥球”系統(tǒng)。

以電商系統(tǒng)的促銷規(guī)則為例。針對(duì)不同類型的顧客與產(chǎn)品,商家會(huì)提供不同的促銷力度;促銷的形式多種多樣,包括贈(zèng)送積分、紅包、優(yōu)惠券、禮品;促銷的周期需要支持定制,既可以是特定的日期,例如雙十一促銷,也可以是節(jié)假日的固定促銷模式。如果我們?cè)谠O(shè)計(jì)時(shí)沒(méi)有充分考慮促銷規(guī)則的復(fù)雜度,并處理好促銷規(guī)則與商品、顧客、賣(mài)家與支付乃至于物流、倉(cāng)儲(chǔ)之間的關(guān)系,開(kāi)發(fā)過(guò)程就會(huì)變得踉踉蹌蹌,舉步維艱。

技術(shù)復(fù)雜度與業(yè)務(wù)復(fù)雜度并非完全獨(dú)立,二者混合在一起產(chǎn)生的化合作用更讓系統(tǒng)的復(fù)雜度變得不可預(yù)期,難以掌控。同時(shí),技術(shù)的變化維度與業(yè)務(wù)的變化維度并不相同,產(chǎn)生變化的原因也不一致,倘若未能很好地界定二者之間的關(guān)系,系統(tǒng)架構(gòu)缺乏清晰邊界,會(huì)變得難以梳理。復(fù)雜度一旦增加,團(tuán)隊(duì)規(guī)模也將隨之?dāng)U大,再揉以嚴(yán)峻的交付周期、人員流動(dòng)等諸多因素,就好似將各種不穩(wěn)定的易燃易爆氣體混合在一個(gè)不可逃逸的密閉容器中一般,隨時(shí)都可能爆炸:

軟件復(fù)雜度

隨著業(yè)務(wù)需求的增加與變化,以及對(duì)質(zhì)量屬性的高標(biāo)準(zhǔn)要求,自然也引起了軟件系統(tǒng)規(guī)模的增大與結(jié)構(gòu)的繁雜,至于變化,則是軟件開(kāi)發(fā)繞不開(kāi)的話題。因此,當(dāng)我們面對(duì)一個(gè)相對(duì)復(fù)雜的軟件系統(tǒng)時(shí),通常面臨的問(wèn)題在于:

  • 問(wèn)題域過(guò)于龐大而復(fù)雜,使得從問(wèn)題域中尋求解決方案的挑戰(zhàn)增加。該問(wèn)題與軟件系統(tǒng)的規(guī)模有關(guān)。
  • 開(kāi)發(fā)人員將業(yè)務(wù)邏輯的復(fù)雜度與技術(shù)實(shí)現(xiàn)的復(fù)雜度混淆在一起。該問(wèn)題與軟件系統(tǒng)的結(jié)構(gòu)有關(guān)。
  • 隨著需求的增長(zhǎng)和變化,無(wú)法控制業(yè)務(wù)復(fù)雜度和技術(shù)復(fù)雜度。該問(wèn)題與軟件系統(tǒng)的變化有關(guān)。

針對(duì)這三個(gè)問(wèn)題,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)都給出了自己的應(yīng)對(duì)措施。

二、領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的應(yīng)對(duì)措施

1. 隔離業(yè)務(wù)復(fù)雜度與技術(shù)復(fù)雜度

要避免業(yè)務(wù)邏輯的復(fù)雜度與技術(shù)實(shí)現(xiàn)的復(fù)雜度混淆在一起,首要任務(wù)就是確定業(yè)務(wù)邏輯與技術(shù)實(shí)現(xiàn)的邊界,從而隔離各自的復(fù)雜度。這種隔離也是題中應(yīng)有之義,畢竟技術(shù)與業(yè)務(wù)的關(guān)注點(diǎn)完全不同。例如在電商的領(lǐng)域邏輯中,訂單業(yè)務(wù)關(guān)注的業(yè)務(wù)規(guī)則包括驗(yàn)證訂單有效性,計(jì)算訂單總額,提交和審核訂單的流程等;技術(shù)關(guān)注點(diǎn)則從實(shí)現(xiàn)層面保障這些業(yè)務(wù)能夠正確地完成,包括確保分布式系統(tǒng)之間的數(shù)據(jù)一致性,確保服務(wù)之間通信的正確性等。

業(yè)務(wù)邏輯并不關(guān)心技術(shù)是如何實(shí)現(xiàn)的。無(wú)論采用何種技術(shù),只要業(yè)務(wù)需求不變,業(yè)務(wù)規(guī)則就不會(huì)變化。換言之,理想狀態(tài)下,我們應(yīng)該保證業(yè)務(wù)規(guī)則與技術(shù)實(shí)現(xiàn)是正交的。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)通過(guò)分層架構(gòu)與六邊形架構(gòu)確保業(yè)務(wù)邏輯與技術(shù)實(shí)現(xiàn)的隔離。

(1) 分層架構(gòu)的關(guān)注點(diǎn)分離

分層架構(gòu)遵循了“關(guān)注點(diǎn)分離”原則,將屬于業(yè)務(wù)邏輯的關(guān)注點(diǎn)放到領(lǐng)域?qū)?Domain Layer)中,而將支撐業(yè)務(wù)邏輯的技術(shù)實(shí)現(xiàn)放到基礎(chǔ)設(shè)施層(Infrastructure Layer)中。同時(shí),領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)又頗具創(chuàng)見(jiàn)的引入了應(yīng)用層(Application Layer)。應(yīng)用層扮演了雙重角色。一方面它作為業(yè)務(wù)邏輯的外觀(Facade),暴露了能夠體現(xiàn)業(yè)務(wù)用例的應(yīng)用服務(wù)接口;另一方面它又是業(yè)務(wù)邏輯與技術(shù)實(shí)現(xiàn)的粘合劑,實(shí)現(xiàn)二者之間的協(xié)作。

下圖展現(xiàn)的就是一個(gè)典型的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)分層架構(gòu)。藍(lán)色區(qū)域的內(nèi)容與業(yè)務(wù)邏輯有關(guān),灰色區(qū)域的內(nèi)容與技術(shù)實(shí)現(xiàn)有關(guān),二者涇渭分明,然后匯合在應(yīng)用層。應(yīng)用層確定了業(yè)務(wù)邏輯與技術(shù)實(shí)現(xiàn)的邊界,通過(guò)直接依賴或者依賴注入(DI,Dependency Injection)的方式將二者結(jié)合起來(lái):

(2) 六邊形架構(gòu)的內(nèi)外分離

由Cockburn提出的六邊形架構(gòu)則以“內(nèi)外分離”的方式,更加清晰地勾勒出業(yè)務(wù)邏輯與技術(shù)實(shí)現(xiàn)的邊界,且將業(yè)務(wù)邏輯放在了架構(gòu)的核心位置。這種架構(gòu)模式改變了我們觀察系統(tǒng)架構(gòu)的視角:

體現(xiàn)業(yè)務(wù)邏輯的應(yīng)用層與領(lǐng)域?qū)犹幱诹呅渭軜?gòu)的內(nèi)核,并通過(guò)內(nèi)部的六邊形邊界與基礎(chǔ)設(shè)施的模塊隔離開(kāi)。當(dāng)我們?cè)谶M(jìn)行軟件開(kāi)發(fā)時(shí),只要恪守架構(gòu)上的六邊形邊界,就不會(huì)讓技術(shù)實(shí)現(xiàn)的復(fù)雜度污染到業(yè)務(wù)邏輯,保證了領(lǐng)域的整潔。邊界還隔離了變化產(chǎn)生的影響。如果我們?cè)陬I(lǐng)域?qū)踊驊?yīng)用層抽象了技術(shù)實(shí)現(xiàn)的接口,再通過(guò)依賴注入將控制的方向倒轉(zhuǎn),業(yè)務(wù)內(nèi)核就會(huì)變得更加的穩(wěn)定,不會(huì)因?yàn)榧夹g(shù)選型或其他決策的變化而導(dǎo)致領(lǐng)域代碼的修改。

(3) 案例:隔離數(shù)據(jù)庫(kù)與緩存的訪問(wèn)

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)建議我們?cè)陬I(lǐng)域?qū)咏①Y源庫(kù)(Repository)的抽象,它的實(shí)現(xiàn)則被放在基礎(chǔ)設(shè)施層,然后采用依賴注入在運(yùn)行時(shí)為業(yè)務(wù)邏輯注入具體的資源庫(kù)實(shí)現(xiàn)。那么,對(duì)于處于內(nèi)核之外的Repositories模塊而言,即使選擇從MyBatis遷移到Sprint Data,領(lǐng)域代碼都不會(huì)受到牽連:

  1. package practiceddd.ecommerce.ordercontext.application; 
  2.  
  3. @Transaction 
  4. public class OrderAppService { 
  5.    @Service 
  6.    private PlaceOrderService placeOrder; 
  7.     
  8.    public void placeOrder(Identity buyerId, List<OrderItem> items, ShippingAddress shipping, BillingAddress billing) { 
  9.        try { 
  10.            palceOrder.execute(buyerId, items, shipping, billing); 
  11.        } catch (OrderRepositoryException | InvalidOrderException | Exception ex) { 
  12.            ex.printStackTrace(); 
  13.            logger.error(ex.getMessage()); 
  14.        } 
  15.    } 
  16.  
  17. package practiceddd.ecommerce.ordercontext.domain; 
  18.  
  19. public interface OrderRepository { 
  20.    List<Order> forBuyerId(Identity buyerId); 
  21.    void add(Order order); 
  22. }  
  23.  
  24. public class PlaceOrderService { 
  25.    @Repository 
  26.    private OrderRepository orderRepository; 
  27.  
  28.    @Service 
  29.    private OrderValidator orderValidator;     
  30.  
  31.    public void execute(Identity buyerId, List<OrderItem> items, ShippingAddress shipping, BillingAddress billing) { 
  32.        Order order = Order.create(buyerId, items, shipping, billing); 
  33.        if (orderValidator.isValid(order)) { 
  34.            orderRepository.add(order); 
  35.        } else { 
  36.            throw new InvalidOrderException(String.format("the order which placed by buyer with %s is invalid.", buyerId)); 
  37.        } 
  38.    } 
  39.  
  40. package practiceddd.ecommerce.ordercontext.infrastructure.db; 
  41.  
  42. public class OrderMybatisRepository implements OrderRepository {} 
  43. public class OrderSprintDataRepository implements OrderRepository {} 

對(duì)緩存的處理可以如法炮制,但它與資源庫(kù)稍有不同之處。資源庫(kù)作為訪問(wèn)領(lǐng)域模型對(duì)象的入口,其本身提供的增刪改查功能,在抽象層面上是對(duì)領(lǐng)域資源的訪問(wèn)。因此在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中,我們通常將資源庫(kù)的抽象歸屬到領(lǐng)域?qū)?。?duì)緩存的訪問(wèn)則不相同,它的邏輯就是對(duì)key和value的操作,與具體的領(lǐng)域無(wú)關(guān)。倘若要為緩存的訪問(wèn)方法定義抽象接口,在分層的歸屬上應(yīng)該屬于應(yīng)用層,至于實(shí)現(xiàn)則屬于技術(shù)范疇,應(yīng)該放在基礎(chǔ)設(shè)施層:

  1. package practiceddd.ecommerce.ordercontext.application; 
  2.  
  3. @Transaction 
  4. public class OrderAppService { 
  5.    @Repository 
  6.    private OrderRepository orderRepository; 
  7.  
  8.    @Service 
  9.    private CacheClient<List<Order>> cacheClient; 
  10.     
  11.    public List<Order> findBy(Identity buyerId) { 
  12.        Optional<List<Order>> cachedOrders = cacheClient.get(buyerId.value()); 
  13.        if (cachedOrders.isPresent()) { 
  14.            return orders.get(); 
  15.        }  
  16.        List<Order> orders = orderRepository.forBuyerId(buyerId); 
  17.        if (!orders.isEmpty()) { 
  18.            cacheClient.put(buyerId.value(), orders); 
  19.        } 
  20.        return orders; 
  21.    } 
  22.  
  23. package practiceddd.ecommerce.ordercontext.application.cache; 
  24.  
  25. public interface CacheClient<T> { 
  26.    Optional<T> get(String key); 
  27.    void put(String key, T value); 
  28.  
  29. package practiceddd.ecommerce.ordercontext.infrastructure.cache; 
  30.  
  31. public class RedisCacheClient<T> implements CacheClient<T> {} 

2. 限界上下文的分而治之

在前面分析緩存訪問(wèn)接口的歸屬時(shí),我們將接口放在了系統(tǒng)的應(yīng)用層。從層次的職責(zé)來(lái)看,這樣的設(shè)計(jì)是合理的,但它卻使得系統(tǒng)的應(yīng)用層變得更加臃腫,職責(zé)也變得不夠單一了。這是分層架構(gòu)與六邊形架構(gòu)的局限所在,因?yàn)檫@兩種架構(gòu)模式僅僅體現(xiàn)了一個(gè)軟件系統(tǒng)的邏輯劃分。倘若我們將一個(gè)軟件系統(tǒng)視為一個(gè)縱橫交錯(cuò)的魔方,前述的邏輯劃分僅僅是一種水平方向的劃分。至于垂直方向的劃分,則是面向垂直業(yè)務(wù)的切割。這種方式更利于控制軟件系統(tǒng)的規(guī)模,將一個(gè)龐大的軟件系統(tǒng)劃分為松散耦合的多個(gè)小系統(tǒng)的組合。

針對(duì)前述案例,我們可以將緩存視為一個(gè)獨(dú)立的子系統(tǒng)。它同樣擁有自己的業(yè)務(wù)邏輯和技術(shù)實(shí)現(xiàn),因而也可以為其建立屬于緩存領(lǐng)域的分層架構(gòu)。在架構(gòu)的宏觀視角,這個(gè)緩存子系統(tǒng)與訂單子系統(tǒng)處于同一個(gè)抽象層次,這一概念在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中,被稱之為限界上下文(Bounded Context)。

針對(duì)龐大而復(fù)雜的問(wèn)題域,限界上下文采用了“分而治之”的思想對(duì)問(wèn)題域進(jìn)行了分解,有效地控制了問(wèn)題域的規(guī)模,進(jìn)而控制了整個(gè)系統(tǒng)的規(guī)模。一旦規(guī)模減小,無(wú)論業(yè)務(wù)復(fù)雜度還是技術(shù)復(fù)雜度,都會(huì)得到顯著的降低,在對(duì)領(lǐng)域進(jìn)行分析以及建模時(shí),也能變得更容易。如果說(shuō)分層架構(gòu)與六邊形架構(gòu)確保了業(yè)務(wù)邏輯與技術(shù)實(shí)現(xiàn)的隔離,則限界上下文對(duì)整個(gè)系統(tǒng)進(jìn)行了劃分,將一個(gè)大系統(tǒng)拆分為一個(gè)個(gè)小系統(tǒng)后,我們?cè)倮梅謱蛹軜?gòu)與六邊形架構(gòu)思想對(duì)其進(jìn)行邏輯分層,設(shè)計(jì)會(huì)變得更易于把控,系統(tǒng)的架構(gòu)也會(huì)變得更加的清晰。

案例:限界上下文幫助架構(gòu)的演進(jìn)

國(guó)際報(bào)稅系統(tǒng)是為跨國(guó)公司的駐外出差雇員(系統(tǒng)中被稱之為Assignee)提供方便一體化的稅收信息填報(bào)平臺(tái)。客戶是一家會(huì)計(jì)師事務(wù)所,該事務(wù)所的專員(Admin)通過(guò)該平臺(tái)可以收集雇員提交的報(bào)稅信息,然后對(duì)這些信息進(jìn)行稅務(wù)評(píng)審。如果Admin評(píng)審出信息有問(wèn)題,則返回給Assignee重新修改和填報(bào)。一旦信息確認(rèn)無(wú)誤,則進(jìn)行稅收分析和計(jì)算,并獲得最終的稅務(wù)報(bào)告提交給當(dāng)?shù)卣约肮蛦T本人。

系統(tǒng)主要涉及的功能包括:

  • 駐外出差雇員的薪酬與福利
  • 稅收計(jì)劃與合規(guī)評(píng)審
  • 對(duì)稅收評(píng)審的分配管理
  • 稅收策略設(shè)計(jì)與評(píng)審
  • 對(duì)駐外出差雇員的稅收合規(guī)評(píng)審
  • 全球的Visa服務(wù)

主要涉及的用戶角色包括:

  • Assignee:駐外出差雇員
  • Admin:稅務(wù)專員
  • Client:出差雇員的雇主

在早期的架構(gòu)設(shè)計(jì)時(shí),架構(gòu)師并沒(méi)有對(duì)整個(gè)系統(tǒng)的問(wèn)題域進(jìn)行拆分,而是基于用戶角色對(duì)系統(tǒng)進(jìn)行了簡(jiǎn)單粗暴的劃分,分為兩個(gè)相對(duì)獨(dú)立的子系統(tǒng):Frond End與Office End。這兩個(gè)子系統(tǒng)單獨(dú)部署,分別面向Assignee與Admin。系統(tǒng)之間的集成則通過(guò)消息和Web Service進(jìn)行通信。兩個(gè)子系統(tǒng)的開(kāi)發(fā)分屬不同的團(tuán)隊(duì),F(xiàn)rond End由美國(guó)的團(tuán)隊(duì)負(fù)責(zé)開(kāi)發(fā)與維護(hù),而Office End則由印度的團(tuán)隊(duì)負(fù)責(zé)。整個(gè)架構(gòu)如下圖所示:

采用這種架構(gòu)面臨如下問(wèn)題:

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

采用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),我們將架構(gòu)的主要關(guān)注點(diǎn)放在了“領(lǐng)域”,與客戶進(jìn)行了充分的需求溝通和交流。通過(guò)分析已有系統(tǒng)的問(wèn)題域,結(jié)合客戶提出的新需求,對(duì)整個(gè)問(wèn)題域進(jìn)行了梳理,并利用限界上下文對(duì)問(wèn)題域進(jìn)行了分解,獲得了如下限界上下文:

  • Account Management:管理用戶的身份與配置信息
  • Calendar Management:管理用戶的日程與旅行足跡

之后,客戶希望能改進(jìn)需求,做到全球范圍內(nèi)的工作指派與管理,目的在于提高公司的運(yùn)營(yíng)效率。通過(guò)對(duì)領(lǐng)域的分析,我們又識(shí)別出兩個(gè)限界上下文。在原有的系統(tǒng)架構(gòu)中,這兩個(gè)限界上下文同時(shí)處于Front End與Office End之中,屬于重復(fù)開(kāi)發(fā)的業(yè)務(wù)邏輯:

  • Work Record Management:實(shí)現(xiàn)工作的分配與任務(wù)的跟蹤
  • File Sharing:目的是實(shí)現(xiàn)客戶與會(huì)計(jì)師事務(wù)所之間的文件交換

隨著我們對(duì)領(lǐng)域知識(shí)的逐漸深入理解與分析,又隨之識(shí)別出如下限界上下文:

  • Consent:管理合法的遵守法規(guī)的狀態(tài)
  • Notification:管理系統(tǒng)與客戶之間的交流
  • Questionnaire:對(duì)問(wèn)卷調(diào)查的數(shù)據(jù)收集

這個(gè)領(lǐng)域分析的過(guò)程實(shí)際上就是通過(guò)對(duì)領(lǐng)域的分析,引入限界上下文對(duì)問(wèn)題域進(jìn)行分解,通過(guò)降低規(guī)模的方式降低問(wèn)題域的復(fù)雜度;同時(shí),通過(guò)為模型確定清晰的邊界,使得系統(tǒng)的結(jié)構(gòu)變得更加的清晰,保證了領(lǐng)域邏輯的一致性。一旦確定了清晰的領(lǐng)域模型,就能夠幫助我們更加容易地發(fā)現(xiàn)系統(tǒng)的可重用點(diǎn)與可擴(kuò)展點(diǎn),并遵循“高內(nèi)聚松耦合”原則對(duì)系統(tǒng)職責(zé)進(jìn)行合理分配,再輔以分層架構(gòu)劃分邏輯邊界,如下圖所示:

我們將識(shí)別出來(lái)的限界上下文定義為微服務(wù),并對(duì)外公開(kāi)REST服務(wù)接口。UI Applications是一個(gè)薄薄的展現(xiàn)層,它會(huì)調(diào)用后端的RESTful服務(wù),也使得服務(wù)在保證接口不變的前提下能夠單獨(dú)演化。每個(gè)服務(wù)都是獨(dú)立的,可以單獨(dú)部署,因而可以針對(duì)服務(wù)建立單獨(dú)的代碼庫(kù)和對(duì)應(yīng)的特性團(tuán)隊(duì)(Feature Team)。服務(wù)的重用性和可擴(kuò)展性也有了更好的保障,服務(wù)與UI之間的集成變得更簡(jiǎn)單,整個(gè)架構(gòu)更加清晰了。

3. 領(lǐng)域模型對(duì)領(lǐng)域知識(shí)的抽象

領(lǐng)域模型是對(duì)業(yè)務(wù)需求的一種抽象,表達(dá)了領(lǐng)域概念、領(lǐng)域規(guī)則以及領(lǐng)域概念之間的關(guān)系。一個(gè)好的領(lǐng)域模型是對(duì)統(tǒng)一語(yǔ)言的可視化表示,通過(guò)它可以減少需求溝通可能出現(xiàn)的歧義;通過(guò)提煉領(lǐng)域知識(shí),并運(yùn)用抽象的領(lǐng)域模型去表達(dá),就可以達(dá)到對(duì)領(lǐng)域邏輯的化繁為簡(jiǎn)。模型是封裝,實(shí)現(xiàn)了對(duì)業(yè)務(wù)細(xì)節(jié)的隱藏;模型是抽象,提取了領(lǐng)域知識(shí)的共同特征,保留了面對(duì)變化時(shí)能夠良好擴(kuò)展的可能性。

案例:項(xiàng)目管理系統(tǒng)的領(lǐng)域模型

我們開(kāi)發(fā)的項(xiàng)目管理系統(tǒng)需要支持多種軟件項(xiàng)目管理流程,例如瀑布、RUP、XP或者Scrum。這些項(xiàng)目管理流程是迥然不同的,如果需要各自提供不同的解決方案,就會(huì)使得系統(tǒng)的模型變得非常復(fù)雜,也可能引入許多不必要的重復(fù)。通過(guò)領(lǐng)域建模,我們可以對(duì)項(xiàng)目管理領(lǐng)域的知識(shí)進(jìn)行抽象,尋找具有共同特征的領(lǐng)域概念。這就需要分析各種項(xiàng)目管理流程的主要特征與表現(xiàn),才能從中提煉出領(lǐng)域模型。

瀑布式軟件開(kāi)發(fā)由需求、分析、設(shè)計(jì)、編碼、測(cè)試、驗(yàn)收六個(gè)階段構(gòu)成,每個(gè)階段都由不同的活動(dòng)構(gòu)成,這些活動(dòng)可能是設(shè)計(jì)或開(kāi)發(fā)任務(wù),也可能是召開(kāi)評(píng)審會(huì)。流程如下圖所示:

RUP清晰地劃分了四個(gè)階段:先啟階段、細(xì)化階段、構(gòu)造階段與交付階段。每個(gè)階段可以包含一到多個(gè)迭代,每個(gè)迭代有不同的工作,例如業(yè)務(wù)建模、分析設(shè)計(jì)、配置與變更管理等。RUP的流程如下圖所示:

XP作為一種敏捷方法,采用了迭代的增量式開(kāi)發(fā),提倡為客戶交付具有業(yè)務(wù)價(jià)值的可運(yùn)行軟件。在執(zhí)行交付計(jì)劃之前,XP要求團(tuán)隊(duì)對(duì)系統(tǒng)的架構(gòu)做一次預(yù)研(Architectual Spike,又被譯為架構(gòu)穿刺)。當(dāng)架構(gòu)的初始方案確定后,就可以進(jìn)入每次小版本的交付。每個(gè)小版本交付又被劃分為多個(gè)周期相同的迭代。在迭代過(guò)程中,要求執(zhí)行一些必須的活動(dòng),如編寫(xiě)用戶故事、故事點(diǎn)估算、驗(yàn)收測(cè)試等。XP的流程如下圖所示:

Scrum同樣是迭代的增量開(kāi)發(fā)過(guò)程。項(xiàng)目在開(kāi)始之初,需要在準(zhǔn)備階段確定系統(tǒng)愿景、梳理業(yè)務(wù)用例、確定產(chǎn)品待辦項(xiàng)(product backlog)、制定發(fā)布計(jì)劃以及組建團(tuán)隊(duì)。一旦在確定了產(chǎn)品待辦項(xiàng)以及發(fā)布計(jì)劃之后,就進(jìn)入sprint迭代階段。sprint迭代過(guò)程是一個(gè)固定時(shí)長(zhǎng)的項(xiàng)目過(guò)程,在這個(gè)過(guò)程中,整個(gè)團(tuán)隊(duì)需要召開(kāi)計(jì)劃會(huì)議、每日站會(huì)、評(píng)審會(huì)議和回顧會(huì)議。Scrum的流程如下圖所示:

不同的項(xiàng)目管理流程具有不同的業(yè)務(wù)概念。例如瀑布式開(kāi)發(fā)分為了六個(gè)階段,但卻沒(méi)有發(fā)布和迭代的概念。RUP沒(méi)有發(fā)布的概念,而Scrum又為迭代引入了sprint的概念。

不同的項(xiàng)目管理流程具有不同的業(yè)務(wù)規(guī)則。例如RUP的四個(gè)階段會(huì)包含多個(gè)迭代周期,每個(gè)迭代周期都需要完成對(duì)應(yīng)的工作,只是不同的工作在不同階段所占的比重不同。XP需要在進(jìn)入發(fā)布階段之前,進(jìn)行架構(gòu)預(yù)研,而在每次小版本發(fā)布之前,都需要進(jìn)行驗(yàn)收測(cè)試和客戶驗(yàn)收。Scrum的sprint是一個(gè)基本固定的流程,每個(gè)迭代召開(kāi)的四會(huì)(計(jì)劃會(huì)議、評(píng)審會(huì)議、回顧會(huì)議與每日站會(huì))都有明確的目標(biāo)。

領(lǐng)域建模就是要從這些紛繁復(fù)雜的領(lǐng)域邏輯中尋找到能夠表示項(xiàng)目管理領(lǐng)域的概念,并利用面向?qū)ο蠼7妒交蚱渌妒綄?duì)概念進(jìn)行抽象,并確定它們之間的關(guān)系。經(jīng)過(guò)對(duì)這些項(xiàng)目管理流程的分析,我們雖然發(fā)現(xiàn)在業(yè)務(wù)概念和規(guī)則上確有不同之處,但由于它們都?xì)w屬于軟件開(kāi)發(fā)領(lǐng)域,我們自然也能尋找到某些共同特征的蛛絲馬跡。

首先,從項(xiàng)目管理系統(tǒng)的角度看,無(wú)論針對(duì)何種項(xiàng)目管理流程,我們的主題需求是不變的,就是要為這些管理流程制定軟件開(kāi)發(fā)計(jì)劃(Plan)。不同之處在于,計(jì)劃可以由多個(gè)階段(Phase)組成,也可以由多個(gè)發(fā)布(Release)組成。一些項(xiàng)目管理流程沒(méi)有發(fā)布的概念,我們可以認(rèn)為是一個(gè)發(fā)布。那么,到底是發(fā)布包含了多個(gè)階段,還是階段包含了多個(gè)發(fā)布呢?我們發(fā)現(xiàn)在XP中,明顯地劃分了兩個(gè)階段:Architecture Spike與Release Planning,而發(fā)布只屬于Release Planning階段。因而從概念內(nèi)涵上,我們可以認(rèn)為是階段(Phase)包含了發(fā)布(Release)。每個(gè)發(fā)布又包含了一到多個(gè)迭代(Iteration),至于Scrum的sprint概念其實(shí)可以看做是迭代的一種特例。每個(gè)迭代可以開(kāi)展多種不同的活動(dòng)(Activity),這些活動(dòng)可以是整個(gè)團(tuán)隊(duì)參與的會(huì)議,也可以是部分成員或特定角色執(zhí)行的實(shí)踐。對(duì)于計(jì)劃而言,我們還需要跟蹤任務(wù)(Task)。與活動(dòng)不同,任務(wù)具有明確的計(jì)劃起止時(shí)間、實(shí)際起止時(shí)間、工作量、優(yōu)先級(jí)與承擔(dān)人。

于是,我們提煉出如下的統(tǒng)一領(lǐng)域模型:

為了項(xiàng)目管理者更加方便地制定項(xiàng)目計(jì)劃,產(chǎn)品經(jīng)理提出了計(jì)劃模板功能。當(dāng)管理者選擇對(duì)應(yīng)的項(xiàng)目管理生命周期類型后,系統(tǒng)會(huì)自動(dòng)創(chuàng)建滿足其規(guī)則的初始計(jì)劃?;谠撔枨?,我們更新了之前的領(lǐng)域模型:

在增加的領(lǐng)域模型中,LifeCycleSpecification是一個(gè)隱含的概念,遵循領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)提出的規(guī)格(Specification)模式,封裝了項(xiàng)目開(kāi)發(fā)生命周期的約束規(guī)則。

領(lǐng)域模型以可視化的方式清晰地表達(dá)了業(yè)務(wù)含義,我們可以根據(jù)這個(gè)模型來(lái)指導(dǎo)后面的程序設(shè)計(jì)與編碼實(shí)現(xiàn)。當(dāng)增加新的需求或者需求發(fā)生變化時(shí),我們能夠敏銳地捕捉到現(xiàn)有模型的不匹配之處,并對(duì)其進(jìn)行更新。領(lǐng)域模型傳遞了知識(shí),可以作為交流的載體,符合人們的心智模型,有利于讓開(kāi)發(fā)人員從紛繁復(fù)雜的業(yè)務(wù)中解脫出來(lái)。這是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)對(duì)于前述第三個(gè)問(wèn)題——控制業(yè)務(wù)復(fù)雜度的解答。

【本文為51CTO專欄作者“張逸”原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)聯(lián)系原作者】

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來(lái)源: 51CTO專欄
相關(guān)推薦

2018-12-18 10:11:37

軟件復(fù)雜度軟件系統(tǒng)軟件開(kāi)發(fā)

2024-04-25 08:33:25

算法時(shí)間復(fù)雜度空間復(fù)雜度

2021-09-17 10:44:50

算法復(fù)雜度空間

2024-04-16 08:19:40

架構(gòu)高可用消息隊(duì)列

2020-12-30 09:20:27

代碼

2015-10-13 09:43:43

復(fù)雜度核心

2017-12-02 18:53:27

滴滴出行業(yè)務(wù)中臺(tái)對(duì)策

2020-08-24 08:15:29

軟件互聯(lián)網(wǎng)分布式

2021-01-05 10:41:42

算法時(shí)間空間

2017-07-14 10:55:05

2009-07-09 10:45:16

C#基本概念復(fù)雜度遞歸與接口

2014-09-11 15:05:40

驅(qū)動(dòng)設(shè)計(jì)驅(qū)動(dòng)開(kāi)發(fā)

2019-11-18 12:41:35

算法Python計(jì)算復(fù)雜性理論

2022-02-22 10:11:01

系統(tǒng)軟件架構(gòu)

2021-10-15 09:43:12

希爾排序復(fù)雜度

2019-12-24 09:46:00

Linux設(shè)置密碼

2022-08-16 09:04:23

代碼圈圈復(fù)雜度節(jié)點(diǎn)

2020-02-06 13:59:48

javascript算法復(fù)雜度

2014-09-26 10:00:25

驅(qū)動(dòng)設(shè)計(jì)DDD領(lǐng)域

2012-06-21 09:24:05

軟件定義網(wǎng)絡(luò)SDN
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)