Saltar a contenido

ADR-0011 · Descomposición de servicios V1 (10 bounded contexts)

Revisión 2026-05-13: connectors-social suma la 5ª strategy V1 notifications-internal tras detectarse el canal en el legacy productivo (ver docs/discovery/08-legacy-delta.md §5 y §6.1, y ADR-0007). No cambia el conteo de servicios (sigue siendo 10 V1 + 1 stub V2); notifications-internal es una strategy adicional dentro del mismo Lambda connectors-social.

  • Status: Accepted
  • Date: 2026-04-30
  • Deciders: Cristian Fernández (Zerviz Group)
  • Related: ADR-0001..0010, docs/REPO_MAP.md.

Context

El monolito legacy concentra 30+ rutas en una sola Lambda. ADR-001 heredado y playbook §1.3 piden 6–10 servicios para V1 con bounded contexts DDD claros. Este ADR consolida la decomposición.

Decision

10 servicios V1, 1 stub V2 placeholder. Cada uno = un bounded context, una carpeta services/<name>/, su propia API GW HTTP API si expone HTTP, su propio role IAM y sus propios datastores (asignados en ADR-0003).

Servicios

# Servicio Contexto DDD Aggregate(s) Owns Eventos publicados Eventos consumidos
1 tenant-mgmt Plataforma Tenant, ConnectorRegistration, FeatureFlag RDS ze_control_plane, DDB ze-tenants TenantCreated, TenantSuspended, ConnectorEnabled, ConnectorDisabled
2 auth-bff Plataforma Session Cognito triggers UserAuthenticated, UserSignedOut TenantCreated
3 inbound-router Conversation InboundMessage DDB ze-dedup Conversation.MessageReceived
4 flow-engine Conversation ConversationFlow, FlowStep DDB ze-flow-state (+ Streams) Conversation.FlowStepCompleted, Conversation.FlowCompleted, Conversation.HandoffRequested Conversation.MessageReceived
5 connector-orchestrator Channel OutboundDispatch SSM (CB), DDB ze-circuit-breaker, DDB ze-outbound-idempotency Channel.OutboundDispatched, Channel.OutboundFailed Conversation.HandoffRequested, Conversation.FlowStepCompleted, Campaign.MessageReady
6 connectors-social Channel (strategies: whatsapp-meta-cloud, whatsapp-360dialog, webchat-internal, notifications-internal) Secrets Manager (per-tenant) + DDB zen-{env}-notification-flows (para strategy notifications) (publica via orchestrator) (invocado por orchestrator)
7 connectors-cc Channel Five9Session DDB ze-five9-sessions Channel.AgentAssigned, Channel.ConversationClosed Conversation.HandoffRequested
8 outbound-dispatcher Campaign Campaign, CampaignRun, Template RDS ze_app.campaigns, DDB ze-outbound-runs, Step Functions Express Campaign.Started, Campaign.Completed, Campaign.Failed, Campaign.MessageReady Channel.OutboundDispatched, Channel.OutboundFailed
9 reporting-api Reporting (read-model) RDS ze_app (read-only) + DDB ze-read-models (todos los eventos, materializa read-models)
10 encuesta-service Survey Survey, SurveyResponse DDB ze-encuesta-state (TTL 30 d), RDS ze_app.survey_responses Survey.Started, Survey.Completed, Survey.Abandoned Channel.ConversationClosed
(stub V2) voice-bridge Voice

Boundaries duros

  • Sólo tenant-mgmt escribe en ze_control_plane.
  • Sólo flow-engine escribe en ze-flow-state.
  • Sólo connector-orchestrator invoca a strategies (connectors-social, connectors-cc).
  • Sólo outbound-dispatcher orquesta campañas; connector-orchestrator ejecuta cada mensaje.
  • reporting-api es read-only sobre ze_app. Mutaciones via eventos consumidos para materializar read-models en DDB ze-read-models.

Comunicación inter-servicios

  • Asíncrona por defecto: EventBridge → SQS → consumer (ADR-0004).
  • Síncrona sólo cuando hay request-response semántico:
  • auth-bfftenant-mgmt (lookup tenant en pre-token-generation).
  • flow-engineconnector-orchestrator (request-reply para ejecutar paso bot que requiere envío inmediato).
  • SPA → tenant-mgmt, flow-engine (admin), reporting-api (queries).
  • Nunca un servicio lee directo la base de datos de otro.

Convención de paths HTTP por servicio

  • tenant-mgmt: /tenants/*, /connectors/*, /feature-flags/* (admin).
  • auth-bff: /auth/login, /auth/refresh, /auth/logout, /auth/me.
  • inbound-router: /webhook/{provider} (sin auth JWT, ver ADR-0005).
  • flow-engine: /flows/*, /flows/{flowId}/runs/*.
  • connector-orchestrator: /orchestrator/dispatch (interno SigV4).
  • outbound-dispatcher: /campaigns/*, /templates/*.
  • reporting-api: /reports/*.
  • encuesta-service: /surveys/*, /surveys/{surveyId}/responses/*.

Consequences

Positive

  • Bounded contexts claros = ownership clara, blast radius acotado.
  • 10 servicios cabe en el ADR-001 heredado y el playbook §1.3.
  • Cada servicio puede evolucionar (deprecar, dividir) sin romper a los demás siempre que respete el contrato de eventos.

Negative

  • 10 servicios > N microservicios para un equipo pequeño. Mitigado con services/_template y subagentes (node-service-builder, connector-strategist, etc.).
  • Latencia conversacional crece con saltos asíncronos. Mitigación: flow-engine ↔ connector-orchestrator puede ser sync request-reply.

Alternatives considered

  • 5 servicios bigger: rechazado, repite el problema del monolito a menor escala.
  • 20 servicios fine-grained: rechazado, ops overhead.
  • Modelo monolito modular en una Lambda grande: descartado por ADR-0001.

Tenant-isolation impact

  • Cada servicio aplica packages/tenant-context middleware.
  • tenant-mgmt valida y emite los tenant_id que los demás respetan.

Blast radius

  • Caída de un servicio no detiene a los demás (eventos quedan en SQS hasta procesar).
  • Excepción: auth-bff caído ⇒ no hay logins nuevos; tokens vigentes siguen funcionando hasta expirar.

Cost note

  • 10 Lambdas + 8 HTTP API + ~12 SQS + ~5 DDB tablas + 1 RDS shared. Dev: < USD 30/mes.

ISO 27001 controls touched

  • A.6.1.2 (segregation of duties): bounded contexts con ownership.
  • A.14.2.5 (secure system engineering): hexagonal por servicio.

Sources

  • ADRs 0001..0010.
  • ZEngine/ADR-001-ZEngine-Architecture.md (legacy ADR heredado, validación Bounded Contexts).
  • docs/discovery/04-as-is-report.md (pain points del monolito).
  • Playbook §1.3.