# ADR-007: Service Classification (Facades, Orchestrators, Aggregators)

**Date**: 2025-01-15
**Status**: Accepted

## Context

The BFF layer communicates with multiple external systems (WHMCS, Salesforce, Freebit, JapanPost) and coordinates complex workflows. Without clear naming conventions, services become:

- Difficult to understand at a glance
- Hard to know what they're responsible for
- Inconsistent in their patterns

Many "services" were actually orchestrating multiple systems but named generically (e.g., `SalesforceService`, `FreebitOperationsService`).

## Decision

**Classify services by their architectural role** using clear naming conventions:

| Type             | Suffix          | Purpose                                          | Dependencies                              |
| ---------------- | --------------- | ------------------------------------------------ | ----------------------------------------- |
| **Facade**       | `*Facade`       | Unified entry point for an integration subsystem | Multiple services within same integration |
| **Orchestrator** | `*Orchestrator` | Coordinate workflows across multiple systems     | Multiple services/integrations            |
| **Aggregator**   | `*Aggregator`   | Combine read-only data from multiple sources     | Multiple services (read-only)             |
| **Service**      | `*Service`      | Single-responsibility operations                 | 1-2 integrations max                      |

## Rationale

### Why Facades?

Integration modules (WHMCS, Salesforce, Freebit) contain multiple internal services. A **Facade** provides:

- **Single entry point**: Consumers don't need to know internal structure
- **Consistent interface**: Unified error handling, logging, queueing
- **Encapsulation**: Internal service changes don't affect consumers

```typescript
// ✅ GOOD: Facade abstracts internal complexity
@Injectable()
export class WhmcsConnectionFacade {
  // Single entry point for ALL WHMCS API operations
  // Handles queueing, error handling, request prioritization
}

// Consumers use the facade
constructor(private readonly whmcs: WhmcsConnectionFacade) {}
```

### Why Orchestrators?

Complex workflows span multiple systems. An **Orchestrator**:

- Coordinates multi-step operations
- Manages transaction boundaries
- Handles cross-system error recovery

```typescript
// ✅ GOOD: Orchestrator for cross-system workflow
@Injectable()
export class OrderFulfillmentOrchestrator {
  constructor(
    private readonly salesforce: SalesforceFacade,
    private readonly whmcs: WhmcsOrderService,
    private readonly freebit: FreebitFacade
  ) {}

  async executeFulfillment(orderId: string) {
    // Coordinates SF → WHMCS → Freebit workflow
  }
}
```

### Why Aggregators?

Dashboard and profile endpoints combine data from multiple sources. An **Aggregator**:

- Is **read-only** (no mutations)
- Combines data from multiple services
- Handles partial failures gracefully

```typescript
// ✅ GOOD: Aggregator for read-only data composition
@Injectable()
export class MeStatusAggregator {
  constructor(
    private readonly users: UsersService,
    private readonly orders: OrderOrchestrator,
    private readonly payments: WhmcsPaymentService
  ) {}

  async getStatusForUser(userId: string): Promise<MeStatus> {
    // Combines user, order, payment data for dashboard
  }
}
```

### Alternatives Considered

| Approach                                        | Pros                   | Cons                                   |
| ----------------------------------------------- | ---------------------- | -------------------------------------- |
| **Generic `*Service`**                          | Familiar               | No architectural clarity               |
| **Layer-based (Repository/Service/Controller)** | Simple                 | Doesn't capture orchestration patterns |
| **Role-based naming**                           | Clear responsibilities | More files, learning curve             |

## Consequences

### Positive

- Clear responsibilities from class name
- Easier onboarding (developers know what each type does)
- Consistent patterns across modules
- Better testability (mock entire facades)

### Negative

- Refactoring existing services required
- Developers must learn classification rules
- More specific naming conventions to follow

## Implementation

### Integration Facades

Located in `integrations/{provider}/facades/`:

```
integrations/
├── whmcs/
│   ├── facades/
│   │   └── whmcs.facade.ts        # WhmcsConnectionFacade
│   └── services/                   # Internal services
├── salesforce/
│   ├── facades/
│   │   └── salesforce.facade.ts   # SalesforceFacade
│   └── services/
└── freebit/
    ├── facades/
    │   └── freebit.facade.ts      # FreebitFacade
    └── services/
```

### Module Orchestrators

Located in module services or dedicated orchestrators folder:

```typescript
// modules/orders/services/order-fulfillment-orchestrator.service.ts
@Injectable()
export class OrderFulfillmentOrchestrator {}

// modules/subscriptions/sim-management/services/sim-orchestrator.service.ts
@Injectable()
export class SimOrchestrator {}
```

### Aggregators

Currently in their modules, planned for dedicated `aggregators/` folder:

```typescript
// modules/me-status/me-status.service.ts
@Injectable()
export class MeStatusAggregator {}

// modules/users/infra/user-profile.service.ts
@Injectable()
export class UserProfileAggregator {}
```

### Dependency Rules

```
┌─────────────────────────────────────────────────────────────────┐
│                     ALLOWED IMPORTS                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Controllers                                                     │
│      ↓ (can import)                                              │
│  Orchestrators / Aggregators / Facades                           │
│      ↓ (can import)                                              │
│  Services                                                        │
│      ↓ (can import)                                              │
│  Integration Facades                                             │
│      ↓ (can import)                                              │
│  Integration Entity Services                                     │
│                                                                  │
├─────────────────────────────────────────────────────────────────┤
│                     FORBIDDEN                                    │
│                                                                  │
│  ✗ Controllers → Integration Services (use facades)              │
│  ✗ Services → Orchestrators (wrong direction)                    │
│  ✗ Aggregators → Mutation methods                                │
│  ✗ Integration Services → Module Services                        │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
```

