System Design
Estados y ciclos de vida
Explica cómo modelar estados, transiciones, invariantes, eventos y operaciones válidas para representar correctamente el ciclo de vida de entidades y procesos.
- Última actualización
- Actualizada
- Nivel
- Aplicación
System Design
Explica cómo modelar estados, transiciones, invariantes, eventos y operaciones válidas para representar correctamente el ciclo de vida de entidades y procesos.
Un estado representa una condición relevante y persistente del ciclo de vida de una entidad. Diseñar estados significa definir qué hechos son válidos, qué eventos permiten cambiar y qué garantías deben conservarse durante cada transición.
Una entidad no es solo una fila con datos; muchas veces atraviesa fases con reglas diferentes.
estado actual + evento + guard
→ transición
→ nuevo estado + efectosEjemplo:
confirmed + startPreparation [stock reservado]
→ preparing
→ registrar inicio y responsableEl estado actual condiciona qué operaciones son legales. Un buen modelo evita combinaciones imposibles y hace explícitos los puntos de no retorno.
Sin un ciclo de vida definido, las aplicaciones acumulan booleanos y condiciones dispersas:
isPaid = true
isCancelled = true
isDelivered = trueLa combinación puede ser contradictoria, pero la base la permite y cada pantalla interpreta distinto.
Un estado explícito permite razonar sobre:
Condición que permanece y afecta comportamiento: pending, confirmed, cancelled.
Intención de producir cambio: ConfirmOrder.
Hecho ocurrido: OrderConfirmed.
Condición que permite transición: stock reservado y pago válido.
Cambio asociado: liberar inventario, registrar fecha o publicar evento.
Confundir comando y evento lleva a nombrar como hecho algo que todavía puede ser rechazado.
máquina de estados
├─ conjunto finito de estados relevantes
├─ eventos que solicitan transición
├─ guards que protegen reglas
├─ efectos atómicos o coordinados
└─ historial que explica cómo se llegóNo todo campo cambiante merece convertirse en estado. Debe afectar reglas, operaciones o interpretación del ciclo de vida.
| Desde | Evento | Guard | Hacia | Efectos |
|---|---|---|---|---|
| draft | confirm | items \> 0 y reserva válida | confirmed | capturar precios y registrar confirmación |
| confirmed | startPreparation | sucursal operativa | preparing | registrar responsable |
| preparing | completePreparation | todos los items resueltos | ready | notificar logística |
| confirmed | cancel | autorizado y no despachado | cancelled | liberar reserva y auditar |
| dispatched | confirmDelivery | evidencia válida | delivered | cerrar entrega |
La tabla suele ser mejor artefacto de trabajo que un enum aislado porque hace visibles guards y efectos.
Es fuente de verdad y representa una decisión de negocio.
Se calcula a partir de hechos:
isOverdue = dueAt < now && status not in [paid, cancelled]Guardar ambos puede generar contradicción. Persiste un derivado solo si necesitas snapshot histórico, rendimiento medido o independencia de fuentes; define sincronización.
Una entidad puede tener dimensiones independientes:
orderStatus: confirmed
paymentStatus: approved
fulfillmentStatus: preparingForzar todo en un enum produce combinaciones explosivas como confirmed_paid_preparing. Separa máquinas cuando evolucionan por eventos distintos, pero define invariantes entre ellas.
Un estado terminal no siempre significa que no habrá más procesos. delivered puede habilitar devolución. Quizá el pedido termina, pero comienza otro agregado o workflow.
Identifica puntos de no retorno y transiciones compensatorias en lugar de permitir volver arbitrariamente.
draft
→ requested
→ approved
→ in_transit
→ receivedAlternativas:
requested → rejected
requested → cancelled
approved → cancelled [antes de despacho]
in_transit → exceptionReglas:
El modelo obliga a decidir qué ocurre con pérdida, recepción parcial y cancelación tardía.
Una transición crítica debe coordinar estado y efectos que forman una misma invariante.
confirmed → cancelled
+ liberar reserva una sola vez
+ registrar motivoSi el estado cambia pero el inventario no, queda inconsistencia. Cuando efectos externos no caben en la misma transacción, utiliza outbox, estado intermedio y reconciliación.
Repetir un evento ya aplicado no debe duplicar efectos.
Opciones:
La semántica depende del contrato. cancel repetido puede devolver cancelled, pero nunca liberar stock dos veces.
Dos comandos pueden competir:
cancelOrder
vs
startPreparationAmbos leen confirmed. Solo uno debe ganar según versión o lock. Usa:
La máquina define la regla; la implementación protege atomicidad.
Guardar solo estado actual responde “qué es”, pero no “cómo llegó”. Para auditoría o debugging registra:
Puede ser tabla de transiciones, eventos de dominio o log auditable. No todo sistema necesita event sourcing.
Los flujos distribuidos necesitan estados como:
payment_pending.cancellation_pending.delivery_exception.No los uses como cajón genérico. Define owner, timeout, acciones permitidas y cómo salen.
Una reserva puede expirar automáticamente:
reserved + expire [now >= expiresAt]
→ expired
→ liberar unidadesDefine zona horaria, job responsable, idempotencia y comportamiento si la expiración llega tarde.
No apliques transición inválida. Guarda para reconciliar o descarta con evidencia.
Comprueba versión y estado antes de cambiar.
Necesitan migración o compatibilidad explícita.
Es demasiado genérico. Distingue fallo terminal, reintentable y pendiente de intervención.
Crea compensación o nuevo proceso; no “retrocedas” como si nada hubiera ocurrido.
Quizá el agregado necesita estado derivado, subentidades o procesamiento parcial.
Borrar una entidad no siempre es estado; decide retención, auditoría y significado.
Permiten combinaciones imposibles.
processingPayment puede ser estado temporal válido, pero clickingButton no pertenece al dominio.
Elimina invariantes.
Cancelar cambia estado, pero nadie libera recursos.
No explica qué espera ni quién lo resuelve.
Separa dimensiones o bounded contexts.
Otros canales pueden violarlo.
Las carreras sobrescriben decisiones.
Property-based testing puede generar secuencias aleatorias y verificar que nunca aparece un estado imposible.
Aporta cuando comportamiento cambia por fase, existen múltiples transiciones, auditoría o flujos largos. Para un recurso CRUD con active/inactive simple, un enum y validación pueden bastar.
pending?Diagramas de estados visualiza esta tabla y permite revisar caminos, guards y estados terminales.