案例背景:Mary Rowan 的電商應用程式#
本章透過一個完整的案例研究,展示沒有使用 DI 的程式碼會產生什麼問題。主角 Mary Rowan 被要求建構一個電商應用程式,技術選型為:
- UI 層:ASP.NET Core MVC
- 資料存取層:Entity Framework Core + SQL Server
- 架構:傳統三層式架構 (Three-Layer Architecture)
Mary 採用 inside-out 的開發方式——從資料層開始,依序往上建構到 Domain 層,最後是 UI 層。

Figure 2.1: 標準三層式應用程式架構
資料存取層#
Mary 首先建立了 Product 類別和 CommerceContext:
public class CommerceContext : DbContext
{
protected override void OnConfiguring(
DbContextOptionsBuilder builder)
{
builder.UseSqlServer(
"Server=.;Database=Commerce;..."); // 硬編碼的連線字串
}
}連線字串直接寫死在程式碼中,導致每次更換資料庫環境都需要修改並重新編譯程式碼。
Domain 層#
ProductService 直接引用資料存取層的 CommerceContext 和 Product:
public class ProductService
{
private readonly CommerceContext context;
public ProductService()
{
this.context = new CommerceContext(); // 直接 new
}
public IEnumerable<Product> GetFeaturedProducts()
{
return context.Products
.Where(p => p.IsFeatured)
.ToList();
}
}問題顯而易見:Domain 層反向依賴了資料存取層,而非資料存取層依賴 Domain 層。
UI 層#
HomeController 直接建立 ProductService,折扣邏輯也散落在 Controller 和 View 中:
public class HomeController : Controller
{
public ViewResult Index()
{
var service = new ProductService(); // 直接 new
var products = service.GetFeaturedProducts();
// 折扣邏輯在 UI 層處理
// ...
}
}問題清單#
這段緊耦合的程式碼暴露了多項設計問題:
- 依賴方向錯誤:Domain 層和 UI 層都依賴資料存取層,形成自上而下的單向依賴鏈
- 沒有定義 Abstraction:所有類別直接依賴具體實作,沒有介面可供替換
- 領域概念放錯位置:
Product(核心領域概念)定義在資料存取層中 - 折扣邏輯外洩:業務邏輯散落在 UI 層,違反關注點分離
- 硬編碼的連線字串:無法針對不同環境進行設定
實際影響#
這些設計問題直接導致以下後果:
| 問題 | 後果 |
|---|---|
| 無法替換資料存取 | 想換成 NoSQL 或 Web API?必須大幅修改 Domain 層 |

Figure 2.8: 嘗試替換關聯式資料存取層
ProductService 永遠綁定真實資料庫 |
| 無法平行開發 | 資料層未完成前,Domain 層和 UI 層無法獨立進行 |
| 維護成本高 | 任何變更都可能產生連鎖反應 |
依賴圖分析#
從依賴圖來看,所有箭頭都指向下方(資料存取層),形成一個倒金字塔的依賴結構。這意味著最底層的實作細節變成了整個系統的核心,任何資料存取的變更都會向上波及所有層級。

Figure 2.6: Mary 應用程式的依賴圖,箭頭指向被依賴的模組

Figure 2.10: 理想的依賴圖
本章刻意呈現反面教材。這個「改善前」的版本將在第三章以 DI 原則重新設計,兩相對照之下,DI 的價值會更加清晰。