日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
“整潔架構(gòu)”和商家前端的重構(gòu)之路

?1. 背景

團(tuán)隊(duì)歸屬于后方業(yè)務(wù)支撐部門,組內(nèi)的項(xiàng)目都以 pc 中后臺(tái)應(yīng)用為主。對(duì)比移動(dòng)端應(yīng)用,代碼庫(kù)比較龐大,業(yè)務(wù)邏輯也相對(duì)復(fù)雜。在持續(xù)的迭代過(guò)程中,我們發(fā)現(xiàn)當(dāng)前的代碼倉(cāng)庫(kù)仍然有不少可以優(yōu)化的點(diǎn):

創(chuàng)新互聯(lián)專注于哈爾濱企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,商城網(wǎng)站制作。哈爾濱網(wǎng)站建設(shè)公司,為哈爾濱等地區(qū)提供建站服務(wù)。全流程定制網(wǎng)站開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

可以減弱對(duì) ui 框架的依賴

21 年前端平臺(tái)決定技術(shù)棧統(tǒng)一遷移到 React 生態(tài),后續(xù)平臺(tái)的基礎(chǔ)建設(shè)也都圍繞 React 展開,這就使得商家使用 Vue 生態(tài)做開發(fā)的系統(tǒng)面臨技術(shù)棧遷移的難題,將業(yè)務(wù)邏輯和 UI 框架節(jié)藕變得異常重要。

代碼風(fēng)格可以更加統(tǒng)一

隨著代碼量和團(tuán)隊(duì)成員的增加,應(yīng)用里風(fēng)格迥異的代碼也越來(lái)越多。為了能夠持續(xù)迅速的進(jìn)行迭代,團(tuán)隊(duì)急需一套統(tǒng)一的頂層代碼架構(gòu)設(shè)計(jì)方案。

可以集成自動(dòng)化測(cè)試用例

隨著業(yè)務(wù)變得越來(lái)越復(fù)雜,在迅速的迭代過(guò)程中團(tuán)隊(duì)需要頻繁地對(duì)功能進(jìn)行回歸,因此我們對(duì)于自動(dòng)化單測(cè)用例的訴求也變的越來(lái)越強(qiáng)烈。

為了完成以上的優(yōu)化,四組對(duì)現(xiàn)有的應(yīng)用架構(gòu)做了一次重構(gòu),而重構(gòu)的核心就是整潔架構(gòu)。

2. 整潔架構(gòu) (The Clean Architecture)

整潔架構(gòu) (The clean architecture) 是由 Robert C. Martin (Uncle Bob) 在 2012 年提出的一套代碼組織的理念,其核心主要是依據(jù)各部分代碼作用的不同將其拆分成不同的層次,在各層次間制定了明確的依賴原則,以達(dá)到以下目的:

  • 與框架無(wú)關(guān):無(wú)論是前端代碼還是服務(wù)端代碼,其邏輯本身都應(yīng)該是獨(dú)立的,不應(yīng)該依賴于某一個(gè)第三方框架或工具庫(kù)。一套獨(dú)立的代碼可以把第三方框架等作為工具使用。
  • 可測(cè)試:代碼中的業(yè)務(wù)邏輯可以在不依賴 ui、數(shù)據(jù)庫(kù)、服務(wù)器的情況下進(jìn)行測(cè)試
  • 和 ui 無(wú)關(guān):代碼中的業(yè)務(wù)邏輯不應(yīng)該和 ui 做強(qiáng)綁定。比如把一個(gè) web 應(yīng)用切換成桌面應(yīng)用,業(yè)務(wù)邏輯不應(yīng)該受到影響。
  • 和數(shù)據(jù)庫(kù)無(wú)關(guān):無(wú)論數(shù)據(jù)庫(kù)用的是 mysql 還是 mongodb,無(wú)論其怎么變,都不該影響到業(yè)務(wù)邏輯。
  • 和外部服務(wù)無(wú)關(guān):無(wú)論外部服務(wù)怎么變,都不影響到使用該服務(wù)的業(yè)務(wù)邏輯。

