1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        領(lǐng)域驅(qū)動設(shè)計(DDD)能給前端帶來什么

        共 6194字,需瀏覽 13分鐘

         ·

        2021-09-27 23:55

        感謝大家還沒取關(guān)我,畢竟這么久沒更新  最近在建設(shè)投資體系,這跟知識體系一樣是個系統(tǒng)工程,大家感興趣的話后續(xù)可以聊下這個話題。然后讀書系列會繼續(xù)進行下去~

        為什么需要DDD

            在回答這個問題之前,我們先看下大部分軟件都會經(jīng)歷的發(fā)展過程:頻繁的變更帶來軟件質(zhì)量的下降

        而這又是軟件發(fā)展的規(guī)律導(dǎo)致的:

        • 軟件是對真實世界的模擬,真實世界往往十分復(fù)雜

        • 人在認識真實世界的時候總有一個從簡單到復(fù)雜的過程

        • 因此需求的變更是一種必然,并且總是由簡單到復(fù)雜演變

        • 軟件初期的業(yè)務(wù)邏輯非常清晰明了,慢慢變得越來越復(fù)雜

            可以看到需求的不斷變更和迭代導(dǎo)致了項目變得越來越復(fù)雜,那么問題來了,項目復(fù)雜性提高的根本原因是需求變更引起的嗎?

            根本原因其實是因為在需求變更過程中沒有及時的進行解耦和擴展。


            那么在需求變更的過程中如何進行解耦和擴展呢?DDD發(fā)揮作用的時候來了。

        什么是DDD

        DDD(領(lǐng)域驅(qū)動設(shè)計)的概念見維基百科:

        https://zh.wikipedia.org/wiki/%E9%A0%98%E5%9F%9F%E9%A9%85%E5%8B%95%E8%A8%AD%E8%A8%88

            可以看到領(lǐng)域驅(qū)動設(shè)計(domin-driven design)不同于傳統(tǒng)的針對數(shù)據(jù)庫表結(jié)構(gòu)的設(shè)計,領(lǐng)域模型驅(qū)動設(shè)計自然是以提煉和轉(zhuǎn)換業(yè)務(wù)需求中的領(lǐng)域知識為設(shè)計的起點。在提煉領(lǐng)域知識時,沒有數(shù)據(jù)庫的概念,亦沒有服務(wù)的概念,一切圍繞著業(yè)務(wù)需求而來,即:

        • 現(xiàn)實世界有什么事物  ->  模型中就有什么對象

        • 現(xiàn)實世界有什么行為  ->  模型中就有什么方法

        • 現(xiàn)實世界有什么關(guān)系  -> 模型中就有什么關(guān)聯(lián)

            在DDD中按照什么樣的原則進行領(lǐng)域建模呢?

            單一職責(zé)原則(Single responsibility principle)即SRP:軟件系統(tǒng)中每個元素只完成自己職責(zé)內(nèi)的事,將其他的事交給別人去做。

            上面這句話有沒有什么哪里不清晰的?有,那就是“職責(zé)”兩個字。職責(zé)該怎么理解?如何限定該元素的職責(zé)范圍呢?這就引出了“限界上下文”的概念。

            Eric Evans 用細胞來形容限界上下文,因為“細胞之所以能夠存在,是因為細胞膜限定了什么在細胞內(nèi),什么在細胞外,并且確定了什么物質(zhì)可以通過細胞膜?!边@里,細胞代表上下文,而細胞膜代表了包裹上下文的邊界。

            我們需要根據(jù)業(yè)務(wù)相關(guān)性、耦合的強弱程度、分離的關(guān)注點對這些活動進行歸類,找到不同類別之間存在的邊界,這就是限界上下文的含義。上下文(Context)是業(yè)務(wù)目標,限界(Bounded)則是保護和隔離上下文的邊界,避免業(yè)務(wù)目標的不單一而帶來的混亂與概念的不一致。

        如何DDD

        DDD的大體流程如下:

        1. 建立統(tǒng)一語言

            統(tǒng)一語言是提煉領(lǐng)域知識的產(chǎn)出物,獲得統(tǒng)一語言就是需求分析的過程,也是團隊中各個角色就系統(tǒng)目標、范圍與具體功能達成一致的過程。

            使用統(tǒng)一語言可以幫助我們將參與討論的客戶、領(lǐng)域?qū)<遗c開發(fā)團隊拉到同一個維度空間進行討論,若沒有達成這種一致性,那就是雞同鴨講,毫無溝通效率,相反還可能造成誤解。因此,在溝通需求時,團隊中的每個人都應(yīng)使用統(tǒng)一語言進行交流。

            一旦確定了統(tǒng)一語言,無論是與領(lǐng)域?qū)<业挠懻?,還是最終的實現(xiàn)代碼,都可以通過使用相同的術(shù)語,清晰準確地定義領(lǐng)域知識。重要的是,當(dāng)我們建立了符合整個團隊皆認同的一套統(tǒng)一語言后,就可以在此基礎(chǔ)上尋找正確的領(lǐng)域概念,為建立領(lǐng)域模型提供重要參考。

            舉個例子,不同玩家對于英雄聯(lián)盟(league of legends)的稱呼不盡相同;國外玩家一般叫“League”,國內(nèi)玩家有的稱呼“擼啊擼”,有的稱呼“LOL”等等。那么如果要開發(fā)相關(guān)產(chǎn)品,開發(fā)人員和客戶首先需要統(tǒng)一對“英雄聯(lián)盟”的語言模型。

        1. 事件風(fēng)暴(Event Storming)

            事件風(fēng)暴會議是一種基于工作坊的實踐方法,它可以快速發(fā)現(xiàn)業(yè)務(wù)領(lǐng)域中正在發(fā)生的事件,指導(dǎo)領(lǐng)域建模及程序開發(fā)。它是Alberto Brandolini發(fā)明的一種領(lǐng)域驅(qū)動設(shè)計實踐方法,被廣泛應(yīng)用于業(yè)務(wù)流程建模和需求工程,基本思想是將軟件開發(fā)人員和領(lǐng)域?qū)<揖奂谝黄?,相互學(xué)習(xí),類似頭腦風(fēng)暴。

            會議一般以探討領(lǐng)域事件開始,從前向后梳理,以確保所有的領(lǐng)域事件都能被覆蓋。

            什么是領(lǐng)域事件呢?

            領(lǐng)域事件是領(lǐng)域模型中非常重要的一部分,用來表示領(lǐng)域中發(fā)生的事件。一個領(lǐng)域事件將導(dǎo)致進一步的業(yè)務(wù)操作,在實現(xiàn)業(yè)務(wù)解耦的同時,還有助于形成完整的業(yè)務(wù)閉環(huán)。

        領(lǐng)域事件可以是業(yè)務(wù)流程的一個步驟,比如投保業(yè)務(wù)繳費完成后,觸發(fā)投保單轉(zhuǎn)保單的動作;也可能是定時批處理過程中發(fā)生的事件,比如批處理生成季繳保費通知單,觸發(fā)發(fā)送繳費郵件通知操作;或者一個事件發(fā)生后觸發(fā)的后續(xù)動作,比如密碼連續(xù)輸錯三次,觸發(fā)鎖定賬戶的動作。

        1. 進行領(lǐng)域建模,將各個模型分配到各個限界上下文中,構(gòu)建上下文地圖。

            領(lǐng)域建模時,我們會根據(jù)場景分析過程中產(chǎn)生的領(lǐng)域?qū)ο?,比如命令、事件等之間關(guān)系,找出產(chǎn)生命令的實體,分析實體之間的依賴關(guān)系組成聚合,為聚合劃定限界上下文,建立領(lǐng)域模型以及模型之間的依賴。

            上面我們大體了解了DDD的作用,概念和一般的流程,雖然前端和后端的DDD不盡相同,但是我們?nèi)匀豢梢詫⑦@種思想應(yīng)用于我們的項目中。

        DDD能給前端項目帶來什么

        通過領(lǐng)域模型 (feature)組織項目結(jié)構(gòu),降低耦合度

            很多通過react腳手架生成的項目組織結(jié)構(gòu)是這樣的:

        -components  component1  component2  -actions.ts ...allActions
        -reducers.ts ...allReducers

            這種代碼組織方式,比如actions.ts 中的 actions 其實沒有功能邏輯關(guān)系;當(dāng)增加新的功能的時候,只是機械的往每個文件夾中加入對應(yīng)的component,action,reducer,而沒有關(guān)心他們功能上的關(guān)系。那么這種項目的演進方向就是:
        項目初期:規(guī)模小,模塊關(guān)系清晰  ---> 迭代期:加入新的功能和其他元素 ---> 項目收尾:文件結(jié)構(gòu),模塊依賴錯綜復(fù)雜。
        因此我們可以通過領(lǐng)域模型的方式來組織代碼,降低耦合度。
        1. 首先從功能角度對項目進行拆分。將業(yè)務(wù)邏輯拆分成高內(nèi)聚松耦合的模塊。從而對 feature 進行新增,重構(gòu),刪除,重命名等變得簡單 ,不會影響到其他的feature,使項目可擴展和可維護。

        2. 再從技術(shù)角度進行拆分,可以看到componet, routing,reducer 都來自等多個功能模塊
        可以看到:
        • 技術(shù)上的代碼按照功能的方式組織在feature下面,而不是單純通過技術(shù)角度進行區(qū)分。
        • 通常是由一個文件來管理所有的路由,隨著項目的迭代,這個路由文件也會變得復(fù)雜。那么可以把路由分散在feature中,由每個feature 來管理自己的路由。
        通過feature來組織代碼結(jié)構(gòu)的好處是:當(dāng)項目的功能越來越多時,整體復(fù)雜度不會指數(shù)級上升,而是始終保持在可控的范圍之內(nèi),保持可擴展,可維護。

        如何組織 componet,action,reducer

        文件夾結(jié)構(gòu)該如何設(shè)計?
        • 按feature組織組件,action 和 reducer
        • 組件和樣式文件在同一級
        • Redux放在單獨的文件
        1. 每個feature下面分為 redux文件夾 和 組件文件
        1. redux文件夾下面的 action.js 只是充當(dāng)loader的作用,負責(zé)將各個action引入,而沒有具體的邏輯。reducer 同理
        1. 項目的根節(jié)點還需要一個 root loader 來加載 feature 下的資源

        如何組織 router

        組織router的核心思想是把每個路由配置分發(fā)到每個 feature 自己的路由表中,那么需要:
        • 每個feature都有自己專屬的路由配置
        • 頂層路由(頁面級別的路由)通過JSON配置1,然后解析JSON到React Router
        1.每個feature有自己的路由配置
        2.頂層的routerConfig引入各個feature的子路由


        import { App } from '../features/home';import { PageNotFound } from '../features/common';import homeRoute from '../features/home/route';import commonRoute from '../features/common/route';import examplesRoute from '../features/examples/route';
        const childRoutes = [ homeRoute, commonRoute, examplesRoute,];
        const routes = [{ path'/', componet: App, childRoutes: [ ... childRoutes,path:'*'name'Page not found'component: PageNotFound }, ].filter( r => r.componet || (r.childRoutes && r.childRoutes.length > 0))  }]  export default routes
        3.解析JSON 路由到 React Router
        import React from 'react';import { Switch, Route } from 'react-router-dom';import { ConnectedRouter } from 'connected-react-router';import routeConfig from './common/routeConfig';
        function renderRouteConfig(routes, path) { const children = [] // children component list const renderRoute = (item, routeContextPath) => { let newContextPath; if (/^\//.test(item.path)) { newContextPath = item.path; } else { newContextPath = `${routeContextPath}/${item.path}`; } newContextPath = newContextPath.replace(/\/+/g, '/'); if (item.component && item.childRoutes) { const childRoutes = renderRouteConfigV3(item.childRoutes, newContextPath); children.push( <Route key={newContextPath} render={props => <item.component {...props}>{childRoutes}</item.component>} path={newContextPath} />, ); } else if (item.component) { children.push( <Route key={newContextPath} component={item.component} path={newContextPath} exact />, ); } else if (item.childRoutes) { item.childRoutes.forEach(r => renderRoute(r, newContextPath)); } }; routes.forEach(item => renderRoute(item,path)) return <Switch>children</Switch>}

        function Root() { const children = renderRouteConfig(routeConfig, '/'); return ( <ConnectedRouter>{children}</ConnectedRouter> );}



        瀏覽 45
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            六十路性爱视频 | beeg中国少妇呻吟 | 日日夜夜拍拍 | 在办公室伦流澡到高潮在线观看 | 日本视频一区二区三区 | ass大乳尤物肉体pics | 天天谢天天干 | 乐吧成人门户总站官网 | 囗交无遮挡电影 | AV福利在线 |