核心邏輯用領域命名,不要放進 Services/ 那種技術分類。
我的 app/ 長這樣:
app/
Commerce/ ← 自己加的
Support/ ← 自己加的
Console/
Http/
Mail/
Models/
Providers/
Laravel 原本的資料夾我一個都沒動,只加了兩個。
沒有 Services/、沒有 Repositories/、沒有 DTOs/、沒有 Actions/、沒有 Enums/、沒有 Traits/。
app/Services/因為那不是一個分類,那是一個垃圾桶。
半年後打開它,你會看到 OrderService、MailService、ImageService、HelperService,彼此毫無關係,只是因為「不知道放哪」所以都丟進去。
app/DTOs/ 也一樣。它把「購物車的狀態」跟「寄信的參數」放在同一個資料夾,只因為它們都是 DTO。這是用「這個類別是什麼」來分類,而不是「它在處理什麼事」。
我的分法是後者:
app/Commerce/ 這個系統的核心:購物車、定價、促銷、結帳、金流、訂閱
app/Support/ 跨模組的基礎設施:圖片儲存、單號、點數帳本、各種 builder
CartState 放在 Commerce/,不是因為它是 DTO,是因為它在處理商業邏輯。
一個新人接手這個專案,看到 app/ 底下只有 Commerce/ 是自己加的,他馬上知道:這個系統的重心在這裡,其他都是週邊。
這比任何文件都有效。
而且它跟前面幾篇的主張是同一件事的兩面:
檔案結構會反映你的判斷。 如果你的 app/ 底下是 Services/ Repositories/ DTOs/ Actions/,那代表你的判斷是「所有模組一樣重要、用同一套規矩」。如果是 Commerce/,代表你做過取捨。
看到這裡可能會有人想:那我來切 Catalog/、Ordering/、Shipping/、Marketing/、Membership/。
不要。
中小企業的電商,真正需要保護的領域只有一個,就是結帳與定價。 其他模組是 CRUD,把它們切成五個領域資料夾,只是把簡單的東西搬到比較深的地方。
我的原則是:一個領域資料夾就好,等到真的第二塊變複雜了再加第二個。 資料夾是結果,不是計畫。
Commerce/ 裡面用到的觀念,其實都有正式名稱:
CartLine、AdjustmentCartInput知道名字有好處——你想深入的時候,才查得到資料。
但我要誠實說:我沒有在做完整的 DDD。 沒有 Aggregate、沒有 Repository、沒有 Domain Service、沒有 Bounded Context 的那一整套。我只是把「會算錢的邏輯」集中在一個用商業語言命名的資料夾裡,讓它不碰資料庫。
這樣就足夠解決中小企業電商 90% 的問題了。完整的 DDD 是為了幾十人的團隊、幾十個子領域設計的,套在一個三人團隊的電商上,你會花很多力氣在維護架構,而不是在賣東西。