為了實(shí)現(xiàn)以上目的,整潔架構(gòu)把應(yīng)用劃分成了 entities、use cases、interface adapters (MVC、MVP 等)、Web/DB 等至少四層。這套架構(gòu)除了分層之外,在層與層之間還有一個(gè)非常明確的依賴關(guān)系,外層的邏輯依賴內(nèi)層的邏輯。

Entity

entities 封裝了企業(yè)級(jí)的業(yè)務(wù)邏輯和規(guī)則。entities 沒(méi)有什么固定的形式,無(wú)論是一個(gè)對(duì)象也好,是一堆函數(shù)的集合也好,唯一的標(biāo)準(zhǔn)就是能夠被企業(yè)的各個(gè)應(yīng)用所復(fù)用。

Use Case

entities 封裝了企業(yè)里最通用的一部分邏輯,而應(yīng)用各自的業(yè)務(wù)邏輯就都封裝在 use case 里面。日常開發(fā)中最常見的對(duì)于某個(gè)模型的 crud 操作就屬于 usecase 這一層。

Interface Adapter

這一層類似于膠水層,需要負(fù)責(zé)內(nèi)圈的 entity 和 use case 同外圈的 external interfaces 之間的數(shù)據(jù)轉(zhuǎn)化。需要把外層服務(wù)的數(shù)據(jù)轉(zhuǎn)化成內(nèi)層 entity 和 usecase 可以消費(fèi)的數(shù)據(jù),反之亦然。如上面圖上畫的,這一層有時(shí)候可能很簡(jiǎn)單 (一個(gè)轉(zhuǎn)化函數(shù)), 有時(shí)候可能復(fù)雜到包含一整個(gè) MVC/MVP 的架構(gòu)。

External Interfaces

我們需要依賴的外部服務(wù),第三方框架,以及需要糊的頁(yè)面 UI 都?xì)w屬在這一層。這一層完全不感知內(nèi)圈的任何邏輯,所以無(wú)論這一層怎么變 (ui 變化),都不應(yīng)該影響到內(nèi)圈的應(yīng)用層邏輯 (usecase) 和企業(yè)級(jí)邏輯 (entity)。

依賴原則

在整潔架構(gòu)的原始設(shè)計(jì)中,并不是強(qiáng)制一定只能寫這么四層,根據(jù)業(yè)務(wù)的需要還可以拆分的更細(xì)。不過(guò)無(wú)論怎么拆,都需要遵守前面提到的從外至內(nèi)的依賴原則。即 entity 作為企業(yè)級(jí)的通用邏輯,不能依賴任何模塊。而外層的 ui 等則可以使用 usecase、entity。

3. 重構(gòu)

前面介紹了當(dāng)前代碼庫(kù)目前的一些具體問(wèn)題,而整潔架構(gòu)的理念正好可以幫助我們優(yōu)化代碼可維護(hù)性。

作為前端,我們的業(yè)務(wù)邏輯不應(yīng)該依賴視圖層 (ui 框架及其生態(tài)),同時(shí)應(yīng)當(dāng)保證業(yè)務(wù)邏輯的獨(dú)立性和可復(fù)用性 (usecase & entity)。最后,作為數(shù)據(jù)驅(qū)動(dòng)的端應(yīng)用,要保證應(yīng)用視圖渲染和業(yè)務(wù)邏輯等不受數(shù)據(jù)變動(dòng)的影響 (adapter & entity)。

根據(jù)以上的思考,我們對(duì) “整潔架構(gòu)” 做了如下落地。

Entities

對(duì)于前端應(yīng)用來(lái)說(shuō),在 entity 層我們只需要將服務(wù)端的生數(shù)據(jù)做一層簡(jiǎn)單的抽象,生成一個(gè)貧血對(duì)象給后續(xù)的渲染和交互邏輯使用。

