在 Laravel 技術面試中,有一題出現率高達 90%、但也最容易讓人掉入死胡同的陷阱題:
「你在過去的專案中,使用過哪些 Design Pattern(設計模式)?」
很多人聽到這題,第一反應是開始像念經一樣背名詞:
「我用過 MVC, Repository, Service, Factory, Strategy, Observer, Singleton, DTO...」
講完之後洋洋得意,以為展現了雄厚的軟體工程底子。但現實是,面試官往往在心裡默默給了個 X,幾天後寄出一封感謝信。
為什麼?因為背出這些名詞,只證明了你有讀過書;但「過度設計(Over-engineering)」與「為了套 Pattern 而寫 Pattern」,恰恰是資深架構師最害怕在團隊裡看到的災難。
Laravel 框架本身已經將無數成熟的設計模式(DI、Service Container、Pipeline、Observer 等)封裝得極其優雅。面試官真正想聽的,從來不是「你背過多少 Pattern」,而是:你能不能精準判斷什麼時候該抽象?什麼時候該保持簡單?以及,你懂不懂得控制系統的複雜度。
以下是我在面試與帶團隊時,評估工程師是否具備「實戰架構思維」的 5 個核心維度:
一、被發感謝信的第一個盲點:框架已經解決的問題,你又重造一次輪子
Laravel 開發者最常犯的錯誤,就是「忽視框架內建的抽象,自己硬寫一套笨重版」。
當你在 Controller 寫下這段程式碼:
PHP
public function __construct(
private PaymentService $paymentService
) {}
這背後已經是極其成熟的 Dependency Injection (DI) 與 Service Container 機制。
許多拿到感謝信的人,喜歡在專案裡自己寫一套自製的 Service Locator、Singleton 產生器,或是手寫一套 Event Dispatcher。這在面試官眼裡不是「手藝好」,而是「不熟悉生態系」與「增加團隊不必要的維護成本」。
二、被發感謝信的第二個盲點:把 Repository 當作高級 CRUD 工具
「我們全站套用 Repository Pattern,Controller 絕對不碰 Model!」這是面試中最常聽到的豪語。
當面試官追問:「那你的 Repository 怎麼寫?」
很多人秀出來的 Code 卻是長這樣:
PHP
// ❌ 毫無意義的 Pass-through Code
class UserRepository {
public function find(int $id) {
return User::find($id);
}
}
在已經擁有強大 Eloquent ORM 的 Laravel 中,這樣寫只是在框架上無謂地疊加一層沒有商業價值的封裝。
- 什麼時候是壞設計? 一般 CRUD、直接使用 Eloquent Scope 能解決的情境,硬套 Repository 只會讓代碼追蹤變得無比繁瑣。
- 什麼時候才是好設計? 只有當專案確實存在多種異質資料源切換(如平時讀 DB、特定情境切到外部 REST API/Elasticsearch),或是極度複雜的 DB 存取需要完全隔離時,抽象這層才有意義。
三、資深工程師的核心修養:明確的抽象界線
真正能拿 Offer 的回答,會展現出對「抽象成本」的務實評估:
1. Controller 瘦身,但絕不盲目拆 Service
當業務邏輯很簡單時:
PHP
$user->update($request->validated());
沒有必要為了「看起來很專業」,硬寫成
Controller → UserService → UserRepository → UserModel。多疊四層抽象,不會讓你的程式碼變高尚,只會讓接手的工程師想把你換掉。2. Strategy Pattern:只在複雜度夠大時登場
- 適合作法: 系統需要同時支援 LinePay、ECPay、Stripe,建立
PaymentStrategy介面搭配 Factory,擴充性極高。 - 過度設計: 業務邏輯只有簡單的
if ($type === 'A')二分法,卻預先建立 Interface、Strategy、Resolver 3 個類別。關鍵永遠是:「當前的業務複雜度,是否值得付出這筆抽象化成本?」
3. Event & Listener:絕不拿來模糊 Transaction 邊界
當「訂單成立」後,發送 App 推播、觸發 Webhook,用 Event/Listener 解耦非常完美。
但如果某個動作是訂單成立不可分割的商業條件(例如庫存扣減、交易狀態變更),就必須堅決保留在主流程或 DB Transaction 內!如果為了「程式碼看起來很漂亮」硬拆成 Event,一旦 Event 丟進 Queue 處理失敗,將會帶來災難性的資料不一致。
四、在高併發與企業級系統中,Pattern 救不了你
很多工程師喜歡把高併發(如多租戶秒殺、熱門抽獎)歸因於「因為我套用了某某 Design Pattern」。
這在資深架構師聽來非常外行。高併發的核心是資源競態(Race Condition)與系統穩定度,這需要的是底層機制:
- Concurrency Control: DB Transaction 隔離層級、原子更新(Atomic Update)、行級鎖(
lockForUpdate)、Redis Atomic Locks。 - 流量削峰: Queue / Redis Stream 異步佇列化與兩階段寫入。
- DB 效能: 覆蓋索引(Covering Index)、讀寫分離、快取策略。
把併發與架構問題歸因於 Pattern,代表你對系統底層的併發控制與硬體瓶頸還缺乏實務經驗。
五、AI Coding 時代的鐵律:「最小修改路徑」
在現代使用 AI Agent(如 Cursor、Claude)輔助開發的時代,「避免過度設計」變得前所未有的重要。
AI 非常喜歡一看到需求,就一股腦產生:
Service + DTO + Repository + Interface + Factory + Trait 組合包。表面上看起來架構極其完整,但往往與專案既有的 Codebase 風格脫節,造成嚴重的技術債。資深工程師的核心價值,在於引導 AI 做出最小且正確的修改:
$$\text{理解需求} \longrightarrow \text{走訪既有 Codebase} \longrightarrow \text{確認現有 Pattern} \longrightarrow \text{選擇最小修改路徑} \longrightarrow \text{Code Review 與驗證}$$
💡 面試官想聽到的「滿分回答範本」
下次面試被問到這題,你可以這樣回答:
「Laravel 本身已經提供非常多優秀的架構機制(如 Service Container、Middleware、Form Request、Event/Listener、Policy 等)。在實務上,我絕不把使用 Pattern 當作 KPI,而是作為控制複雜度的工具。遇到多金流或動態策略切換時,我會用 Strategy;需要解耦旁支邏輯時,我會用 Event;業務流程複雜時,我會抽 Service。但我不會在沒有存取複雜度時強套 Repository 或 DTO,避免產生無謂的抽象層。我更重視的是架構的維護性、可組合性與資料一致性。例如在 CMS 專案中透過 Traits 與 Custom Casts 建立可組合的共通 Module;在高併發情境中,我關注的是鎖機制、Transaction 邊界與快取削峰,而不是套用什麼名詞。對我來說,真正優秀的架構設計,是清楚知道什麼時候該抽象,以及什麼時候該保持簡單。」
後記
寫程式久了,真的會越來越不在意「這段程式到底算哪一種名詞 Pattern」。
真實的企業系統永遠是多租戶 + 權限 + 多語系 + 第三方 API + 高併發 + 歷史技術債的混合體。能幫團隊控制複雜度、降低維護成本、確保系統穩定演進的設計,就是好的設計。