什麼是技術細節,業務邏輯?
業務人員和開發人員對需求的時,常常會出現下面這段對話。
業務人員:我希望加一個用戶註冊的功能,用戶要填入自己的用戶電話,郵箱,還有頭像,我們保存圖片需要進行大小縮放,以便符合我們的要求,同時電話,郵箱要有效,還要加短信驗證碼。
開發人員1:好的,短信驗證碼我們需要寫一個發短信的服務,然後,隨機生成驗證碼,將驗證碼存到驗證碼錶中和phonenumber對應起來。然後當用戶註冊的時候,需要前端檢查電話號碼,郵箱是否正確。然後後端先通過phonenumber查詢到驗證碼,進行驗證碼驗證,然後獲得頭像圖片,進行圖片處理,最後存到mysql中的user和account表中。
用戶註冊操作代碼實現如下
class AccountService {
public void createAccount(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);
Account account = new Account(username, password);
accountRepo.save(username);
account.setPassword(password);
}
// 圖片處理,使得圖片大小在一個規定的值
private File processPicture(String avatar){
//下載圖片
//判斷圖片大小是否符合標準
//當圖片過大,進行圖片裁剪
//當圖片過小,進行圖片放大
...
}
private void getPicture(String avatar){
File avatarFile = processPicture(avatar)
// uploadFile 實現邏輯
...
}
}
}
class Account{
private String username;
private String password;
}
class User{
private String username;
private String phone;
private String email;
private String uploadedUrl;
private String avatar;
}
用戶註冊操作代碼修改
class AccountService {
private int WIDTH_PIXEL = 1024;
private int HIGHT_PIXEL = 1024;
public void createAccount(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);
Account account = new Account(username, password);
accountRepo.save(username);
account.setPassword(password);
}
}
class Account{
private String username;
private String password;
}
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){
//下載圖片
//判斷圖片大小是否符合標準
//當圖片過大,進行圖片裁剪
//當圖片過小,進行圖片放大
//上傳圖片
}
}
可以看到,上面的對話和代碼實現,包含2類內容。技術細節和業務邏輯。
- 技術細節:它們和業務無關,僅僅代表應用中的一種技術能力。如,我的數據庫表如何設計,驗證誰來做,是前後端分離還是不分離,數據是從前端來,還是從後端給。
- 業務邏輯:它們和業務相關,代表應用中的一種業務能力。如,填入電話,郵箱,頭像圖片,圖片大小的規定,以及短信驗證碼。
咱們再看看AccountService,主要實現註冊功能的類。processPicture方法中存在了處理圖片的邏輯(裁剪圖片,放大圖片等技術細節),也包含了業務邏輯(規定的圖片大小)。當我們想從AccountService中瞭解業務邏輯時,我們需要讀懂processPicture方法。我們需要花很長的時間去理解processPicture方法中的技術細節是在做什麼(如何做圖片裁剪,如何做放大圖片操作)。然而,過多的技術細節展現對於瞭解業務邏輯沒有任何幫助,反而會使人凌亂。使得代碼不那麼清晰。
再看修改後,我們將技術細節封裝到了個新的類 FileUtil中,同時將圖片縮放的大小這個業務邏輯放在AccountService中。這樣,我們在讀AccountService的時候,就可以更清晰的瞭解業務邏輯。
爲什麼要區分?
大腦的短期記憶無法一次容納約7個以上的記憶項目 —— 《金字塔原理》
當一個開發人員去熟悉一個新模塊的時候,首先會從整體去了解這個新模塊提供的功能,然後再會去看每個功能具體的實現細節是什麼。就像畫畫一樣,一開始要畫草圖去確定各個部分畫什麼東西,然後再慢慢補充細節。所謂細節都是魔鬼,如果一開始就進入細節,就容易陷入到細節中去,很難看到全局並找到重點。
因此,從認知的角度,我們其實一開始關注的是業務邏輯,當瞭解到我們關心的業務邏輯後,纔會去做技術細節的展開。因此,我們可以把業務邏輯的代碼和具體的技術實現進行分離,這樣,可以幫助我們通過整體-細節的方式進行學習。
如何區分業務邏輯和技術細節?
我們可以通過問問題的方式來區分現在做的這個事兒是業務邏輯還是技術細節。
- 這個事兒業務人員關心麼?有業務價值麼?
排序算法業務關心麼?把數據存到關係型數據庫還是文檔型數據庫業務關心麼?實時計算結果還是提前計算好結果保存下來,業務關心麼?是先取A數據進行計算還是先去B數據計算,業務關心麼?有價值麼?
如果答案是:Yes,那麼這就是業務邏輯,如果答案是:No,那麼這些可能就是技術細節,需要封裝到別處去 - 這個事兒換一種方式可以達成麼?
冒泡排序可以換成其他方式?Mysql存儲換一種方式可以達成麼?實時計算結果換一種方式能達成麼?
如果答案是:Yes,那麼這就是技術細節,如果答案是:No,那麼就是業務邏輯
區分後該怎麼辦?
分清了業務邏輯和技術細節後,我們需要把他們進行分區。
將業務邏輯放到一個package中,將技術細節放到一個package中。
三層架構
業務邏輯集中在service層中。業務無關的技術細節按照功能放到其他層。
- Controller
- TaskRequest
- TaskController
- TaskCreateRequest
- ProjectController
- ProjectResponse/ProjectVO
- Service
- Task
- TaskService
- Task
- Project
- ProjectService
- Project
- Picture
- Picture
- PictureService
- Repository
- Processor
- Utils
- EdocUtils
- PicutreUtils
- Config
- LogConfig
- HealthCheckConfig
整潔架構(Clean Arch)
- adapter
- inbound(外部對本服務的訪問,UI,controller)
- rest
- resources
- TaskRequest
- rpc
- outbound(本服務訪問三方系統,db,中間件,服務)
- persist
- application(用例,用例可以調用多個domain裏面的service)
- usecase
- TaskUseCase
- ProjectUseCase
- domain(業務模型)
- tasks
- Task
- TaskService
- projects
- Project
- ProjectService
- config(配置信息)
- LogConfig
- HealthCheckConfig