Un invariante del dominio es una propiedad que siempre debe cumplirse para que el modelo permanezca válido. No es una regla ocasional ni una preferencia de interfaz. Es una condición que protege la coherencia del negocio.
Por ejemplo, una franja horaria no puede tener hora de fin anterior a la hora de inicio. Un pedido confirmado debe tener al menos un ítem. Un turno reservado debe estar asociado a un paciente. Si una de estas condiciones se rompe, el sistema queda en un estado inválido desde el punto de vista del dominio.
Podemos definir un invariante de la siguiente manera:
La palabra "siempre" es importante. Si una regla solo se aplica en algunos casos, estados o contextos, quizá sea una condición de negocio, pero no necesariamente un invariante general. El análisis debe precisar cuándo la propiedad debe cumplirse y qué alcance tiene.
Todo invariante es una regla de negocio, pero no toda regla de negocio es un invariante. Una regla puede indicar una política, una decisión o un cálculo. Un invariante, en cambio, protege la validez estructural o lógica del dominio.
Por ejemplo, "una cancelación fuera de término registra una inasistencia" es una regla de decisión. "Un turno no puede estar reservado por dos pacientes al mismo tiempo" es un invariante de consistencia.
Las entidades suelen tener invariantes relacionados con identidad, estados y relaciones obligatorias. Un Paciente debe tener una identidad válida. Un Turno reservado debe tener una franja horaria y un paciente. Una Factura emitida debe tener número, fecha e importe total válido.
Cuando una entidad cambia, sus invariantes deben seguir cumpliéndose. Si una operación deja a la entidad en un estado imposible, el modelo está permitiendo una modificación inválida.
Los objetos de valor también pueden tener invariantes. Un Rango de fechas debe tener fecha de inicio menor o igual a fecha de fin. Un Porcentaje puede estar entre 0 y 100. Un importe de Dinero debe tener monto y moneda, y en ciertos contextos puede no permitir valores negativos.
Estos invariantes suelen ser muy importantes porque evitan que valores inválidos se propaguen por el modelo. Si un objeto de valor solo puede crearse en estado válido, el resto del dominio trabaja con más confianza.
Algunas propiedades siempre deben cumplirse entre conceptos asociados. Por ejemplo, una Agenda no debe tener franjas horarias superpuestas. Un Profesional no puede atender una Especialidad para la cual no está habilitado. Un Turno no puede pertenecer a una Agenda que no corresponde al Profesional seleccionado.
Estas reglas involucran más de un concepto, por lo que requieren especial atención. Si se distribuyen sin claridad, pueden terminar implementadas de forma incompleta o contradictoria.
Algunos invariantes dependen del estado de una entidad. Por ejemplo, un Turno disponible puede no tener Paciente asociado, pero un Turno reservado sí debe tenerlo. Una Agenda en borrador puede estar incompleta, pero una Agenda publicada debe tener al menos una franja válida.
En estos casos, el invariante no se expresa sobre todos los estados por igual, sino sobre estados específicos. Por eso conviene formularlo con precisión: "Todo turno en estado reservado debe tener un paciente asociado".
Algunos invariantes tienen una dimensión temporal. Por ejemplo, dos turnos del mismo profesional no pueden ocupar el mismo horario. Una cobertura debe estar vigente en la fecha del turno. Una promoción no puede aplicarse fuera de su período de validez.
Las reglas temporales deben definir con claridad qué fecha se toma como referencia: fecha de creación, fecha del turno, fecha de pago, fecha actual del sistema o fecha de vigencia administrativa.
Los invariantes definen límites de consistencia. Si una operación modifica datos relacionados, el modelo debe garantizar que al finalizar la operación las propiedades importantes sigan siendo verdaderas.
Por ejemplo, al reservar un turno, no alcanza con asociar un paciente. También debe verificarse que la franja esté disponible, que no exista otra reserva incompatible y que el estado resultante sea coherente. El invariante protege la consistencia del conjunto.
Supongamos una Agenda con varias Franjas horarias. Un invariante podría ser:
Esta regla debe cumplirse al crear una agenda, al agregar una franja, al modificar horarios y al publicar la agenda. Si el sistema permite superposición, luego podrían aparecer dos turnos disponibles para el mismo profesional en el mismo momento.
En un sistema de ventas, un invariante podría ser:
Esto no significa que un pedido en borrador deba cumplir exactamente las mismas condiciones. El invariante puede estar asociado al estado confirmado. La formulación precisa evita aplicar reglas demasiado pronto o demasiado tarde.
Una validación superficial puede revisar un campo en una pantalla. Un invariante del dominio protege una propiedad esencial del modelo, sin importar desde dónde llegue la operación. Si una reserva puede crearse desde una web, una aplicación móvil o una importación masiva, el invariante debe cumplirse en todos esos caminos.
Por eso, los invariantes no deberían depender solo de la interfaz. Deben formar parte del conocimiento central del dominio y quedar visibles en el análisis.
La siguiente tabla muestra invariantes posibles:
| Invariante | Conceptos involucrados | Qué protege |
|---|---|---|
| Una franja horaria debe terminar después de comenzar. | Franja horaria | Validez temporal básica. |
| Un turno reservado debe tener paciente. | Turno, Paciente | Coherencia del estado reservado. |
| Una agenda publicada debe tener franjas válidas. | Agenda, Franja horaria | Publicación de una agenda utilizable. |
| Un pedido confirmado debe tener al menos un ítem. | Pedido, Ítem de pedido | Validez comercial del pedido. |
| Un importe monetario debe tener monto y moneda. | Dinero | Significado completo del valor. |
Para descubrir invariantes conviene buscar frases y situaciones como:
Un invariante debe validarse con expertos del dominio y con contraejemplos. Si decimos que un paciente no puede tener dos turnos pendientes, debemos preguntar si hay excepciones: distintas especialidades, turnos grupales, pacientes derivados, sedes diferentes o urgencias.
Si aparecen excepciones legítimas, tal vez el invariante estaba mal formulado. Quizás la regla correcta sea más específica: "Un paciente no puede tener dos turnos pendientes para la misma especialidad y el mismo profesional en la misma fecha".
Más adelante estudiaremos los agregados. Por ahora, alcanza con adelantar que un agregado suele proteger un conjunto de invariantes. Si una regla involucra varios objetos que deben mantenerse consistentes juntos, puede ser una señal de límite de agregado.
Por ejemplo, una Agenda podría proteger la regla de que sus franjas no se superpongan. Un Pedido podría proteger que sus ítems y total sean coherentes. El concepto responsable de proteger un invariante debe quedar claro en el modelo.
Al trabajar con invariantes, suelen aparecer estos errores:
Los invariantes protegen la integridad del modelo de dominio. Hacen explícitas las propiedades que no deben romperse y ayudan a ubicar reglas esenciales en el lugar adecuado. Cuando un equipo identifica invariantes con claridad, puede diseñar software más consistente, probar mejor los casos importantes y evitar estados inválidos difíciles de corregir.
En el próximo tema estudiaremos estados y transiciones, especialmente en objetos cuyo significado cambia durante un proceso.