# Clean Architecture Pattern

## Problem

Software systems accumulate coupling between business rules and delivery mechanisms (web frameworks, databases, UI). Over time, the framework "owns" the application — switching frameworks means rewriting business logic. Testing requires booting the entire infrastructure. The dependency graph points inward and outward indiscriminately.

## Solution

**Clean Architecture** (Robert C. Martin) organizes code into concentric layers with a strict **dependency rule**: source code dependencies must point inward only. Inner layers know nothing about outer layers.

- **Entities** (innermost) — Enterprise-wide business rules and objects.
- **Use Cases** — Application-specific business rules. Orchestrate data flow to/from entities.
- **Interface Adapters** — Convert data between the format most convenient for use cases and the format for external agencies (DB, web).
- **Frameworks & Drivers** (outermost) — Web frameworks, databases, UI. Glue code only.

## When to Use

- Large, long-lived applications where framework independence matters.
- Systems where business rules must be testable without any infrastructure.
- Teams that want enforced architectural boundaries.
- Applications expected to outlive their current framework or database choice.

## When to Avoid

- Small applications or microservices with minimal business logic.
- Prototypes or MVP where speed of delivery outweighs architectural purity.
- Teams unfamiliar with the pattern (steep learning curve for marginal benefit on small projects).

## Pseudocode

```
// Entities (innermost)
CLASS User:
    id, name, email

// Use Cases
INTERFACE UserRepository:   // defined in use case layer
    METHOD save(user)
    METHOD find(id) -> User

CLASS CreateUser:
    CONSTRUCTOR(repo: UserRepository)
    METHOD execute(name, email) -> User:
        user = User(name, email)
        VALIDATE user
        repo.save(user)
        RETURN user

// Interface Adapters
CLASS UserController:
    METHOD create(request):
        result = create_use_case.execute(request.name, request.email)
        RETURN present(result)

// Frameworks (outermost)
CLASS SQLUserRepository IMPLEMENTS UserRepository:
    METHOD save(user): INSERT INTO users ...
```

## Python

```python
from abc import ABC, abstractmethod
from dataclasses import dataclass


# =============================================
# LAYER 1: ENTITIES (innermost)
# =============================================

@dataclass
class User:
    id: int
    name: str
    email: str

    def validate(self) -> list[str]:
        errors = []
        if not self.name or len(self.name) < 2:
            errors.append("Name must be at least 2 characters")
        if "@" not in self.email:
            errors.append("Invalid email format")
        return errors


# =============================================
# LAYER 2: USE CASES
# =============================================

class UserRepository(ABC):
    """Port defined at the use case layer."""
    @abstractmethod
    def save(self, user: User) -> None: ...

    @abstractmethod
    def find_by_id(self, user_id: int) -> User | None: ...

    @abstractmethod
    def find_all(self) -> list[User]: ...


@dataclass
class CreateUserRequest:
    name: str
    email: str


@dataclass
class CreateUserResponse:
    user_id: int
    success: bool
    errors: list[str]


class CreateUserUseCase:
    def __init__(self, repo: UserRepository) -> None:
        self._repo = repo
        self._next_id = 1

    def execute(self, request: CreateUserRequest) -> CreateUserResponse:
        user = User(self._next_id, request.name, request.email)
        errors = user.validate()
        if errors:
            return CreateUserResponse(0, False, errors)
        self._repo.save(user)
        self._next_id += 1
        return CreateUserResponse(user.id, True, [])


class ListUsersUseCase:
    def __init__(self, repo: UserRepository) -> None:
        self._repo = repo

    def execute(self) -> list[User]:
        return self._repo.find_all()


# =============================================
# LAYER 3: INTERFACE ADAPTERS
# =============================================

class UserPresenter:
    @staticmethod
    def present_creation(response: CreateUserResponse) -> str:
        if response.success:
            return f"  User #{response.user_id} created successfully."
        return f"  Failed: {', '.join(response.errors)}"

    @staticmethod
    def present_list(users: list[User]) -> str:
        if not users:
            return "  No users found."
        lines = [f"  #{u.id} {u.name} <{u.email}>" for u in users]
        return "\n".join(lines)


class UserController:
    def __init__(self, create_uc: CreateUserUseCase, list_uc: ListUsersUseCase) -> None:
        self._create_uc = create_uc
        self._list_uc = list_uc

    def create(self, name: str, email: str) -> str:
        request = CreateUserRequest(name, email)
        response = self._create_uc.execute(request)
        return UserPresenter.present_creation(response)

    def list_all(self) -> str:
        users = self._list_uc.execute()
        return UserPresenter.present_list(users)


# =============================================
# LAYER 4: FRAMEWORKS & DRIVERS (outermost)
# =============================================

class InMemoryUserRepository(UserRepository):
    def __init__(self) -> None:
        self._store: dict[int, User] = {}

    def save(self, user: User) -> None:
        self._store[user.id] = user

    def find_by_id(self, user_id: int) -> User | None:
        return self._store.get(user_id)

    def find_all(self) -> list[User]:
        return list(self._store.values())


def main() -> None:
    # Composition root: wire everything together
    repo = InMemoryUserRepository()
    create_uc = CreateUserUseCase(repo)
    list_uc = ListUsersUseCase(repo)
    controller = UserController(create_uc, list_uc)

    print("--- Create valid users ---")
    print(controller.create("Alice", "alice@example.com"))
    print(controller.create("Bob", "bob@example.com"))

    print("\n--- Create invalid users ---")
    print(controller.create("", "bad"))
    print(controller.create("X", "no-at-sign"))

    print("\n--- List all users ---")
    print(controller.list_all())


if __name__ == "__main__":
    main()
```

