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:
- Strategies que usan AWS SDK clients (SES, SNS, Connect) en vez de
fetchHTTP plano. Hay que poder mockearlos en unit tests sin agregaraws-sdk-client-mocka cada workspace. - Idempotencia delegada al proveedor cuando el proveedor lo soporta
nativamente (Amazon Connect
ClientToken, 360dialogX-Idempotency-Key). - Multi-strategy por canal (
whatsapppuede resolverse ameta-cloudo360dialogsegúnstrategyHint). - Mismo
CcStrategyinterface 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?: stringse propaga al strategy.- Strategies con header de idempotencia provider-native lo reenvían
(
X-Idempotency-Keypara 360dialog). - Strategies con
ClientTokenpropio (Amazon Connect) lo derivan determinísticamente detenantId#conversationIdcon 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:StartChatContactrequierenresources="*"para el instance arn pattern; mitigado con SCP a nivel cuenta (pendiente Phase 3).