Many engineering organizations attempt to escape monolithic friction by decomposing their entire system into dozens of microservices simultaneously. Without clear domain boundaries, this merely converts in-process function calls into fragile, high-latency HTTP or gRPC network requests with distributed transaction complexity.

## Domain-Driven Boundary Identification

SazM approaches decomposition by profiling **change frequency** and **operational contention**:

1. **High Friction / High Velocity**: Services that deploy multiple times daily and suffer deployment queue bottlenecks (e.g. Lead Intake, User Onboarding).
2. **High Contention / High Latency**: Services that hold long-running database locks or interface with unstable third-party APIs.
3. **Stable Core**: Core accounting or ledger tables that change rarely and operate reliably within the monolith.

## The Anti-Corruption Layer (ACL)

When modern services must read or write data shared with the legacy monolith, an Anti-Corruption Layer translates between domain models:

```typescript
export function adaptLegacyUserData(legacyRow: Record<string, any>): CanonicalUser {
  return {
    id: legacyRow.vch_user_uuid || legacyRow.int_user_id.toString(),
    email: legacyRow.txt_email_address.trim().toLowerCase(),
    tier: legacyRow.int_status === 1 ? 'enterprise' : 'standard',
    createdAt: new Date(legacyRow.dt_created).toISOString(),
  };
}
```

By isolating the modern schema from legacy naming idiosyncrasies, the modern system remains clean and verifiable.

## Event-Driven Decoupling via Transaction Outbox

Instead of cross-database transactions, publish domain events to an Outbox table:

```sql
BEGIN TRANSACTION;
UPDATE orders SET status = 'shipped' WHERE id = ?;
INSERT INTO domain_events (id, topic, payload, created_at)
VALUES (uuid(), 'order.shipped', json_object('order_id', ?), datetime('now'));
COMMIT;
```

A background worker polls the outbox and streams events to downstream services with guaranteed delivery.