interface IRawOrder {
amount: number
barCode: string
orderNo: string
orderType: string
skuId: number
deliveryTime: number
orderTime: number
productImg: string
status: number
}

export default function buildMakeOrder({
formatTimestamp,
formatImageUrl,
}: {
formatTimestamp: (timestamp: number, format?: string) => string
formatImageUrl: (
image: string,
config?: { width: number; height: number },
) => string
}) {
return function makeOrder(raw?: IRawOrder) {
if (!raw || !raw.orderNo) {
Monitor.warn('臟數(shù)據(jù)')
return null;
}
return {
amount: raw.amount,
barCode: raw.barCode,
orderNo: raw.orderNo,
orderType: raw.orderType,
skuId: raw.skuId,
status: raw.status,
statusDescription: selectStatusDescription(raw.status),
deliveryTime: formatTimestamp(raw.deliveryTime),
orderTime: formatTimestamp(raw.orderTime),
productImg: formatImageUrl(raw.productImg),
}
}
}

function selectStatusDescription(status: number): string {
switch (status) {
case 0:
return '待支付'
case 1:
return '待發(fā)貨'
case 2:
return '待收貨'
case 3:
return '已完成'
default:
return ''
}
}

以上是商家后臺(tái)訂單模型的 entity 工廠函數(shù),工廠主要負(fù)責(zé)對(duì)服務(wù)端返回的生數(shù)據(jù)進(jìn)行加工處理,讓其滿足渲染層和邏輯層的要求。除了抽象數(shù)據(jù)之外,可以看到在 entity 工廠還對(duì)數(shù)據(jù)進(jìn)行了校驗(yàn),將臟數(shù)據(jù)、不符合預(yù)期的數(shù)據(jù)全部處理掉或者進(jìn)行兜底 (具體操作要看業(yè)務(wù)場(chǎng)景)。

有一點(diǎn)需要注意的是,在設(shè)計(jì) entity 的時(shí)候 (尤其是基礎(chǔ) entity) 需要考慮復(fù)用性。舉個(gè)例子,在上面 orderEntity 的基礎(chǔ)上,我們通過(guò)簡(jiǎn)單的組合就可以生成一個(gè)虛擬商品訂單 entity:

import { makeOrder } from '@/entities'

export default function buildMakeVirtualOrder() {
return function makeVirtualOrder(raw?: IRawPresaleOrder) {
const order = makeOrder(raw)

if(! order || !raw.virtualOrderType) {
Monitor.warn('臟數(shù)據(jù)')
return null
}

return {
...order,
virtualOrderType: raw.virtualOrderType,
virtualOrderDesc: selectVirtualOrderDesc(raw.virtualOrderType)
}
}
}

如此一來(lái),我們就通過(guò) entity 層達(dá)到了兩個(gè)目的:

  • 把前端的邏輯和服務(wù)端接口數(shù)據(jù)隔離開,無(wú)論服務(wù)端怎么變,前端后續(xù)的渲染、業(yè)務(wù)代碼不需要變,我們只需要變更 entitiy 工廠函數(shù);并且經(jīng)過(guò) entity 層處理過(guò)后,所有流入后續(xù)渲染 & 交互邏輯的數(shù)據(jù)都是可靠的;對(duì)于部分異常數(shù)據(jù),前端應(yīng)用可以第一時(shí)間發(fā)現(xiàn)并報(bào)警。
  • 通過(guò)對(duì)業(yè)務(wù)模型進(jìn)行抽象,實(shí)現(xiàn)了模塊間的組合、復(fù)用。另外,抽象出的 entity 對(duì)代碼的維護(hù)性也有非常大的幫助,開發(fā)者可以非常直觀的知道所使用的 entity 所包含的所有字段。

Usecase