## Naming Conventions

### DO

```typescript
// Integration entry points
export class WhmcsConnectionFacade {}
export class SalesforceFacade {}
export class FreebitFacade {}

// Cross-system workflows
export class OrderFulfillmentOrchestrator {}
export class SimOrchestrator {}

// Read-only data composition
export class MeStatusAggregator {}
export class UserProfileAggregator {}

// Single-responsibility operations
export class WhmcsInvoiceService {}
export class SalesforceOrderService {}
```

### DON'T

```typescript
// ❌ Generic names that hide complexity
export class SalesforceService {} // What does it do?
export class FreebitOperationsService {} // Operations = everything?
export class MeStatusService {} // Service doing aggregation

// ❌ Orchestrators named as services
export class SimOrchestratorService {} // Drop the Service suffix
```

## Facade vs Orchestrator: Key Differences

A common source of confusion is when to use a **Facade** vs an **Orchestrator**. Here's the distinction:

| Aspect               | Facade                                 | Orchestrator                   |
| -------------------- | -------------------------------------- | ------------------------------ |
| **Scope**            | Single integration (WHMCS, Salesforce) | Multiple integrations/systems  |
| **Purpose**          | Abstract internal complexity           | Coordinate workflows           |
| **Dependency**       | Owns internal services                 | Consumes facades and services  |
| **Mutation Pattern** | Direct API calls                       | Transaction coordination       |
| **Error Handling**   | System-specific errors                 | Cross-system rollback/recovery |
| **Example**          | `WhmcsConnectionFacade`                | `OrderFulfillmentOrchestrator` |

### When to Use Each

**Use a Facade when:**

- You're building an entry point for a single external system
- You want to encapsulate multiple services within one integration
- Consumers shouldn't know about internal service structure

**Use an Orchestrator when:**

- You're coordinating operations across multiple systems
- You need distributed transaction patterns
- Failure in one system requires compensation in another

## When to Split an Orchestrator

**Guideline: Consider splitting when an orchestrator exceeds ~300 lines.**

Signs an orchestrator needs refactoring:

1. **Too many responsibilities**: Validation + transformation + execution + side effects
2. **Difficult to test**: Mocking 10+ dependencies
3. **Long methods**: Single methods exceeding 100 lines
4. **Mixed concerns**: Business logic mixed with infrastructure

### Extraction Patterns

Extract concerns into specialized services:

```typescript
// BEFORE: Monolithic orchestrator
@Injectable()
export class OrderFulfillmentOrchestrator {
  // 700 lines doing everything
}

// AFTER: Focused orchestrator with extracted services
@Injectable()
export class OrderFulfillmentOrchestrator {
  constructor(
    private readonly stepTracker: WorkflowStepTrackerService,
    private readonly sideEffects: FulfillmentSideEffectsService,
    private readonly validator: OrderFulfillmentValidator
  ) {}
  // ~200 lines of pure orchestration
}
```

**Common extractions:**

- **Step tracking** → `WorkflowStepTrackerService` (infrastructure)
- **Side effects** → `*SideEffectsService` (events, notifications, cache)
- **Validation** → `*Validator` (composable validators)
- **Error handling** → `*ErrorService` (error classification, recovery)

## File Naming Conventions

### Services

| Type         | File Name Pattern                  | Class Name Pattern     |
| ------------ | ---------------------------------- | ---------------------- |
| Facade       | `{domain}.facade.ts`               | `{Domain}Facade`       |
| Orchestrator | `{domain}-orchestrator.service.ts` | `{Domain}Orchestrator` |
| Aggregator   | `{domain}.aggregator.ts`           | `{Domain}Aggregator`   |
| Service      | `{domain}.service.ts`              | `{Domain}Service`      |
| Validator    | `{domain}.validator.ts`            | `{Domain}Validator`    |

### Examples

```
# Integration facades
integrations/whmcs/facades/whmcs.facade.ts          → WhmcsConnectionFacade
integrations/salesforce/facades/salesforce.facade.ts → SalesforceFacade

# Module orchestrators
modules/orders/services/order-fulfillment-orchestrator.service.ts → OrderFulfillmentOrchestrator
modules/auth/application/auth-orchestrator.service.ts            → AuthOrchestrator

# Aggregators
modules/me-status/me-status.aggregator.ts           → MeStatusAggregator
modules/users/infra/user-profile.service.ts         → UserProfileAggregator

# Validators
modules/orders/validators/internet-order.validator.ts → InternetOrderValidator
modules/orders/validators/sim-order.validator.ts      → SimOrderValidator
```

### File vs Class Naming

- **File names**: Use kebab-case with `.service.ts`, `.facade.ts`, `.aggregator.ts` suffix
- **Class names**: Use PascalCase without `Service` suffix for Orchestrators/Aggregators

```typescript
// ✅ GOOD
// File: order-fulfillment-orchestrator.service.ts
export class OrderFulfillmentOrchestrator {}

// File: me-status.aggregator.ts
export class MeStatusAggregator {}

// ❌ AVOID
// File: order-fulfillment-orchestrator.ts (missing .service)
export class OrderFulfillmentOrchestratorService {} // Don't add Service suffix
```

## Related

- [ADR-006: Thin Controllers](./006-thin-controllers.md)
- [BFF Integration Patterns](../development/bff/integration-patterns.md)
