Saltar a contenido

ADR-0014 — Extensiones del Strategy Pattern para conectores V1.1

  • Status: Accepted
  • Date: 2026-05-16
  • Deciders: solution-architect, connector-strategist
  • Related: ADR-0007 (Strategy pattern para conectores), ADR-0010 (Secretos y KMS)

Context

V1.1 introduce 3 nuevos conectores: WhatsApp 360dialog (BSP alternativo a Meta Cloud), Notifications (Email SES + SMS SNS) y Amazon Connect Chat (handoff alternativo a Five9). Estos casos exponen patterns que no estaban cubiertos por ADR-0007:

  1. Strategies que usan AWS SDK clients (SES, SNS, Connect) en vez de fetch HTTP plano. Hay que poder mockearlos en unit tests sin agregar aws-sdk-client-mock a cada workspace.
  2. Idempotencia delegada al proveedor cuando el proveedor lo soporta nativamente (Amazon Connect ClientToken, 360dialog X-Idempotency-Key).
  3. Multi-strategy por canal (whatsapp puede resolverse a meta-cloud o 360dialog según strategyHint).
  4. Mismo CcStrategy interface compartido entre proveedores con secrets heterogéneos (Five9Secret vs AmazonConnectSecret).

Decision

D1. Dependency injection del SDK client

Cada strategy que use AWS SDK acepta un segundo argumento opts: { client? }. Default: lazy-init de un cliente module-scoped (warm container reuse). Tests inyectan un mock con { send: jest.fn() }. Esto evita agregar aws-sdk-client-mock como dependencia transversal del monorepo y mantiene los tests deterministas (no dependen del orden de registración de mocks).

export const emailSesSender = {
  id: 'email-ses',
  version: '1.0.0',
  async send(payload, secret, opts: { client?: SESv2Client } = {}) {
    const client = opts.client ?? getDefaultClient();
    // ...
  },
};

D2. Idempotency keys end-to-end

  • OutboundPayload.idempotencyKey?: string se propaga al strategy.
  • Strategies con header de idempotencia provider-native lo reenvían (X-Idempotency-Key para 360dialog).
  • Strategies con ClientToken propio (Amazon Connect) lo derivan determinísticamente de tenantId#conversationId con SHA-256, de modo que dos retries del mismo handoff producen el mismo token.

D3. Multi-strategy por canal

connector-orchestrator.resolveStrategy() lee req.strategyHint cuando hay >1 strategy disponible para el mismo channel. Ej:

  • channel=whatsapp, hint vacío → whatsapp-meta-cloud (default)
  • channel=whatsapp, hint=whatsapp-360dialog → 360dialog

Cuando V1.2 migremos el registry a DynamoDB por-tenant, el hint pasa a ser sólo override; la fuente de verdad es la tabla zen-{env}-tenant-channels.

D4. CcStrategy<TSecret> genérico

CcStrategy antes era específico de Five9. Se generaliza:

export interface CcStrategy<TSecret = Five9Secret> {
  readonly id: string;
  readonly version: string;
  createInteraction(req: HandoffRequest, secret: TSecret): Promise<CreateInteractionResult>;
}

Default es Five9Secret por compatibilidad con código y tests existentes. El handler usa cast a CcStrategy<unknown> y resuelve secret + strategy juntos por target.

D5. Secret schemas (declarados en Terraform como placeholders)

Strategy Secret path Shape
whatsapp-360dialog /zen/dev/connectors/360dialog/example { apiKey, templateNamespace?, apiVersion? }
360dialog webhook /zen/dev/webhook-secrets/whatsapp-360dialog string (HMAC secret)
email-ses /zen/dev/connectors/email-ses/example { fromAddress, configurationSetName?, replyToAddresses? }
sms-sns /zen/dev/connectors/sms-sns/example { senderId?, smsType? }
amazon-connect /zen/dev/connectors/amazon-connect/example { instanceId, contactFlowId }

ASSUMPTION: 360dialog usa HMAC-SHA256 con header X-Hub-Signature-256 igual a Meta. Confirmar contra docs partner (link a verificar en PR description).

Consequences

  • (+) Strategies AWS-SDK testeables sin lib extra.
  • (+) Idempotencia end-to-end consistente.
  • (+) Multi-strategy por canal abre la puerta a A/B testing entre BSPs.
  • (–) Cada strategy nueva debe definir su shape de secret y agregar IAM en TF.
  • (–) Los caps de connect:StartChatContact requieren resources="*" para el instance arn pattern; mitigado con SCP a nivel cuenta (pendiente Phase 3).