usecase 這一層即是圍繞 entity 展開的一系列 crud 操作,以及為了頁(yè)面渲染做的一些聯(lián)動(dòng) (通過(guò) ui store 實(shí)現(xiàn))。由于當(dāng)前架構(gòu)的原因 (沒(méi)有 bff 層),usecase 還可能承擔(dān)部分微服務(wù)串聯(lián)的工作。

舉個(gè)例子,商家后臺(tái)訂單頁(yè)面在渲染前有一堆準(zhǔn)備邏輯:

  • 根據(jù) route 的 query 參數(shù)以及一些商家類型參數(shù)來(lái)決定默認(rèn)選中哪個(gè) tab
  • 根據(jù)是國(guó)內(nèi)商家還是境外商家,調(diào)用對(duì)應(yīng)的供應(yīng)商接口來(lái)更新供應(yīng)商下拉框

現(xiàn)在大致的實(shí)現(xiàn)是:

{
mounted() {
const { subType } = this.$route.query
/*
7-15行處理了幾種分支鏈路場(chǎng)景下對(duì)subType的賦值問(wèn)題
*/
if (Number(subType) === 0 || subType) {
this.subType = subType.toString()
} else {
if (this.user.merchant.typeId === 4) {
this.subType = this.tabType.cross
} else {
this.subType = this.tabType.ordinarySpot
}
}

/*
getAllLogisticsCarrier有沒(méi)有對(duì)subType賦值呢?光看這段代碼完全不確定
*/
this.getAllLogisticsCarrier()
/*
21-22行又多出來(lái)一個(gè)分支需要對(duì)subType進(jìn)行再次賦值
*/
if (this.isPersonPermission && !this.crossUser) {
this.subType = this.tabType.warehouse
}
},

getAllLogisticsCarrier() {
let getCarrier = API.getAllLogisticsCarrier
if (this.crossUser) {
getCarrier = API.getOrderShipAllLogistics
}

getCarrier({}).then(res => {
if (res.code === 200) {
const options = []

.......... // 給options賦值

this.options2 = options

}
})
},
}

我們能看到 7-15、24-125 行對(duì) this.subType 進(jìn)行了賦值。但由于我們無(wú)法確定 20 行的函數(shù)是否也對(duì) this.subType 進(jìn)行了賦值,所以光憑 mounted 函數(shù)的代碼我們并不能完全確定 subType 的值究竟是什么,需要跳轉(zhuǎn)到 getAllLogisticsCarrier 函數(shù)確認(rèn)。這段代碼在這里已經(jīng)做了簡(jiǎn)化,實(shí)際的代碼像 getAllLogisticsCarrier 這樣的調(diào)用還有好幾個(gè),要想搞清楚邏輯就得把所有函數(shù)全看一遍,代碼的可讀性一般。同時(shí),由于函數(shù)都封裝在 ui 組件里,因此要想給函數(shù)覆蓋單測(cè)的話也需要一些改造。

為了解決問(wèn)題,我們將這部分邏輯都拆分到 usecase 層:

// prepare-order-page.ts
import { tabType } from '@/constants'

interface IParams {
subType?: number
merchantType: number
isCrossUser: boolean
isPersonPermission: boolean
}

/*
做依賴倒置主要是為了方便后續(xù)的單測(cè)和復(fù)用
*/
export default function buildPrepareOrderPage({
queryLogisticsCarriers,
}: {
queryLogisticsCarriers: () => Promise<{ carriers: ICarrires }>
}) {
return async function prepareOrderPage(params: IParams) {
const activeTab = selectActiveTab(params)

const { carriers } = queryLogisticsCarriers(params.isCrossUser)

return {
activeTab,
carriers,
}
}
}

