# Clean Architecture (Hexagonal Architecture)

## 📚 สารบัญ

1. [หลักการพื้นฐาน](#หลักการพื้นฐาน)
2. [โครงสร้าง Clean Architecture](#โครงสร้าง-clean-architecture)
3. [ข้อดี และข้อเสีย](#ข้อดี-และข้อเสีย)
4. [Dependency Rule](#dependency-rule)
5. [Code Examples](#code-examples)
6. [Real World Examples](#real-world-examples)
7. [Best Practices](#best-practices)

---

## 🎯 หลักการพื้นฐาน

### Clean Architecture คืออะไร?

**นิยาม:** สถาปัตยกรรมที่ Business Logic ไม่ขึ้นกับ Frameworks, Databases, UI

**Key Concept:**

- Business logic at center
- External dependencies at edges
- All dependencies point inward
- Easy to test
- Framework-agnostic

### ประวัติศาสตร์

```
2012: Robert C. Martin (Uncle Bob) introduces
↓
"The Clean Architecture" blog post
↓
Builds on: Hexagonal Architecture, Onion Architecture, Screaming Architecture
↓
Principles:
  - Business logic independent
  - Testable without frameworks
  - Framework-agnostic
```

### หลักการหลัก

```
1️⃣  Independent of Frameworks
   - Business logic not dependent on Rails, Express, etc.
   - Frameworks are external tools
   - Easy to replace frameworks

2️⃣  Testable
   - Write unit tests without UI, database, web framework
   - Test business logic directly

3️⃣  Independent of UI
   - Change UI (web, mobile, CLI) without changing business logic
   - Business logic doesn't know about UI

4️⃣  Independent of Database
   - Swap database (MySQL → PostgreSQL) without changing logic
   - Business logic doesn't know about DB

5️⃣  Independent of any External Agency
   - Business logic is isolated
   - Can use in different contexts

6️⃣  Dependency Rule
   - All dependencies point inward
   - Inner circles don't depend on outer circles
   - Inner circles = Abstract, Outer circles = Concrete
```

---

## 🏗️ โครงสร้าง Clean Architecture

### The Onion/Concentric Circles

```
┌─────────────────────────────────────────────────────────────┐
│                    OUTER CIRCLE                             │
│               (Frameworks & Drivers)                         │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ - Web Framework (Express, Django, Rails)             │   │
│  │ - Database (MySQL, PostgreSQL, MongoDB)              │   │
│  │ - UI Frameworks (React, Vue)                         │   │
│  │ - External Libraries                                 │   │
│  │ - HTTP Clients, File Systems                         │   │
│  └──────────────────────────────────────────────────────┘   │
│                          │                                   │
│                          ↑ depends on                        │
│                          │                                   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │          INTERFACE ADAPTERS LAYER                    │   │
│  │  ┌──────────────────────────────────────────────┐    │   │
│  │  │ - Controllers                                │    │   │
│  │  │ - Gateways (Database repositories)           │    │   │
│  │  │ - Presenters                                 │    │   │
│  │  │ - Request/Response Models                    │    │   │
│  │  │ - Dependency Injection Container             │    │   │
│  │  └──────────────────────────────────────────────┘    │   │
│  └──────────────────────────────────────────────────────┘   │
│                          │                                   │
│                          ↑ depends on                        │
│                          │                                   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │          APPLICATION BUSINESS RULES LAYER           │   │
│  │  ┌──────────────────────────────────────────────┐    │   │
│  │  │ - Use Cases (Application Services)           │    │   │
│  │  │ - Orchestrate flows                          │    │   │
│  │  │ - Business rules at this level               │    │   │
│  │  │ - Don't know about outer layers              │    │   │
│  │  └──────────────────────────────────────────────┘    │   │
│  └──────────────────────────────────────────────────────┘   │
│                          │                                   │
│                          ↑ depends on                        │
│                          │                                   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │  ENTITIES / ENTERPRISE BUSINESS RULES LAYER          │   │
│  │  ┌──────────────────────────────────────────────┐    │   │
│  │  │ - Domain Objects (User, Order, Product)      │    │   │
│  │  │ - Business Logic specific to domain          │    │   │
│  │  │ - Value Objects                              │    │   │
│  │  │ - Domain Exceptions                          │    │   │
│  │  │ - Most stable, least likely to change        │    │   │
│  │  └──────────────────────────────────────────────┘    │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│                  INNER CIRCLE                               │
│             (Pure Business Logic)                           │
└─────────────────────────────────────────────────────────────┘

Dependency Direction:
Outer → Adapter → Use Case → Entity
  ↓       ↓          ↓         ↑
  └───────────────────────────┘
  All dependencies point inward
```

### Detailed Layer Breakdown

#### Layer 1: Entities (Core Business Logic)

```javascript
// Domain Objects - Pure business logic
// No dependencies on external packages

class User {
  constructor(id, email, name) {
    this.id = id;
    this.email = email;
    this.name = name;
  }

  // Pure business logic
  isValidEmail() {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email);
  }

  changeEmail(newEmail) {
    if (!this.isValidEmail()) {
      throw new InvalidEmailError("Invalid email format");
    }
    this.email = newEmail;
  }
}

class Order {
  constructor(id, userId, items, total) {
    this.id = id;
    this.userId = userId;
    this.items = items;
    this.total = total;
    this.status = "pending";
  }

  canBeCancelled() {
    return this.status === "pending" || this.status === "confirmed";
  }

  cancel() {
    if (!this.canBeCancelled()) {
      throw new InvalidOrderStateError("Cannot cancel this order");
    }
    this.status = "cancelled";
  }
}

// Domain Exceptions
class InvalidEmailError extends Error {
  constructor(message) {
    super(message);
    this.name = "InvalidEmailError";
  }
}

class InvalidOrderStateError extends Error {
  constructor(message) {
    super(message);
    this.name = "InvalidOrderStateError";
  }
}
```

**Characteristics:**

- No external dependencies
- Pure business logic
- Can be tested without frameworks
- Not aware of databases, UI, frameworks
- Most stable layer

#### Layer 2: Application Business Rules (Use Cases)

```javascript
// Use Cases - Application logic that orchestrates entities
// Can use repositories (interfaces), but not implementations

// Interface (abstraction)
class UserRepository {
  async findById(id) {
    throw new Error("Must implement");
  }
  async save(user) {
    throw new Error("Must implement");
  }
  async findByEmail(email) {
    throw new Error("Must implement");
  }
}

// Use Case
class CreateUserUseCase {
  constructor(userRepository, emailService) {
    this.userRepository = userRepository;
    this.emailService = emailService;
  }

  async execute(request) {
    // 1. Check if email exists
    const existingUser = await this.userRepository.findByEmail(request.email);
    if (existingUser) {
      throw new Error("Email already registered");
    }

    // 2. Create entity
    const user = new User(null, request.email, request.name);

    // 3. Validate entity
    if (!user.isValidEmail()) {
      throw new Error("Invalid email");
    }

    // 4. Save (repository interface, not concrete implementation)
    const savedUser = await this.userRepository.save(user);

    // 5. Send email (external service interface)
    await this.emailService.sendWelcomeEmail(savedUser);

    return {
      id: savedUser.id,
      email: savedUser.email,
      name: savedUser.name,
    };
  }
}

class GetUserUseCase {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  async execute(userId) {
    const user = await this.userRepository.findById(userId);
    if (!user) {
      throw new Error("User not found");
    }
    return {
      id: user.id,
      email: user.email,
      name: user.name,
    };
  }
}
```

**Characteristics:**

- Uses interfaces (abstractions), not concrete implementations
- Depends on entities
- Application-specific business rules
- Orchestrates complex flows
- Can be tested with mock repositories

#### Layer 3: Interface Adapters

```javascript
// Controllers - Convert HTTP to use cases
class UserController {
  constructor(createUserUseCase, getUserUseCase) {
    this.createUserUseCase = createUserUseCase;
    this.getUserUseCase = getUserUseCase;
  }

  async create(req, res) {
    try {
      const result = await this.createUserUseCase.execute({
        email: req.body.email,
        name: req.body.name,
      });
      res.status(201).json(result);
    } catch (error) {
      res.status(400).json({ error: error.message });
    }
  }

  async get(req, res) {
    try {
      const result = await this.getUserUseCase.execute(req.params.id);
      res.status(200).json(result);
    } catch (error) {
      res.status(404).json({ error: error.message });
    }
  }
}

// Repositories - Implement interface, adapt to database
class MySQLUserRepository extends UserRepository {
  constructor(db) {
    super();
    this.db = db;
  }

  async findById(id) {
    const [rows] = await this.db.query("SELECT * FROM users WHERE id = ?", [
      id,
    ]);
    if (rows.length === 0) return null;
    return this.mapToEntity(rows[0]);
  }

  async save(user) {
    const [result] = await this.db.query(
      "INSERT INTO users (email, name) VALUES (?, ?)",
      [user.email, user.name]
    );
    user.id = result.insertId;
    return user;
  }

  async findByEmail(email) {
    const [rows] = await this.db.query("SELECT * FROM users WHERE email = ?", [
      email,
    ]);
    if (rows.length === 0) return null;
    return this.mapToEntity(rows[0]);
  }

  mapToEntity(row) {
    return new User(row.id, row.email, row.name);
  }
}

// Presenters - Format output
class UserPresenter {
  present(user) {
    return {
      id: user.id,
      email: user.email,
      name: user.name,
      created: user.createdAt?.toISOString(),
    };
  }
}
```

**Characteristics:**

- Controllers convert HTTP to use cases
- Repositories implement interfaces
- Presenters format output
- Gateways adapt external services
- Know about frameworks (Express, databases)

#### Layer 4: Frameworks & Drivers

```javascript
// Express setup
const express = require('express');
const mysql = require('mysql2/promise');

// Database setup
const db = mysql.createPool({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'myapp'
});

// Email service implementation
class NodemailerEmailService {
  async sendWelcomeEmail(user) {
    // Use actual nodemailer
    const transporter = nodemailer.createTransport({...});
    await transporter.sendMail({
      to: user.email,
      subject: 'Welcome!',
      body: `Hello ${user.name}`
    });
  }
}

// Dependency injection
const userRepository = new MySQLUserRepository(db);
const emailService = new NodemailerEmailService();
const createUserUseCase = new CreateUserUseCase(userRepository, emailService);
const getUserUseCase = new GetUserUseCase(userRepository);
const userController = new UserController(createUserUseCase, getUserUseCase);

// Express routes
const app = express();
app.use(express.json());

app.post('/users', (req, res) => userController.create(req, res));
app.get('/users/:id', (req, res) => userController.get(req, res));

app.listen(3000);
```

**Characteristics:**

- Framework-specific code
- Database implementations
- External service integrations
- Least important layer
- Most likely to change

---

## ✅ ข้อดี และข้อเสีย

### ✅ ข้อดี

| ข้อดี                  | อธิบาย                                 |
| ---------------------- | -------------------------------------- |
| **Framework-Agnostic** | Change Express → Fastify easily        |
| **Testability**        | Test business logic without frameworks |
| **Independence**       | Swap databases, UI, services           |
| **Maintainability**    | Clear structure, easy to find code     |
| **Reusability**        | Use cases reusable across platforms    |
| **Domain-Focused**     | Business logic at center               |
| **Long-term**          | Scales well as project grows           |
| **Team Organization**  | Clear responsibilities                 |

### ❌ ข้อเสีย

| ข้อเสีย                    | อธิบาย                              |
| -------------------------- | ----------------------------------- |
| **Complexity**             | More layers, more abstractions      |
| **Over-engineering**       | Overkill for simple projects        |
| **Learning Curve**         | Requires understanding architecture |
| **Boilerplate**            | More code needed for setup          |
| **Performance**            | Extra layers = slight overhead      |
| **Team Expertise**         | Need experienced developers         |
| **Not Suitable for Small** | Too much structure for MVP          |
| **Dependency Inversion**   | Requires good interface design      |

### เมื่อไดควรใช้ Clean Architecture?

```
✅ ใช้เมื่อ:
- Long-term projects
- Team size > 10
- Complex business logic
- Need framework independence
- Need extensive testing
- Multiple platforms/interfaces
- Large codebase

❌ ไม่ใช้เมื่อ:
- Simple CRUD applications
- MVP/Prototypes
- Small team
- One-off projects
- Performance-critical systems
- Learning new frameworks
- Simple business logic
```

---

## 🔄 Dependency Rule

### The Golden Rule

**"Source code dependencies must point inward"**

```
CORRECT:
┌─────────────────────────────────────────┐
│     Controller                          │
│     (depends on)                        │
│              ↓                          │
│     UseCase                             │
│     (depends on)                        │
│              ↓                          │
│     Entity                              │
└─────────────────────────────────────────┘

WRONG:
┌─────────────────────────────────────────┐
│     Entity                              │
│     (depends on)                        │
│              ↓                          │
│     UseCase                             │
│     (depends on)                        │
│              ↓                          │
│     Controller                          │
└─────────────────────────────────────────┘
(Entity shouldn't depend on UseCase!)
```

### Violating the Rule

```javascript
// ❌ BAD: Entity depends on database
class User {
  constructor(id, email, name) {
    this.id = id;
    this.email = email;
    this.name = name;
  }

  async save() {
    // ❌ Entity shouldn't know about database!
    const db = new DatabaseConnection();
    await db.query("INSERT INTO users ...");
  }
}

// ✅ GOOD: Entity pure, repository handles persistence
class User {
  constructor(id, email, name) {
    this.id = id;
    this.email = email;
    this.name = name;
  }

  isValid() {
    return this.email.includes("@");
  }
}

class UserRepository {
  async save(user) {
    // Repository knows about database
    await db.query("INSERT INTO users ...");
  }
}
```

---

## 💻 Code Examples

### Complete Clean Architecture Project

```javascript
// ============================================
// LAYER 1: ENTITIES (Enterprise Business Rules)
// ============================================
// src/domain/entities/User.js

class User {
  constructor(id, email, name, passwordHash = null) {
    this.id = id;
    this.email = email;
    this.name = name;
    this.passwordHash = passwordHash;
  }

  // Pure business logic
  isValidEmail() {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(this.email);
  }

  isValidName() {
    return this.name && this.name.trim().length >= 2;
  }

  isPasswordValid() {
    return this.passwordHash && this.passwordHash.length > 0;
  }

  validate() {
    if (!this.isValidEmail()) {
      throw new InvalidEmailError("Invalid email format");
    }
    if (!this.isValidName()) {
      throw new InvalidNameError("Name must be at least 2 characters");
    }
    if (!this.isPasswordValid() && !this.id) {
      throw new MissingPasswordError("Password required for new users");
    }
  }
}

// Domain Exceptions
class InvalidEmailError extends Error {
  constructor(message) {
    super(message);
    this.name = "InvalidEmailError";
  }
}

class InvalidNameError extends Error {
  constructor(message) {
    super(message);
    this.name = "InvalidNameError";
  }
}

class MissingPasswordError extends Error {
  constructor(message) {
    super(message);
    this.name = "MissingPasswordError";
  }
}

module.exports = {
  User,
  InvalidEmailError,
  InvalidNameError,
  MissingPasswordError,
};

// ============================================
// LAYER 2: APPLICATION BUSINESS RULES (Use Cases)
// ============================================
// src/application/usecases/CreateUserUseCase.js

const { User } = require("../../domain/entities/User");

class CreateUserUseCase {
  constructor(userRepository, emailService, passwordEncoder) {
    this.userRepository = userRepository;
    this.emailService = emailService;
    this.passwordEncoder = passwordEncoder;
  }

  async execute(request) {
    // 1. Validate input
    if (!request.email || !request.name || !request.password) {
      throw new Error("Missing required fields");
    }

    // 2. Check if user exists
    const existingUser = await this.userRepository.findByEmail(request.email);
    if (existingUser) {
      throw new Error("Email already registered");
    }

    // 3. Create entity
    const user = new User(null, request.email, request.name);

    // 4. Validate entity
    user.validate();

    // 5. Encode password
    user.passwordHash = await this.passwordEncoder.encode(request.password);

    // 6. Save user (repository interface, not implementation)
    const savedUser = await this.userRepository.save(user);

    // 7. Send welcome email
    await this.emailService.sendWelcomeEmail(savedUser);

    // 8. Return response
    return {
      id: savedUser.id,
      email: savedUser.email,
      name: savedUser.name,
    };
  }
}

module.exports = CreateUserUseCase;

// ============================================
// LAYER 2: INTERFACES (Repository abstraction)
// ============================================
// src/application/interfaces/UserRepository.js

class UserRepository {
  async findById(id) {
    throw new Error("Not implemented");
  }

  async findByEmail(email) {
    throw new Error("Not implemented");
  }

  async save(user) {
    throw new Error("Not implemented");
  }

  async update(user) {
    throw new Error("Not implemented");
  }

  async delete(id) {
    throw new Error("Not implemented");
  }
}

module.exports = UserRepository;

// ============================================
// LAYER 3: INTERFACE ADAPTERS (Repositories)
// ============================================
// src/infrastructure/repositories/MySQLUserRepository.js

const UserRepository = require("../../application/interfaces/UserRepository");
const { User } = require("../../domain/entities/User");

class MySQLUserRepository extends UserRepository {
  constructor(db) {
    super();
    this.db = db;
  }

  async findById(id) {
    const [rows] = await this.db.query("SELECT * FROM users WHERE id = ?", [
      id,
    ]);

    if (rows.length === 0) {
      return null;
    }

    return this._mapToEntity(rows[0]);
  }

  async findByEmail(email) {
    const [rows] = await this.db.query("SELECT * FROM users WHERE email = ?", [
      email,
    ]);

    if (rows.length === 0) {
      return null;
    }

    return this._mapToEntity(rows[0]);
  }

  async save(user) {
    const [result] = await this.db.query(
      "INSERT INTO users (email, name, password_hash) VALUES (?, ?, ?)",
      [user.email, user.name, user.passwordHash]
    );

    user.id = result.insertId;
    return user;
  }

  async update(user) {
    await this.db.query("UPDATE users SET email = ?, name = ? WHERE id = ?", [
      user.email,
      user.name,
      user.id,
    ]);

    return user;
  }

  async delete(id) {
    await this.db.query("DELETE FROM users WHERE id = ?", [id]);
  }

  _mapToEntity(row) {
    return new User(row.id, row.email, row.name, row.password_hash);
  }
}

module.exports = MySQLUserRepository;

// ============================================
// LAYER 3: CONTROLLERS
// ============================================
// src/presentation/controllers/UserController.js

class UserController {
  constructor(createUserUseCase) {
    this.createUserUseCase = createUserUseCase;
  }

  async create(req, res) {
    try {
      const result = await this.createUserUseCase.execute({
        email: req.body.email,
        name: req.body.name,
        password: req.body.password,
      });

      res.status(201).json({
        success: true,
        data: result,
      });
    } catch (error) {
      res.status(400).json({
        success: false,
        error: error.message,
      });
    }
  }
}

module.exports = UserController;

// ============================================
// LAYER 4: FRAMEWORKS & DRIVERS
// ============================================
// src/main/index.js

const express = require("express");
const mysql = require("mysql2/promise");
const bcrypt = require("bcryptjs");

// Setup database
const db = mysql.createPool({
  host: "localhost",
  user: "root",
  password: "password",
  database: "myapp",
});

// Import entities
const CreateUserUseCase = require("../application/usecases/CreateUserUseCase");
const MySQLUserRepository = require("../infrastructure/repositories/MySQLUserRepository");
const UserController = require("../presentation/controllers/UserController");

// Email service implementation
class NodemailerEmailService {
  async sendWelcomeEmail(user) {
    console.log(`Sending welcome email to ${user.email}`);
    // Actual email sending
  }
}

// Password encoder implementation
class BcryptPasswordEncoder {
  async encode(password) {
    return await bcrypt.hash(password, 10);
  }

  async matches(plain, hashed) {
    return await bcrypt.compare(plain, hashed);
  }
}

// Dependency Injection
const userRepository = new MySQLUserRepository(db);
const emailService = new NodemailerEmailService();
const passwordEncoder = new BcryptPasswordEncoder();

const createUserUseCase = new CreateUserUseCase(
  userRepository,
  emailService,
  passwordEncoder
);

const userController = new UserController(createUserUseCase);

// Express setup
const app = express();
app.use(express.json());

app.post("/users", (req, res) => userController.create(req, res));

app.listen(3000, () => {
  console.log("Server running on port 3000");
});

module.exports = app;

// ============================================
// TESTING (Example)
// ============================================
// tests/application/CreateUserUseCase.test.js

const CreateUserUseCase = require("../../src/application/usecases/CreateUserUseCase");

// Mock repositories
class MockUserRepository {
  constructor() {
    this.users = [];
  }

  async findByEmail(email) {
    return this.users.find((u) => u.email === email);
  }

  async save(user) {
    user.id = this.users.length + 1;
    this.users.push(user);
    return user;
  }
}

class MockEmailService {
  async sendWelcomeEmail(user) {
    // Do nothing
  }
}

class MockPasswordEncoder {
  async encode(password) {
    return `hashed_${password}`;
  }
}

describe("CreateUserUseCase", () => {
  let useCase;
  let userRepository;
  let emailService;
  let passwordEncoder;

  beforeEach(() => {
    userRepository = new MockUserRepository();
    emailService = new MockEmailService();
    passwordEncoder = new MockPasswordEncoder();

    useCase = new CreateUserUseCase(
      userRepository,
      emailService,
      passwordEncoder
    );
  });

  test("should create user successfully", async () => {
    const result = await useCase.execute({
      email: "test@example.com",
      name: "John Doe",
      password: "securepass123",
    });

    expect(result.id).toBeDefined();
    expect(result.email).toBe("test@example.com");
    expect(result.name).toBe("John Doe");
  });

  test("should reject duplicate email", async () => {
    await useCase.execute({
      email: "test@example.com",
      name: "John",
      password: "pass123",
    });

    await expect(
      useCase.execute({
        email: "test@example.com",
        name: "Jane",
        password: "pass456",
      })
    ).rejects.toThrow("Email already registered");
  });

  test("should reject invalid email", async () => {
    await expect(
      useCase.execute({
        email: "invalid",
        name: "John",
        password: "pass123",
      })
    ).rejects.toThrow("Invalid email format");
  });
});
```

---

## 🌍 Real World Examples

### Example 1: Enterprise Application (Large Scale)

```
Company uses Clean Architecture for:
- 100+ developers
- Multiple teams
- Microservices setup

Structure:
domain/
  ├── entities/
  │   ├── User.js
  │   ├── Order.js
  │   └── Product.js
  └── valueobjects/
      └── Money.js

application/
  ├── usecases/
  │   ├── CreateOrderUseCase.js
  │   ├── ProcessPaymentUseCase.js
  │   └── NotifyCustomerUseCase.js
  └── interfaces/
      ├── OrderRepository.js
      ├── PaymentService.js
      └── NotificationService.js

infrastructure/
  ├── repositories/
  │   └── PostgresOrderRepository.js
  ├── services/
  │   ├── StripePaymentService.js
  │   └── SESNotificationService.js
  └── http/
      └── ExpressAdapter.js

presentation/
  └── controllers/
      ├── OrderController.js
      └── ProductController.js

Result:
- Entities = 0 dependencies
- Use Cases = Depend on interfaces
- Repositories = Implement interfaces
- Controllers = Depend on use cases
- Can test everything independently
```

### Example 2: Startups (Quick Iteration)

```
Early stage startup using Clean Architecture benefits:

Month 1-3:
- Build core domain (Entities)
- Implement use cases
- Simple MySQL backend
- ExpressJS frontend

Month 4-6:
- Add new features (new use cases)
- Scale database (PostgreSQL)
- No changes to entity/use case code

Month 12:
- Migrate to microservices
- Use same entities/use cases
- Different Express instances
- Kafka for communication

Year 2:
- Add mobile app (React Native)
- Same use cases, different controller
- Same entities, different repositories
- Easy to share business logic
```

---

## 🛡️ Best Practices

### 1. Keep Entities Pure

```javascript
// ✅ GOOD: No external dependencies
class Product {
  constructor(id, name, price) {
    this.id = id;
    this.name = name;
    this.price = price;
  }

  getPriceWithTax(taxRate) {
    return this.price * (1 + taxRate);
  }
}

// ❌ BAD: Dependencies in entity
class Product {
  constructor(id, name, price, db) {
    this.id = id;
    this.name = name;
    this.price = price;
    this.db = db;
  }

  async save() {
    await this.db.insert(...); // ❌ Shouldn't know about DB!
  }
}
```

### 2. Use Interfaces for Dependencies

```javascript
// ✅ GOOD: Interface-based dependency
class UserService {
  constructor(userRepository, emailService) {
    this.userRepository = userRepository;
    this.emailService = emailService;
  }
}

// Can inject any implementation:
const mysqlRepo = new MySQLUserRepository();
const mongoRepo = new MongoUserRepository();
const smtpEmail = new SMTPEmailService();
const sendgridEmail = new SendGridEmailService();

// ❌ BAD: Hard-coded concrete dependencies
class UserService {
  constructor() {
    this.userRepository = new MySQLUserRepository();
    this.emailService = new SMTPEmailService();
  }
}
```

### 3. Dependency Injection Container

```javascript
// ✅ GOOD: Centralized DI
class Container {
  constructor() {
    this.services = {};
  }

  register(name, factory) {
    this.services[name] = factory;
  }

  get(name) {
    return this.services[name](this);
  }
}

const container = new Container();

// Register implementations
container.register("userRepository", () => new MySQLUserRepository(db));
container.register("emailService", () => new SMTPEmailService());
container.register(
  "createUserUseCase",
  (c) => new CreateUserUseCase(c.get("userRepository"), c.get("emailService"))
);

// Use
const useCase = container.get("createUserUseCase");
```

### 4. Value Objects

```javascript
// ✅ GOOD: Encapsulate domain concepts
class Email {
  constructor(value) {
    if (!Email.isValid(value)) {
      throw new InvalidEmailError("Invalid email");
    }
    this.value = value;
  }

  static isValid(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  }

  equals(other) {
    return this.value === other.value;
  }
}

class Money {
  constructor(amount, currency) {
    this.amount = amount;
    this.currency = currency;
  }

  add(other) {
    if (this.currency !== other.currency) {
      throw new Error("Currency mismatch");
    }
    return new Money(this.amount + other.amount, this.currency);
  }
}

// Usage
const email = new Email("test@example.com");
const price = new Money(99.99, "USD");
```

### 5. Use Case Naming

```javascript
// ✅ GOOD: Clear, action-oriented names
-CreateUserUseCase -
  GetUserByIdUseCase -
  UpdateUserProfileUseCase -
  DeleteUserUseCase -
  ProcessPaymentUseCase -
  CancelOrderUseCase -
  GenerateInvoiceUseCase -
  // ❌ BAD: Vague names
  UserOperation -
  HandleData -
  DoSomething -
  Process;
```

### 6. Error Handling

```javascript
// ✅ GOOD: Domain-specific exceptions
class DomainException extends Error {}
class UserAlreadyExistsException extends DomainException {}
class InvalidEmailException extends DomainException {}
class OrderNotFoundException extends DomainException {}

// Use cases throw domain exceptions
async execute(request) {
  const existing = await this.userRepository.findByEmail(request.email);
  if (existing) {
    throw new UserAlreadyExistsException();
  }
}

// Controllers catch and map to HTTP
try {
  const result = await useCase.execute(request);
  res.json(result);
} catch (error) {
  if (error instanceof UserAlreadyExistsException) {
    res.status(409).json({ error: 'User already exists' });
  } else {
    res.status(500).json({ error: 'Internal error' });
  }
}
```

---

## 📊 Comparison: Architecture Styles

| Aspect          | Layered   | MVC       | Clean       |
| --------------- | --------- | --------- | ----------- |
| **Complexity**  | Low       | Low       | High        |
| **Testability** | Good      | Fair      | Excellent   |
| **Scalability** | Medium    | Medium    | High        |
| **Framework**   | Dependent | Dependent | Independent |
| **Learning**    | Easy      | Easy      | Hard        |
| **Boilerplate** | Low       | Low       | High        |
| **Best For**    | SMEs      | Web Apps  | Enterprise  |
| **Refactoring** | Easy      | Easy      | Hard        |

---

## ✅ ตรวจสอบความเข้าใจ

**คำถามตัวเอง:**

1. ✓ Clean Architecture คืออะไร?
2. ✓ 4 layers คืออะไร?
3. ✓ Dependency Rule คืออะไร?
4. ✓ Entity vs Use Case ต่างกันอย่างไร?
5. ✓ ทำไมต้อง interface/abstraction?
6. ✓ ทำไม value objects สำคัญ?
7. ✓ เมื่อไดควรใช้ Clean Architecture?

---

## 📚 สรุป

### Key Takeaways

```
✅ Clean Architecture = Business logic at center
✅ Framework-agnostic, testable
✅ 4 layers with clear dependencies
✅ Entities = Pure business logic
✅ Use Cases = Application flows
✅ Adapters = Framework code
✅ All dependencies point inward
✅ Excellent for long-term projects
❌ Overkill for simple apps
❌ Requires experience to implement well
```

### เมื่อไดควรเลือก Clean Architecture

```
✅ USE IF:
- Long-term projects
- Large teams (10+)
- Complex business logic
- Need framework independence
- Testing critical
- Multiple platforms/clients
- Enterprise applications

❌ DON'T USE IF:
- MVP/Prototypes
- Small teams (<5)
- Simple CRUD
- Learning new tech
- Time to market critical
- Simple business logic
- No framework changes expected
```

---
