原文地址: https://juejin.im/post/6845166891670093838
劃了半年,現在開始接客!
?本篇文章存在大量干貨,建議調整姿勢反復觀看,所有技術堆疊通用,本文以vue專案為例?
「好代碼一定是設計出來的!而不是用多么牛逼的技術堆疊」
DDD
注意這不是大笑表情包,DDD(domain-driven design-領域驅動設計),大部分前端接到需求的時候都在思考這個原型我要怎么實作某塊功能細節(用哪個UI庫、該怎么寫),即使不了解業務也一樣可以開發,通常也能完成作業,這種情況稱為 ——「面向功能編程(沒有思考的前端資源)」,
然而,隨著業務的深入、需求不斷地迭代和變更,我們(前端)仿佛在負重前行,為什么?
- 業務不熟悉,不知道為什么要改需求,也不知道修改需求影響了原來哪塊代碼
- 歷史包袱多,投入相較后端越來越重
領域怎么劃分?
[data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1030" height="284"></svg>](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1030" height="284"></svg>)
差不多就是這樣抽象(就硬劃),建議和后端同學進行 「深入♂♂探討」(抽象能力個人認為需要學習思考+后天培養,也不能指望文章)
?我認為應該在了解產品(或行業領域)的前提下進行軟體開發,先根據專案抽象出業務邊界模塊,建立領域再動手開發,?
沙箱
[data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="570" height="374"></svg>](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="570" height="374"></svg>)
?哎呀,你這個代碼怎么引數亂七八糟,方法全依賴在一起我都不敢改啊,怎么一個js有9000行?
我們先來康康什么是沙箱(sandbox),安全獨立的,隔離外界的,互不影響的環境結構,
看起來好像沒什么,我們再看看設計模式應該準許你的準則,
- 單一原責
- 迪米特(最少知識)原則
- 開放封閉原則
- 依賴倒置
- 介面隔離
- 里式替換
是不是覺得DDD和設計原則、沙箱環境都有相同想強調的地方?
好像又繞回來了,嘮了半天,盡和我搞文縐縐的文字游戲,是你需要30萬還是想爬山🧗了?
不是這樣的,如果
「「希望洗掉和新增一塊代碼(領域),并不影響原有代碼,并且不需要改動也不會報錯,怎么做?」」
接下來教大家如何運用在你的前端工程中,
結構
普通工程的目錄結構
[data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="552" height="490"></svg>](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="552" height="490"></svg>)
很傳統的目錄結構,包括vue-cli3也是這么做的,按照功能職責劃分,想修改路由前往routes檔案夾里修改,修改vuex去store里找,
[data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="500"></svg>](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="500"></svg>)
按DDD劃分的目錄結構(非戰斗人員請迅速撤離)
ps: 這里的領域檔案夾名不大對,當作錯誤示范將就看了
[data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="588" height="658"></svg>](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="588" height="658"></svg>)
貼一份新目錄結構的說明
[data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="876" height="816"></svg>](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="876" height="816"></svg>)
優點
為什么要這樣做?
職責
假設一個專案多人開發,拆分需求一定是按照某塊業務功能劃分的(分配作業),這就是我們在無意識中利用DDD的思想拆分產品業務,
- 清晰定位了每個同事的作業職責和邊界
- 出現問題方便定位到問題的范圍
- 減少代碼的沖突
效能
- 維護成本低
- 可復用某塊業務領域
- 可拓展(在統一的模式下做自動化、基建等)
缺點
- 代碼風格和編程思路的轉換
- 老專案的兼容 (在歷史債上轉換)
領域內職責&細節
?因害怕篇幅過長(懶),所以本文使用了大量偽代碼(意識流裝逼)?
資料訪問物件 DAO (Data Access Object)
static async getXxxList(api, payload) {
const res = await api(payload);
const { results, totalCount } = res.data.data;
return { data: results, total: totalCount };
}
復制代碼
沒什么好說的,處理領域內的介面,api 介面都放在這里,如果要處理資料建議也在這里完成,做到視圖與資料分離,不要在view里再做資料的封裝,
如果處理非常復雜建議再分層DTO(Data Transfer Object),把邏輯放在這里面,
?當你發現有介面重復的時候建議復寫,一般不會超過3個如果超過了思考是否領域拆分有問題?
引數/列舉 Model
里面一般有倆個檔案夾:
列舉 Enums
一些selecet的內容
[
["發布成功", 1],
["未發布", 2],
["等待中", 3],
["發布失敗", 4]
....
]
復制代碼
對應表物件 Vo
放一些對應表結構的資料,包括需要默認的內容、引數
{
// 團隊 Number teamId = null;
// 應用 String appName = null;
// 申請人 Enums(Role) role = 1;
// 開始時間 dateStr beginTime = null;
// 結束時間 dateStr endTime = null;
}
復制代碼
路由 Router
注意,通常我們都是寫在最外層一個統一的router檔案夾來注冊匯總,
「但是在領域里需要自己的路由,根據領域的實作和業務邏輯來規劃細分路由,」
視圖 View
類似路由的理念,可以在同一領域下拆分為多個視圖,畢竟有些龐大的業務需要很多頁面才能支撐起來他的流轉,
中臺系統的某個領域內可能需要2-3套crud才能支撐起來,那其中的View檔案夾下可能就要放2-3套對應的crud檔案夾,檔案夾內部才是視圖的結構(搜索表單、表格、詳情等按需細分),
Vuex
正常用法,領域內不同單元之間需要流通資料可用,不建議跨領域使用
領域注冊 Main.js
[data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1280" height="641"></svg>](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1280" height="641"></svg>)
這里考慮到每個領域需要賦予的能力都不一樣,所以需要針對單個領域模塊提供可選松耦合的能力,
有點發布訂閱模式內味了,注冊了什么東東?先上一段神奇的代碼
import routes from "./router";
export { routes };
// 領域模塊名稱const MODULE_NAME = "Doctor";
// 注冊模塊能力export default ({
registerRouter,
registerStore,
registerApi,
.......
}) => {
// 使用模塊能力 registerRouter(MODULE_NAME, routes);
registerStore(MODULE_NAME, store);
};
復制代碼
看到了很多register開頭的引數這些是自定義的能力,從能力中心拿出注冊,
「能力分析」
能力的訂(相當于注冊能力的名單封裝),在最后會用到這里
- registerRouter 把路由注冊到Doctor模塊下
Doctor: {
path: "/doctor/",
name: "doctor",
redirect: "/doctor/goodDoctor",
component: () => import(/* webpackChunkName: "goodDoctor" */ "../view/Main"),
children: [
{
path: "/doctor/goodDoctor",
name: "goodDoctor",
component: () =>
import(/* webpackChunkName: "goodDoctor" */ "../view/goodDoctor/Index")
},
{
path: "/doctor/badDoctor",
name: "badDoctor",
component: () =>
import(/* webpackChunkName: "badDoctor" */ "../view/badDoctor/Index")
},
]
}
復制代碼
- registerStore 把doctor的store注冊到vuex里
Doctor: {
goodDoctor:{
namespaced: true,
state,
getters,
mutations,
actions
},
badDoctor:{
namespaced: true,
state,
getters,
mutations,
actions
}
}
復制代碼
還有很多就不細說了
「擴展」
- 針對單個領域的埋點
- 針對單個領域的性能監控
- 針對單個領域的錯誤監控
還有很多遐想(瞎想)空間......
中心化-領域匯總 Module.js
問:整了那么多領域,怎么讓他實體化把專案跑起來?
答:將領域匯總掛載到vue上
// 獲取src下的領域import 領域1 from "@/領域1/main";
import 領域2 from "@/領域2/main";
——————————————————————————————————————
/*
也可以用
require.context('@', false, /\\main.js/)
匹配所有src下領域內的main檔案注冊
*/
const modules = [領域1,領域2,領域3....];
new Center(modules).mount("#app");
復制代碼
這個中心函式Center到底做了什么了什么?
import Vue from "vue";
import App from "../App";
class Center {
constructor(modules) {
// modulesCenter做了層遍歷,注冊的所有內容放進函式以便獲取 modulesCenter(modules);
Center.store = this.store = modulesCenter.createStore();
Center.router = this.router = modulesCenter.createRouter();
Center.vue = this.vue = new Vue({
store: this.store,
router: this.router,
render: h => h(App)
});
}
mount(mountEl) {
this.vue.$mount(mountEl);
return (Center.currentCenter = this);
}
}
export default Center;
復制代碼
大致做了什么,估計你們也猜到了
- createRouter
const router = new VueRouter({
mode: "history",
...modules.router
});
復制代碼
- createStore
const store = new Vuex.Store({
...modules.store
});
復制代碼
共享領域資料
說白了就是跨領域呼叫,其實都在本地了,代碼和呼叫vuex modules是一毛一樣的
computed: {
//都在 Doctor 內 ...mapAction('Doctor',[
'/goodDoctor',
'/badDoctor',
])
}
復制代碼
將來的composition Api 可能會更加輕哦,
?當然是不建議有耦合資料的,如果出現跨領域了依賴,說明拆分有問題,反推后端這里是否可以進行改變,防患于未然,面向未來的需求提前編程,?
基本能力講完了是不是覺得這么做的意義和消耗的時間不成正比? 讓我們把能利再升華一下,看看如何擴展成架構反哺業務......
[data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="400" height="400"></svg>](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="400" height="400"></svg>)
擴展基建中臺
- 代碼統一規范
- 領域代碼復用(也能跨專案)
- 提升效能,結合webpack5+vite可以只coding個別領域
- 搭建微前端體系前的技術籌備統一
領域gui化
https://user-gold-cdn.xitu.io/2020/7/2/1730fb84f701ebd5?imageView2/0/w/1280/h/960/format/webp/ignore-error/1
使用某個領域
直接拖拽,low code或者微服務都可以,不限于輸出形式
https://user-gold-cdn.xitu.io/2020/7/2/1730fb4213072566?imageView2/0/w/1280/h/960/format/webp/ignore-error/1
配套設施
需要管理后臺進行收集與輸出,其它配套設施一大堆就不過多描述了
總結(人話時間)
- 思考業務
- 可插拔
- 提效
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/249053.html
標籤:其他
上一篇:Go常見并發模式
下一篇:java 支付寶支付