function selectActiveTab({
subType,
isCrossUser,
isPersonPermission,
merchantType,
}: IParams) {
if (isPersonPermission && !isCrossUser) {
return tabType.warehouse
}

if (Number(subType) === 0 || subType) {
return subType.toString()
}

if (merchantType === 4) {
return tabType.cross
}

return tabType.ordinarySpot
}
// query-logistics-carriers
export default function buildQueryLogisticsCarriers({
fetchAllLogisticsCarrier,
fetchOrderShipAllLogistics,
}: {
fetchAllLogisticsCarrier: () => Promise<{ data: {carriers: ICarrires }}>
fetchOrderShipAllLogistics: () => Promise<{ data: {carriers: ICarrires }}>
}) {
return async function queryLogisticsCarriers(isCrossUser: boolean) {
if (isCrossUser) {
return fetchAllLogisticsCarrier()
}

return fetchOrderShipAllLogistics()
}
}

// index.vue
{
mounted() {
const {activeTab, carriers} = prepareOrderPage(params)

this.subType = activeTab;
this.options = buildCarrierOptions(carriers) // 將carries轉(zhuǎn)換成下拉框option
}
}

首先,可以看到所有 usecase 一定是一個(gè)純函數(shù),不會(huì)存在副作用的問(wèn)題。

其次,prepareOrderPage usecase 專門為訂單頁(yè)定制,拆分后一眼就能看出來(lái)訂單頁(yè)的準(zhǔn)備工作需要干決定選中的 tab 和拉取供應(yīng)商列表兩件事情。而另一個(gè)拆分出來(lái)的 queryLogisticsCarriers 則是封裝了商家后臺(tái)跨境、國(guó)內(nèi)兩種邏輯,后續(xù)無(wú)論跨境還是國(guó)內(nèi)的邏輯如何變更,其影響范圍被限制在了 queryLogisticsCarriers 函數(shù),我們需要對(duì)其進(jìn)行功能回歸;而對(duì)于 prepareOrderPage 來(lái)說(shuō),queryLogisticsCarriers 只是 () => Promise<{ carriers: ICarrires }> 的一個(gè)實(shí)現(xiàn)而已,其內(nèi)部調(diào)用 queryLogisticsCarriers 的邏輯完全不受影響,不需要進(jìn)行回歸。

最后,而由于我們做了依賴倒置,我們可以非常容易的給 usecase 覆蓋單測(cè):

import buildPrepareOrderPage from '@/utils/create-goods';

function init() {
const queryLogisticsCarriers = jest.fn();

const prepareOrderPage = buildPrepareOrderPage({ queryLogisticsCarriers });

return {
prepareOrderPage,
queryLogisticsCarriers,
};
}

describe('訂單頁(yè)準(zhǔn)備邏輯', () => {
it('當(dāng)用戶是國(guó)內(nèi)商家且在入倉(cāng)白名單上,在打開訂單頁(yè)時(shí),默認(rèn)打開入倉(cāng)tab', async () => {
const { prepareOrderPage } = init();
const params = {
merchantType: 2
isCrossUser: false
isPersonPermission: true
}

const { activeTab } = await prepareOrderPage(params)

expect(activeTab).toEqual({tabType.warehouse});
});

it('當(dāng)用戶是跨境商家,在打開訂單頁(yè)時(shí),默認(rèn)打開跨境tab', async () => {
const { prepareOrderPage } = init();
const params = {
merchantType: 4
isCrossUser: true
isPersonPermission: true
}

const { activeTab } = await prepareOrderPage(params)

expect(activeTab).toEqual({tabType.cross});
});

......
});

單測(cè)除了進(jìn)行功能回歸之外,它的描述 (demo 里使用了 Given-When-Then 的格式,由于篇幅的原因,關(guān)于單測(cè)的細(xì)節(jié)在后續(xù)的文章再進(jìn)行介紹) 對(duì)于了解代碼的邏輯非常非常非常有幫助。由于單測(cè)和代碼邏輯強(qiáng)行綁定的緣故,我們甚至可以將單測(cè)描述當(dāng)成一份實(shí)時(shí)更新的業(yè)務(wù)文檔。

