吸積效應(yīng):為什么接口會(huì)越來越臃腫?我們從一個(gè)接口說起
JAVA前線
歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場(chǎng)分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)
1.1 初始接口
假設(shè)現(xiàn)在有一個(gè)創(chuàng)建訂單接口:
public OrderCreateResultDTO createOrder(OrderCreateDTO order)
- 創(chuàng)建訂單對(duì)象:
public class OrderCreateDTO {
private String bizColumnA;
private String bizColumnB;
private String bizColumnC;
private String bizColumnD;
}
- 訂單響應(yīng)對(duì)象:
public class OrderCreateResultDTO {
private String orderId;
private String bizColumnA;
private String bizColumnB;
private String bizColumnC;
private String bizColumnD;
}
1.2 發(fā)生變化
現(xiàn)在業(yè)務(wù)發(fā)生變化:創(chuàng)建訂單時(shí)不需要C和D字段,新增E和F字段。如果你是開發(fā)人員,你會(huì)怎么設(shè)計(jì)這個(gè)接口?有一種比較樸素的思路是直接刪除C和D字段,新增E和F字段:
- 創(chuàng)建訂單對(duì)象:
public class OrderCreateDTO {
private String bizColumnA;
private String bizColumnB;
private String bizColumnE;
private String bizColumnF;
}
- 訂單響應(yīng)對(duì)象:
public class OrderCreateResultDTO {
private String orderId;
private String bizColumnA;
private String bizColumnB;
private String bizColumnE;
private String bizColumnF;
}
1.3 發(fā)現(xiàn)問題
我們想一想上述方案是否可行呢?答案是不行,有以下三方面原因:
原因一是接口契約性:接口是本服務(wù)暴露給外部使用的,相當(dāng)于上下游簽了合同,如果隨意修改合同,那么合同嚴(yán)肅性蕩然無存。
原因二是版本兼容性:我們知道APP是有版本號(hào)的,假設(shè)APP版本1使用的是初始接口,這時(shí)APP版本2由于新業(yè)務(wù)需要使用新接口,但是你不能直接把初始接口改的面目全非,因?yàn)锳PP版本1有很多用戶在用,如果只考慮新版本,那老版本將會(huì)報(bào)錯(cuò)。
原因三是時(shí)間窗口:我們知道APP發(fā)布版本是需要審核的,即使這一版本是強(qiáng)更,也會(huì)有一個(gè)時(shí)間窗口新功能和老功能是并存的,所以服務(wù)端接口在升級(jí)是必須考慮這種情況。
2 如何思考
既然不能直接升級(jí),那么應(yīng)該如何解決這個(gè)問題?我認(rèn)為需要從兩個(gè)維度思考:
- 分層維度
- 分端維度
2.1 分層思考
在代碼落地實(shí)踐中可以分為多層,但是核心還是三層,我們分別分析每一層如何適配接口變更:
- 數(shù)據(jù)層
- 業(yè)務(wù)層
- 表現(xiàn)層
2.1.1 數(shù)據(jù)層
因?yàn)橐紤]新老版本并存問題,所以數(shù)據(jù)層必須保留新老版本所有字段,標(biāo)記老字段標(biāo)記為廢棄,但是需要修改字段是否必填性,因?yàn)橐恍├习姹咀侄巫兂刹槐靥睿?/p>
public class OrderCreateDO {
private String orderId;
private String bizColumnA;
private String bizColumnB;
@Deprecated
private String bizColumnC;
@Deprecated
private String bizColumnD;
private String bizColumnE;
private String bizColumnF;
}
2.1.2 業(yè)務(wù)層
業(yè)務(wù)層是承載核心業(yè)務(wù)的層級(jí),所以包含大量的業(yè)務(wù)邏輯,有兩種方案:
- 方案一:新增業(yè)務(wù)對(duì)象,重寫業(yè)務(wù)方法
- 方案二:修改業(yè)務(wù)對(duì)象,適配業(yè)務(wù)方法
方案一優(yōu)點(diǎn)是不與老業(yè)務(wù)耦合,缺點(diǎn)是新老方法邏輯可能只有少部分不一樣,所以需要復(fù)制老業(yè)務(wù)方法大量邏輯到新方法中。
方法二優(yōu)點(diǎn)是可以復(fù)用老邏輯,缺點(diǎn)是新老邏輯耦合,如果適配邏輯沒有處理好,可能會(huì)影響老邏輯。
所以兩種方案有各自使用場(chǎng)景,方案一適用于業(yè)務(wù)邏輯重大變動(dòng)場(chǎng)景,既然是重構(gòu)所以可以重新聲明新業(yè)務(wù)對(duì)象:
public class OrderCreateNewBO {
private String bizColumnA;
private String bizColumnB;
private String bizColumnE;
private String bizColumnF;
}
方案二適用于業(yè)務(wù)邏輯微調(diào)變動(dòng)場(chǎng)景,所以老業(yè)務(wù)對(duì)象需要包含新老字段:
public class OrderCreateBO {
private String bizColumnA;
private String bizColumnB;
@Deprecated
private String bizColumnC;
@Deprecated
private String bizColumnD;
private String bizColumnE;
private String bizColumnF;
}
2.1.3 展示層
展示層不應(yīng)該處理復(fù)雜業(yè)務(wù)邏輯,而應(yīng)該是對(duì)業(yè)務(wù)對(duì)象的裁剪和適配,所以可以新增一個(gè)新版本接口,沒有業(yè)務(wù)層那種負(fù)擔(dān):
- 創(chuàng)建訂單對(duì)象:
public class OrderCreateDTOV2 {
private String bizColumnA;
private String bizColumnB;
private String bizColumnE;
private String bizColumnF;
}
- 訂單響應(yīng)對(duì)象:
public class OrderCreateResultDTOV2 {
private String orderId;
private String bizColumnA;
private String bizColumnB;
private String bizColumnE;
private String bizColumnF;
}
但是如果展示層不規(guī)范,包含大量業(yè)務(wù)邏輯,那么思考方式和業(yè)務(wù)層一樣,也需要使用方案二。
2.2 分端思考
一個(gè)系統(tǒng)在業(yè)務(wù)上通常分三個(gè)端:
- 面向B端用戶
- 面向C端用戶
- 面向運(yùn)營(yíng)用戶

