四層架構 (Four Layers)

架構圖

┌─────────────────────────────────────────────┐
│  Presentation Layer (UI)                    │
│  Angular Components, Controllers            │
├─────────────────────────────────────────────┤
│  Application Layer (Use Cases)              │
│  Business Logic, Orchestration              │
├─────────────────────────────────────────────┤
│  Domain Layer (Entities)                    │
│  Business Rules, Data Models                │
├─────────────────────────────────────────────┤
│  Infrastructure Layer (External)            │
│  Database, API, File System                 │
└─────────────────────────────────────────────┘

各層職責

1. Domain Layer(最核心)

包含: Entities, Value Objects, Domain Events, Repository Interfaces

規則: 不依賴任何外層

// domain/entities/user.ts
export class User {
  constructor(
    public readonly id: string,
    public readonly email: string,
    private _name: string
  ) {
    this.validate();
  }

  private validate(): void {
    if (!this.email.includes('@')) {
      throw new Error('Invalid email');
    }
  }

  changeName(name: string): void {
    if (name.length < 2) throw new Error('Name too short');
    this._name = name;
  }
}

// domain/repositories/user-repository.ts
export interface UserRepository {
  findById(id: string): Promise<User | null>;
  findByEmail(email: string): Promise<User | null>;
  save(user: User): Promise<void>;
}

2. Application Layer(業務流程)

包含: Use Cases, DTOs, Application Services

規則: 只依賴 Domain Layer

// application/use-cases/create-user.ts
export class CreateUserUseCase {
  constructor(private userRepo: UserRepository) {}

  async execute(request: CreateUserRequest): Promise<UserResponse> {
    // 1. 檢查是否已存在
    const existing = await this.userRepo.findByEmail(request.email);
    if (existing) throw new Error('User already exists');

    // 2. 建立使用者
    const user = new User(generateId(), request.email, request.name);

    // 3. 儲存
    await this.userRepo.save(user);

    // 4. 回傳
    return { id: user.id, email: user.email };
  }
}

// application/dtos/create-user-request.ts
export interface CreateUserRequest {
  email: string;
  name: string;
}

3. Infrastructure Layer(外部資源)

包含: Repository Implementations, External API Clients, Database

規則: 實作 Domain 定義的介面

// infrastructure/repositories/postgres-user-repository.ts
import { UserRepository } from '../../domain/repositories/user-repository';
import { User } from '../../domain/entities/user';

export class PostgresUserRepository implements UserRepository {
  constructor(private pool: Pool) {}

  async findById(id: string): Promise<User | null> {
    const result = await this.pool.query(
      'SELECT * FROM users WHERE id = $1', [id]
    );
    if (result.rows.length === 0) return null;
    return this.toDomain(result.rows[0]);
  }

  async save(user: User): Promise<void> {
    await this.pool.query(
      'INSERT INTO users (id, email, name) VALUES ($1, $2, $3)',
      [user.id, user.email, user.name]
    );
  }

  private toDomain(row: any): User {
    return new User(row.id, row.email, row.name);
  }
}

4. Presentation Layer(使用者介面)

包含: UI Components, Controllers, API Routes

規則: 調用 Application Layer

// presentation/controllers/user-controller.ts
export class UserController {
  constructor(private createUserUseCase: CreateUserUseCase) {}

  async create(req: Request, res: Response): Promise<void> {
    try {
      const result = await this.createUserUseCase.execute({
        email: req.body.email,
        name: req.body.name
      });
      res.json(result);
    } catch (error) {
      res.status(400).json({ error: error.message });
    }
  }
}

依賴注入 (Dependency Injection)

所有依賴都從外部注入,不內部 new:

// bootstrap.ts(應用程式啟動點)
const pool = new Pool({ connectionString: DB_URL });
const userRepo = new PostgresUserRepository(pool);
const createUserUseCase = new CreateUserUseCase(userRepo);
const userController = new UserController(createUserUseCase);

// Web 框架路由
app.post('/users', (req, res) => userController.create(req, res));

目錄結構範例

src/
├── domain/
│   ├── entities/
│   │   ├── user.ts
│   │   └── order.ts
│   ├── repositories/
│   │   ├── user-repository.ts
│   │   └── order-repository.ts
│   └── events/
│       └── user-created.ts
│
├── application/
│   ├── use-cases/
│   │   ├── create-user.ts
│   │   └── get-user.ts
│   ├── dtos/
│   │   ├── create-user-request.ts
│   │   └── user-response.ts
│   └── services/
│       └── email-service.ts
│
├── infrastructure/
│   ├── repositories/
│   │   ├── postgres-user-repository.ts
│   │   └── postgres-order-repository.ts
│   ├── services/
│   │   └── sendgrid-email-service.ts
│   └── database/
│       └── connection.ts
│
├── presentation/
│   ├── controllers/
│   │   └── user-controller.ts
│   ├── routes/
│   │   └── user-routes.ts
│   └── middleware/
│       └── auth.ts
│
└── bootstrap.ts