除了方便寫單測(cè)之外,在通過(guò) usecase 拆分完成之后,ui 組件真正成為了只負(fù)責(zé) “ui” 和監(jiān)聽用戶交互行為的組件,這為我們后續(xù)的 React 技術(shù)棧遷移奠定了基礎(chǔ);通過(guò) usecase 我們也實(shí)現(xiàn)了很不錯(cuò)的模塊化,對(duì)于使用比較多的一些 entity,他的 crud 操作可以通過(guò)獨(dú)立的 usecase 具備了在多個(gè)頁(yè)面甚至應(yīng)用間復(fù)用的能力。

Adapter

上面 usecase 例子中的 fetchAllLogisticsCarrier 就是一個(gè) adapter,這一層起到的作用是將外部系統(tǒng)返回的數(shù)據(jù)轉(zhuǎn)化成 entity,并以一種統(tǒng)一的數(shù)據(jù)格式返回回來(lái)。

這一層很核心的一點(diǎn)即是可以依賴 entity 的工廠函數(shù),將接口返回的數(shù)據(jù)轉(zhuǎn)化成前端自己設(shè)計(jì)的模型數(shù)據(jù),保證流入 usecase 和 ui 層的數(shù)據(jù)都是經(jīng)過(guò)處理的 “干凈數(shù)據(jù)”。除此之外,通常在這一層我們會(huì)用一種固定的數(shù)據(jù)格式返回?cái)?shù)據(jù),比如例子中的 {success: boolean, data?: any}。這樣做主要是為了抹平對(duì)接多個(gè)系統(tǒng)帶來(lái)的差異性,同時(shí)減少多人協(xié)作時(shí)的溝通成本。


type Request = (url: string, params: Record) => Promise;
import makeCarrier from '@/entities/makeCarrier'


export default function buildFetchAllLogisticsCarrier({request}: {request: Request}) {
return async function fetchAllLogisticsCarrier() {
// TODO: 異常處理
const response = await request('/fakeapi', info)

if (!response || !resposne.code === 200) {
return {
success: false
}
}

return {
success: true,
data: {
carriers: response.list?.map(makeCarrier)
}
}
}
}

通過(guò) Adapter + entity 的組合,我們基本形成了前端應(yīng)用和后端服務(wù)之間的防腐層,使得前端可以在完全不清楚接口定義的情況下完成 ui 渲染、usecase 等邏輯的開發(fā)。在服務(wù)端產(chǎn)出定義后,前端只需要將實(shí)際接口返回適配到自己定義的模型 (通過(guò) entity) 即可。這一點(diǎn)對(duì)前端的測(cè)試周提效非常非常非常重要,因?yàn)榉栏瘜拥拇嬖?,我們可以在測(cè)試周完成需求評(píng)審之后根據(jù) prd 的內(nèi)容設(shè)計(jì)出業(yè)務(wù)模型,并以此完成需求開發(fā),在真正進(jìn)入研發(fā)周后只需要和服務(wù)端對(duì)接完成 adapter 這一層的適配即可。

在實(shí)踐過(guò)程中,我們發(fā)現(xiàn)在對(duì)接同一個(gè)系統(tǒng)的時(shí)候 (對(duì)商家來(lái)說(shuō)就是 stark 服務(wù)) 各個(gè) adapter 對(duì)于異常的處理幾乎一模一樣 (上述的 11-15 行),我們可以通過(guò) Proxy 對(duì)其進(jìn)行抽離實(shí)現(xiàn)復(fù)用。當(dāng)然,后續(xù)我們也完全有機(jī)會(huì)根據(jù)接口定義來(lái)自動(dòng)生成 adapter。

UI

在經(jīng)過(guò)前面的拆分之后,無(wú)論咱們的 UI 層用 React 還是 Vue 來(lái)寫,要做的工作都很簡(jiǎn)單了:

  • 監(jiān)聽交互事件并調(diào)用對(duì)應(yīng)的 usecase 來(lái)進(jìn)行響應(yīng)
  • 通過(guò) usecase 來(lái)獲取 entity 數(shù)據(jù)進(jìn)行渲染

