# ADR 002: Result Pattern Over Exceptions in Domain Layer

**Status**: Accepted
**Date**: 2024-10-12
**Context**: Crafted Level - Domain Layer Error Handling

## Context

The domain layer must handle errors without external dependencies. We need a way to represent success/failure that:
- Is explicit in the type system
- Forces error handling
- Doesn't rely on try-catch (side effects)
- Works well with functional composition

## Decision

We use the **Result pattern** for all domain operations:

```typescript
export type Result<T, E = Error> = Success<T> | Failure<E>;

export class Success<T> {
  readonly isSuccess = true;
  readonly isFailure = false;
  constructor(readonly value: T) {}
}

export class Failure<E = Error> {
  readonly isSuccess = false;
  readonly isFailure = true;
  constructor(readonly error: E) {}
}
```

### Usage Examples

```typescript
// Value Object
static create(email: string): Result<Email, EmailInvalidError> {
  if (!Email.isValid(email)) {
    return failure(new EmailInvalidError(email));
  }
  return success(new Email(email));
}

// Entity
publish(): Result<Product, ProductInvalidError> {
  if (this.status === 'published') {
    return failure(new ProductInvalidError('Already published'));
  }
  return success(new Product({ ...this.props, status: 'published' }));
}

// Use Case
async execute(input: CreateUserInput): Promise<Result<User, Error>> {
  const emailResult = Email.create(input.email);
  if (emailResult.isFailure) {
    return failure(emailResult.error);
  }

  const userResult = User.create({ ...input, email: emailResult.value });
  if (userResult.isFailure) {
    return failure(userResult.error);
  }

  return this.userRepository.save(userResult.value);
}
```

## Rationale

### Why Result Pattern?

1. **Type Safety**: Errors are part of the type signature
2. **Explicit**: Impossible to forget error handling (compile error)
3. **Pure**: No exceptions = no side effects in domain
4. **Composable**: Easy to chain operations
5. **Testable**: Clear success/failure paths

### Why NOT Exceptions?

```typescript
// ❌ Problems with exceptions in domain
class User {
  static create(name: string): User {
    if (!name) throw new Error('Name required'); // Side effect!
    return new User(name);
  }
}

// Caller has no idea this can throw
const user = User.create(''); // Runtime explosion
```

Problems:
- Not in type signature (invisible to type system)
- Can be forgotten (no compile-time safety)
- Side effects break purity
- Harder to compose

## Consequences

### Positive
- All errors are explicit and typed
- Impossible to forget error handling
- Domain layer remains pure
- Better IntelliSense/autocomplete
- Easier to test (no mocking try-catch)

### Negative
- More verbose than exceptions
- Requires checking `isSuccess`/`isFailure`
- Learning curve for developers used to exceptions

### Infrastructure Layer Exception

Infrastructure layer CAN throw exceptions because:
- External services might throw
- Database errors are exceptional
- Converts to Result at repository boundary

```typescript
// Infrastructure: exceptions OK
async save(user: User): Promise<Result<User, Error>> {
  try {
    const dbUser = await this.prisma.user.create({ ... });
    return success(this.toDomain(dbUser));
  } catch (error) {
    return failure(error as Error); // Convert to Result
  }
}
```

## Alternatives Considered

1. **Throwing Exceptions**
   - Rejected: Not explicit, side effects, hard to compose

2. **Either Monad (fp-ts)**
   - Too complex, large dependency
   - Our Result type is simpler and sufficient

3. **Never<T> Union Types**
   ```typescript
   type Result<T> = T | Error
   ```
   - Rejected: Can't distinguish success from error at type level

## References

- [Railway Oriented Programming](https://fsharpforfunandprofit.com/rop/)
- [Against Exceptions](https://www.joelonsoftware.com/2003/10/13/13/)
- [TypeScript Discriminated Unions](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions)