**Output:**
```
--- Create valid users ---
  User #1 created successfully.
  User #2 created successfully.

--- Create invalid users ---
  Failed: Name must be at least 2 characters, Invalid email format
  Failed: Name must be at least 2 characters, Invalid email format

--- List all users ---
  #1 Alice <alice@example.com>
  #2 Bob <bob@example.com>
```

## JavaScript

```javascript
// =============================================
// LAYER 1: ENTITIES
// =============================================

class User {
  constructor(id, name, email) {
    this.id = id;
    this.name = name;
    this.email = email;
  }

  validate() {
    const errors = [];
    if (!this.name || this.name.length < 2) {
      errors.push("Name must be at least 2 characters");
    }
    if (!this.email.includes("@")) {
      errors.push("Invalid email format");
    }
    return errors;
  }
}

// =============================================
// LAYER 2: USE CASES
// =============================================

class CreateUserUseCase {
  constructor(repo) {
    this._repo = repo;
    this._nextId = 1;
  }

  execute(name, email) {
    const user = new User(this._nextId, name, email);
    const errors = user.validate();
    if (errors.length > 0) {
      return { userId: 0, success: false, errors };
    }
    this._repo.save(user);
    this._nextId++;
    return { userId: user.id, success: true, errors: [] };
  }
}

class ListUsersUseCase {
  constructor(repo) {
    this._repo = repo;
  }

  execute() {
    return this._repo.findAll();
  }
}

// =============================================
// LAYER 3: INTERFACE ADAPTERS
// =============================================

class UserPresenter {
  static presentCreation(response) {
    if (response.success) {
      return `  User #${response.userId} created successfully.`;
    }
    return `  Failed: ${response.errors.join(", ")}`;
  }

  static presentList(users) {
    if (users.length === 0) return "  No users found.";
    return users.map((u) => `  #${u.id} ${u.name} <${u.email}>`).join("\n");
  }
}

class UserController {
  constructor(createUC, listUC) {
    this._createUC = createUC;
    this._listUC = listUC;
  }

  create(name, email) {
    const response = this._createUC.execute(name, email);
    return UserPresenter.presentCreation(response);
  }

  listAll() {
    const users = this._listUC.execute();
    return UserPresenter.presentList(users);
  }
}

// =============================================
// LAYER 4: FRAMEWORKS & DRIVERS
// =============================================

class InMemoryUserRepository {
  constructor() {
    this._store = new Map();
  }

  save(user) {
    this._store.set(user.id, user);
  }

  findById(id) {
    return this._store.get(id) || null;
  }

  findAll() {
    return Array.from(this._store.values());
  }
}

// --- Main ---
const repo = new InMemoryUserRepository();
const createUC = new CreateUserUseCase(repo);
const listUC = new ListUsersUseCase(repo);
const controller = new UserController(createUC, listUC);

console.log("--- Create valid users ---");
console.log(controller.create("Alice", "alice@example.com"));
console.log(controller.create("Bob", "bob@example.com"));

console.log("\n--- Create invalid users ---");
console.log(controller.create("", "bad"));
console.log(controller.create("X", "no-at-sign"));

console.log("\n--- List all users ---");
console.log(controller.listAll());
```

**Output:**
```
--- Create valid users ---
  User #1 created successfully.
  User #2 created successfully.

--- Create invalid users ---
  Failed: Name must be at least 2 characters, Invalid email format
  Failed: Name must be at least 2 characters, Invalid email format

--- List all users ---
  #1 Alice <alice@example.com>
  #2 Bob <bob@example.com>
```

## Related Patterns

- **Hexagonal Architecture** — hexagonal architecture (Ports & Adapters) shares the same dependency inversion goals; clean architecture adds concentric layer names.
- **Dependency Inversion Principle** — DIP is the theoretical foundation; clean architecture is its architectural expression.
- **Repository** — repositories are the canonical adapter at the data access boundary in clean architecture.
- **DTO** — DTOs carry data across layer boundaries without exposing domain objects to outer layers.