由于 entity 已經(jīng)做了過(guò)濾和適配處理,所以在 ui 層我們可以放心大膽的用,不需要再寫一堆莫名其妙的判斷邏輯。另外由于 entity 是由前端自己定義的模型,無(wú)論開發(fā)過(guò)程中服務(wù)端接口怎么變,受影響的都只有 entity 工廠函數(shù),ui 層不會(huì)受到影響。

最后,在 ui 層我們還剩下令人頭痛的技術(shù)棧遷移問(wèn)題。整個(gè)團(tuán)隊(duì)目前使用 vue 的項(xiàng)目有 10 個(gè),按迭代頻率和項(xiàng)目規(guī)模遷移的方案可以分為兩類:

  • 迭代頻繁的大應(yīng)用:主要包括代碼行數(shù)較多、邏輯較為復(fù)雜的幾個(gè)中大型應(yīng)用。這些應(yīng)用想要一把梭直接完成遷移成本極高,但同時(shí)每個(gè)迭代又有相當(dāng)?shù)男枨??;谶@種情況,對(duì)于這三個(gè)應(yīng)用我們采取了微前端的方式進(jìn)行遷移。每個(gè)應(yīng)用分別起一個(gè)對(duì)應(yīng)的 React 應(yīng)用,對(duì)于新頁(yè)面以及部分邏輯已經(jīng)完全和 ui 解藕遷移成本不高的業(yè)務(wù),都由 React 應(yīng)用來(lái)承接,最后通過(guò) module federation 的方式實(shí)現(xiàn)融合。
  • 迭代不頻繁的小應(yīng)用:剩下的應(yīng)用均是復(fù)雜度不高的小應(yīng)用,這部分應(yīng)用迭代的需求不多,以維護(hù)為主。因此我們的方案是對(duì)現(xiàn)有邏輯進(jìn)行整潔架構(gòu)重構(gòu),在 ui 和邏輯分層之后直接對(duì) ui 層進(jìn)行替換完成遷移。

4. 后續(xù)

通過(guò)整潔架構(gòu)我們形成了統(tǒng)一的編碼規(guī)范,在前端應(yīng)用標(biāo)準(zhǔn)化的道路上邁下了堅(jiān)實(shí)的一步??梢灶A(yù)見的是整個(gè)標(biāo)準(zhǔn)化的過(guò)程會(huì)非常漫長(zhǎng),我們會(huì)陸續(xù)往標(biāo)準(zhǔn)中增加新的規(guī)范使其更加完善,短期內(nèi)在規(guī)劃中的有:

  • 單測(cè)即文檔:上面提到了 usecase 通過(guò)依賴倒置來(lái)配合單測(cè)落地,后續(xù)團(tuán)隊(duì)期望將一些業(yè)務(wù)邏輯的實(shí)現(xiàn)細(xì)則通過(guò)單測(cè)的描述來(lái)進(jìn)行沉淀,解決業(yè)務(wù)文檔實(shí)時(shí)性的問(wèn)題。
  • 完善監(jiān)控體系:前端常遇到的 3 種異常包括 代碼邏輯異常、性能瓶頸 (渲染卡頓、內(nèi)存不足等)、數(shù)據(jù)導(dǎo)致異常。對(duì)于數(shù)據(jù)異常,我們可以在 entity 層映射的過(guò)程中加入對(duì)異常數(shù)據(jù)的埋點(diǎn)上報(bào)來(lái)填補(bǔ)目前監(jiān)控的空白。(代碼邏輯異常通過(guò) sentry 已經(jīng)監(jiān)控,性能監(jiān)控對(duì)于中后臺(tái)應(yīng)用不需要)?

本文名稱:“整潔架構(gòu)”和商家前端的重構(gòu)之路
文章URL:http://www.5511xx.com/article/djhdjhc.html