<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          DDD 能給前端帶來了什么?

          共 5235字,需瀏覽 11分鐘

           ·

          2022-04-18 10:29

          大廠技術(shù)??高級前端??Node進(jìn)階

          點(diǎn)擊上方?程序員成長指北,關(guān)注公眾號

          回復(fù)1,加入高級Node交流群

          大家好,我是考拉??。為什么需要DDD(領(lǐng)域驅(qū)動(dòng)設(shè)計(jì))?

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

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

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

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

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

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

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

          ????根本原因其實(shí)是因?yàn)樵谛枨笞兏^程中沒有及時(shí)的進(jìn)行解耦和擴(kuò)展。


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

          什么是DDD

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

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

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

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

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

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

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

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

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

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

          如何DDD

          DDD的大體流程如下:

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

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

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

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

          ????舉個(gè)例子,不同玩家對于英雄聯(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)暴會(huì)議是一種基于工作坊的實(shí)踐方法,它可以快速發(fā)現(xiàn)業(yè)務(wù)領(lǐng)域中正在發(fā)生的事件,指導(dǎo)領(lǐng)域建模及程序開發(fā)。它是Alberto Brandolini發(fā)明的一種領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)踐方法,被廣泛應(yīng)用于業(yè)務(wù)流程建模和需求工程,基本思想是將軟件開發(fā)人員和領(lǐng)域?qū)<揖奂谝黄穑嗷W(xué)習(xí),類似頭腦風(fēng)暴。

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

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

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

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

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

          ????領(lǐng)域建模時(shí),我們會(huì)根據(jù)場景分析過程中產(chǎn)生的領(lǐng)域?qū)ο螅热缑睢⑹录戎g關(guān)系,找出產(chǎn)生命令的實(shí)體,分析實(shí)體之間的依賴關(guān)系組成聚合,為聚合劃定限界上下文,建立領(lǐng)域模型以及模型之間的依賴。

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

          DDD能給前端項(xiàng)目帶來什么

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

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

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

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

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

          如何組織 componet,action,reducer

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

          如何組織 router

          組織router的核心思想是把每個(gè)路由配置分發(fā)到每個(gè) feature 自己的路由表中,那么需要:
          • 每個(gè)feature都有自己專屬的路由配置
          • 頂層路由(頁面級別的路由)通過JSON配置1,然后解析JSON到React Router
          1.每個(gè)feature有自己的路由配置
          2.頂層的routerConfig引入各個(gè)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( key={newContextPath} render={props => {childRoutes}} path={newContextPath} />, ); } else if (item.component) { children.push( , ); } else if (item.childRoutes) { item.childRoutes.forEach(r => renderRoute(r, newContextPath)); } }; routes.forEach(item => renderRoute(item,path)) return <Switch>childrenSwitch>}

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


          Node 社群



          我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。



          如果你覺得這篇內(nèi)容對你有幫助,我想請你幫我2個(gè)小忙:

          1. 點(diǎn)個(gè)「在看」,讓更多人也能看到這篇文章
          2. 訂閱官方博客?www.inode.club?讓我們一起成長

          點(diǎn)贊和在看就是最大的支持??

          瀏覽 30
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本A∨在线观看 | 水蜜桃视频网站 | 色色色色色色色色色五月婷婷 | 美女性爱网| 久久一级精品视频 |