三端都具有BFF層,但是通常實(shí)現(xiàn)技術(shù)不同:
- 面向B端和C端有APP端
- 面向運(yùn)營(yíng)端通常H5實(shí)現(xiàn)
所以如果不存在APP端,為了接口不變得越來越臃腫,所以面向運(yùn)營(yíng)用戶BFF層可以根據(jù)實(shí)際情況,考慮刪除老字段。
3 技術(shù)系統(tǒng)為什么復(fù)雜
在《為什么需要生物學(xué)思維》這本書中提到復(fù)雜系統(tǒng)形成四個(gè)原因:
- 吸積
- 交互
- 必須處理的意外情況
- 普遍的稀有事務(wù)
3.1 什么是吸積效應(yīng)
從字面上解讀可以將這一過程理解為吸附和積累。如同一粒粒沙子,每次加入一點(diǎn)點(diǎn),最終匯聚成一座沙山。在軟件開發(fā)的世界中,無論每一次的代碼迭代在表面上看起來多么獨(dú)立和微不足道,它們都在客觀上促使代碼量不斷增長(zhǎng)。
3.2 吸積效應(yīng)帶來哪些挑戰(zhàn)
吸積效應(yīng)導(dǎo)致代碼變得如此龐大和復(fù)雜,以至于沒有人能夠全面掌握這個(gè)系統(tǒng)。當(dāng)系統(tǒng)出現(xiàn)問題或故障時(shí),最熟悉這部分代碼的人可能早已離職,消失在人海中。
面對(duì)這種情況開發(fā)人員往往只能添加一段段兼容邏輯,以期降低對(duì)原有系統(tǒng)的影響。然而這種做法反而加劇吸積效應(yīng)。甚至在某些無奈的情況下,團(tuán)隊(duì)可能被迫選擇容忍某些錯(cuò)誤,因?yàn)樾迯?fù)這些錯(cuò)誤的代價(jià)遠(yuǎn)大于容忍它們所帶來的風(fēng)險(xiǎn)。這就是吸積效應(yīng)在軟件開發(fā)中帶來的巨大挑戰(zhàn)。
3.3 怎么應(yīng)對(duì)
技術(shù)系統(tǒng)都是往熵增方向發(fā)展,從有序發(fā)展到無序,所以工程師要通過一些手段減緩這個(gè)進(jìn)程,常見方案是:
- 統(tǒng)一技術(shù)架構(gòu)
- 統(tǒng)一技術(shù)規(guī)范
- 統(tǒng)一業(yè)務(wù)語言
- 代碼分享與審查
- 技術(shù)與業(yè)務(wù)分享
- 系統(tǒng)穩(wěn)定性建設(shè)
JAVA前線
歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場(chǎng)分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)
