在微服務架構中,遠程調用是不可避免的一環,無論是通過 Feign、Dubbo,還是其他 RPC 框架。很多團隊在項目初期為了開發速度,常常直接在業務中調用遠程接口,拿到返回值就繼續向下處理。然而,隨着業務的發展,這種方式往往會埋下無數隱患。
這篇文章將介紹一種經典的架構設計思想 —— 防腐層(Anti-Corruption Layer,ACL),並結合 Feign / Dubbo 遠程調用的實際情況,聊聊如何通過這層“護城河”保護你的業務系統免受外部污染。
一、什麼是防腐層(ACL)?
防腐層是領域驅動設計(DDD)中的重要模式,其核心思想是:
**在內部系統與外部系統交互的邊界處,添加一層適配/轉換邏輯,屏蔽外部系統的不穩定、不規範、不一致,防止對內部領域模型造成腐蝕。**
通俗地説,就是不要讓“外面爛掉的代碼”影響我們“裏面寫得還不錯的業務”。
二、為什麼需要防腐層?
很多開發者可能會問:遠程接口已經封裝好了,我為什麼還要加一層邏輯?這其實類似前端的 axios.interceptors,或網關的統一封裝 ———— 做的是“系統層面統一保護”。
常見遠程調用問題:
❌ **外部返回的數據結構變了**(字段被刪改)
❌ 返回的是 null / 異常值,沒有任何提示
❌ **某些字段不符合預期**(如枚舉類型、時間格式)
❌ 業務調用方直接依賴了第三方的返回類型
❌ **一出問題,全系統崩潰或全鏈路掛掉**
加了防腐層後:
✅ **你可以統一封裝異常、兜底返回、自定義錯誤碼**
✅ 你可以把**髒數據轉換為乾淨的業務模型**
✅ 你可以**限制對外部依賴的耦合範圍**
✅ **換一個接口實現(如從 Feign 換成 Dubbo)也能絲滑無感**
三、實現方式:封裝你的防腐層
我們以一個簡單的 Feign 接口為例:
@FeignClient(name = "user-service")
public interface UserFeignClient {
@GetMapping("/api/users/{id}")
UserRemoteDTO getUserById(@PathVariable("id") String id);
}
✨ 錯誤做法:直接在 Controller 中用 Feign
@GetMapping("/user/{id}")
public UserRemoteDTO getUser(@PathVariable String id) {
return userFeignClient.getUserById(id); // 💣 一旦出錯就是 null / 異常
}
✅ 推薦做法:通過防腐層封裝調用邏輯
@Service
public class UserAclService {
private final UserFeignClient userFeignClient;
public UserAclService(UserFeignClient userFeignClient) {
this.userFeignClient = userFeignClient;
}
public UserDTO getUserInfo(String id) {
UserRemoteDTO remote = userFeignClient.getUserById(id);
if (remote == null || StringUtils.isBlank(remote.getUserName())) {
throw new BusinessException("用户不存在或遠程數據異常");
}
return toDTO(remote);
}
private UserDTO toDTO(UserRemoteDTO remote) {
UserDTO dto = new UserDTO();
dto.setName(remote.getUserName());
dto.setEmail(remote.getEmail());
return dto;
}
}
這樣做之後:
你的 Controller / Service 層永遠拿到的是穩定的 UserDTO;
即使 Feign 接口字段改了,只要防腐層修復,內部邏輯不變;
可以在這裏加上限流、熔斷、兜底邏輯等。
四、Dubbo 也一樣適用
Dubbo 的調用雖然是強類型接口,但也面臨同樣的問題,尤其是多人協作時容易出現接口不一致的 bug。
同樣適合加一層防腐層服務,封裝對外部 Dubbo 接口的所有訪問和轉換邏輯。
五、總結:系統的邊界防線,不只是網關
在微服務中,邊界治理非常關鍵。不要小看防腐層,它雖然代碼量不多,但扮演的是“系統邊界的最後一道防線”。
如果你遇到以下情況,建議立即引入防腐層:
**外部系統頻繁改動**返回結構;
項目初期**未對遠程調用做規範封裝**;
出現過“字段 null”、“接口報錯業務崩潰”等問題;
想提升代碼可維護性、可測試性、易於遷移等能力。