什么是技術(shù)細(xì)節(jié),業(yè)務(wù)邏輯?
業(yè)務(wù)人員和開發(fā)人員對(duì)需求的時(shí),常常會(huì)出現(xiàn)下面這段對(duì)話。
業(yè)務(wù)人員:我希望加一個(gè)用戶注冊(cè)的功能,用戶要填入自己的用戶電話,郵箱,還有頭像,我們保存圖片需要進(jìn)行大小縮放,以便符合我們的要求,同時(shí)電話,郵箱要有效。
開發(fā)人員1:好的,當(dāng)用戶注冊(cè)的時(shí)候,需要前端檢查電話號(hào)碼,郵箱是否正確。然后獲得頭像圖片,進(jìn)行圖片處理,最后存到MySQL中的user和account表中。
用戶注冊(cè)操作代碼實(shí)現(xiàn)如下
class UserService {
public void createUser(String username, String avatar, String phone, String email, String password){
if(password == null){
return;
}
if(password.contains("*")){
return;
}
...
String uploadUrl = getPicture(avatar)
User user = new User(username, phone, email, uploadedUrl);
userRepo.save(user);
}
private void getPicture(String avatar){
File avatarFile = processPicture(avatar)
// uploadFile 實(shí)現(xiàn)邏輯
...
}
// 圖片處理,使得圖片大小在一個(gè)規(guī)定的值
private File processPicture(String avatar){
//下載圖片
//判斷圖片大小是否符合標(biāo)準(zhǔn)
//當(dāng)圖片過大,進(jìn)行圖片裁剪
//當(dāng)圖片過小,進(jìn)行圖片放大
...
}
}
class User{
private String username;
private String phone;
private String email;
private String uploadedUrl;
private String avatar;
}
可以看到,上面的對(duì)話和代碼實(shí)現(xiàn),包含2類內(nèi)容。技術(shù)細(xì)節(jié)和業(yè)務(wù)邏輯。
- 技術(shù)細(xì)節(jié):它們和業(yè)務(wù)無關(guān),僅僅代表應(yīng)用中的一種技術(shù)能力。如,我的數(shù)據(jù)庫(kù)表如何設(shè)計(jì),驗(yàn)證誰(shuí)來做,是前后端分離還是不分離,數(shù)據(jù)是從前端來,還是從后端給。
- 業(yè)務(wù)邏輯:它們和業(yè)務(wù)相關(guān),代表應(yīng)用中的一種業(yè)務(wù)能力。如,填入電話,郵箱,頭像圖片,圖片大小的規(guī)定。
再看看UserService,主要實(shí)現(xiàn)注冊(cè)功能的類。processPicture方法中存在了處理圖片的邏輯(裁剪圖片,放大圖片等技術(shù)細(xì)節(jié)),也包含了業(yè)務(wù)邏輯(規(guī)定的圖片大?。?。當(dāng)我們想從UserService中了解業(yè)務(wù)邏輯時(shí),我們需要讀懂processPicture方法。我們需要花很長(zhǎng)的時(shí)間去理解processPicture方法中的技術(shù)細(xì)節(jié)是在做什么(如何做圖片裁剪,如何做放大圖片操作)。然而,過多的技術(shù)細(xì)節(jié)展現(xiàn)對(duì)于了解業(yè)務(wù)邏輯沒有任何幫助,反而會(huì)使人凌亂。使得代碼不那么清晰。
用戶注冊(cè)操作代碼修改
class UserService {
private int WIDTH_PIXEL = 1024;
private int HIGHT_PIXEL = 1024;
public void createUser(String username, String avatar, String phone, String email, String password){
if(password == null){
return;
}
if(password.contains("*")){
return;
}
...
String uploadedUrl = FileUtils.processPictureTo(avatar, WIDTH_PIXEL, HIGHT_PIXEL)
User user = new User(username, phone, email, uploadedUrl);
userRepo.save(user);
}
}
class User{
private String username;
private String phone;
private String email;
private String uploadedUrl;
private String avatar;
}
class FileUtil {
public String processPictureTo(String avatar,WIDTH_PIXEL, HIGHT_PIXEL){
//下載圖片
//判斷圖片大小是否符合標(biāo)準(zhǔn)
//當(dāng)圖片過大,進(jìn)行圖片裁剪
//當(dāng)圖片過小,進(jìn)行圖片放大
//上傳圖片
}
}
再看修改后,我們將技術(shù)細(xì)節(jié)封裝到了個(gè)新的類FileUtil中,同時(shí)將圖片縮放的大小這個(gè)業(yè)務(wù)邏輯放在UserService中。這樣,我們?cè)谧xUserService的時(shí)候,就可以更清晰的了解業(yè)務(wù)邏輯。
為什么要區(qū)分?
大腦的短期記憶無法一次容納約7個(gè)以上的記憶項(xiàng)目 —— 《金字塔原理》
當(dāng)一個(gè)開發(fā)人員去熟悉一個(gè)新模塊的時(shí)候,首先會(huì)從整體去了解這個(gè)新模塊提供的功能,然后再會(huì)去看每個(gè)功能具體的實(shí)現(xiàn)細(xì)節(jié)是什么。就像畫畫一樣,一開始要畫草圖去確定各個(gè)部分畫什么東西,然后再慢慢補(bǔ)充細(xì)節(jié)。所謂細(xì)節(jié)都是魔鬼,如果一開始就進(jìn)入細(xì)節(jié),就容易陷入到細(xì)節(jié)中去,很難看到全局并找到重點(diǎn)。
因此,從認(rèn)知的角度,我們其實(shí)一開始關(guān)注的是業(yè)務(wù)邏輯,當(dāng)了解到我們關(guān)心的業(yè)務(wù)邏輯后,才會(huì)去做技術(shù)細(xì)節(jié)的展開。因此,我們可以把業(yè)務(wù)邏輯的代碼和具體的技術(shù)實(shí)現(xiàn)進(jìn)行分離,這樣,可以幫助我們通過整體-細(xì)節(jié)的方式進(jìn)行學(xué)習(xí)。
如何區(qū)分業(yè)務(wù)邏輯和技術(shù)細(xì)節(jié)?
我們可以通過問問題的方式來區(qū)分現(xiàn)在做的這個(gè)事兒是業(yè)務(wù)邏輯還是技術(shù)細(xì)節(jié)。
- 這個(gè)事兒業(yè)務(wù)人員關(guān)心么?有業(yè)務(wù)價(jià)值么?
排序算法業(yè)務(wù)關(guān)心么?把數(shù)據(jù)存到關(guān)系型數(shù)據(jù)庫(kù)還是文檔型數(shù)據(jù)庫(kù)業(yè)務(wù)關(guān)心么?實(shí)時(shí)計(jì)算結(jié)果還是提前計(jì)算好結(jié)果保存下來,業(yè)務(wù)關(guān)心么?是先取A數(shù)據(jù)進(jìn)行計(jì)算還是先去B數(shù)據(jù)計(jì)算,業(yè)務(wù)關(guān)心么?有價(jià)值么?
如果答案是:Yes,那么這就是業(yè)務(wù)邏輯,如果答案是:No,那么這些可能就是技術(shù)細(xì)節(jié),需要封裝到別處去 - 這個(gè)事兒換一種方式可以達(dá)成么?
冒泡排序可以換成其他方式?MySQL存儲(chǔ)換一種方式可以達(dá)成么?實(shí)時(shí)計(jì)算結(jié)果換一種方式能達(dá)成么?
如果答案是:Yes,那么這就是技術(shù)細(xì)節(jié),如果答案是:No,那么就是業(yè)務(wù)邏輯
區(qū)分后該怎么辦?
分清了業(yè)務(wù)邏輯和技術(shù)細(xì)節(jié)后,我們需要把他們進(jìn)行分區(qū)。
將業(yè)務(wù)邏輯放到一個(gè)package中,將技術(shù)細(xì)節(jié)放到一個(gè)package中。
三層架構(gòu)
業(yè)務(wù)邏輯集中在service層中。業(yè)務(wù)無關(guān)的技術(shù)細(xì)節(jié)按照功能放到其他層。
- Controller
- TaskRequest
- TaskController
- TaskCreateRequest
- ProjectController
- ProjectResponse/ProjectVO
- Service
- Task
- TaskService
- Task
- Project
- ProjectService
- Project
- Picture
- Picture
- PictureService
- Repository
- Utils
- EdocUtils
- PicutreUtils
- Config
- LogConfig
- HealthCheckConfig
六邊形架構(gòu)
- adapter
- inbound(外部對(duì)本服務(wù)的訪問,UI,controller)
- rest
- resources
- TaskRequest
- rpc
- outbound(本服務(wù)訪問三方系統(tǒng),db,中間件,服務(wù))
- persist
- application(用例,用例可以調(diào)用多個(gè)domain里面的service)
- usecase
- TaskUseCase
- ProjectUseCase
- domain(業(yè)務(wù)模型)
- task
- Task
- TaskRepo
- project
- Project
- ProjectService
- config(配置信息)
- LogConfig
- HealthCheckConfig