乾淨架構核心原則

SOLID 原則

S — 單一職責原則 (Single Responsibility)

每個 class 只有一個改變的理由。

// ❌ 錯誤:一個 class 做太多事
class UserService {
  createUser() { ... }
  sendEmail() { ... }
  logActivity() { ... }
}

// ✅ 正確:各司其職
class UserService {
  constructor(
    private userRepo: UserRepository,
    private emailService: EmailService,
    private logger: Logger
  ) {}
  createUser() { ... }
}

class EmailService {
  sendWelcome() { ... }
}

O — 開放封閉原則 (Open/Closed)

對擴展開放,對修改封閉。

// ✅ 新增支付方式不需修改現有程式碼
interface PaymentProcessor {
  process(amount: number): Promise<Result>;
}

class CreditCardProcessor implements PaymentProcessor { ... }
class LinePayProcessor implements PaymentProcessor { ... }
class ApplePayProcessor implements PaymentProcessor { ... }

L — 里氏替換原則 (Liskov Substitution)

子類別必須能替換父類別。

// ✅ 所有 Repository 都能互換使用
interface UserRepository {
  findById(id: string): Promise<User>;
  save(user: User): Promise<void>;
}

class PostgresUserRepository implements UserRepository { ... }
class InMemoryUserRepository implements UserRepository { ... } // 測試用

I — 介面隔離原則 (Interface Segregation)

不要強迫客戶端依賴不需要的介面。

// ❌ 錯誤:太大的介面
interface Repository {
  findById(): any;
  save(): any;
  delete(): any;
  findByEmail(): any;
  generateReport(): any;
}

// ✅ 正確:拆分小介面
interface ReadableRepository<T> {
  findById(id: string): Promise<T>;
}

interface WritableRepository<T> {
  save(entity: T): Promise<void>;
  delete(id: string): Promise<void>;
}

D — 依賴反轉原則 (Dependency Inversion)

高層模組不應依賴低層模組,兩者都應依賴抽象。

// ❌ 錯誤:高層依賴低層
class OrderService {
  constructor(private db: PostgresDatabase) {} // 硬性依賴
}

// ✅ 正確:依賴抽象
class OrderService {
  constructor(private orderRepo: OrderRepository) {} // 依賴介面
}

依賴規則 (The Dependency Rule)

源碼的依賴關係只能從外向內,不能從內向外。

Presentation → Application → Domain
     ↑              ↑           ↑
     └──────────────┴───────────┘
         依賴方向只能向內

實際含義

  • Domain 層:不 import 任何東西(除了標準庫)
  • Application 層:只能 import Domain
  • Infrastructure 層:可以 import Application + Domain
  • Presentation 層:可以 import 所有層

其他重要原則

DRY (Don't Repeat Yourself)

重複的程式碼要抽成共用函數或類別。

KISS (Keep It Simple, Stupid)

簡單的方案通常是最好的方案。

YAGNI (You Aren't Gonna Need It)

不要為「可能」的需求寫程式碼。

组合優於繼承

優先使用 composition 而非 inheritance。

// ✅ 組合
class OrderService {
  constructor(
    private validator: OrderValidator,
    private pricing: PricingService
  ) {}
}

// ❌ 繼承
class OrderService extends BaseService { ... }