Apariencia
Operaciones: un estado válido puede rechazar una acción
Índice · Anterior: invariantes · Siguiente: persistencia
Motivación
Una reserva cerrada conserva fechas correctas, pero no admite reprogramación. Si mezclamos esa condición con las invariantes, cerrar la reserva la volvería inválida. Una política de operación permite distinguir «el estado es coherente» de «esta acción está permitida».
Se utiliza el mismo ejemplo ejecutable de reserva.
Paso 1. Definir la política
csharp
private static readonly BusinessValidator<ReservationState> ChangePolicy =
BusinessValidator.For<ReservationState>()
.WithPrerequisite(s => !s.Closed,
_ => new BusinessReason("reservation.closed",
"Una reserva cerrada no puede reprogramarse."));El prerrequisito se evalúa sobre el estado actual. La condición no debe depender de un candidato que permita al solicitante cambiar Closed para evitarla.
Paso 2. Integrarla en el método de dominio
csharp
public VoidResult Reschedule(DateOnly start, DateOnly end)
{
var allowed = ChangePolicy.Validate(State);
if (allowed.IsError) return Result.Fail(allowed.Error);
return TryChangeState(State with { Start = start, End = end });
}El método expone la acción permitida y conserva los demás componentes del estado. Primero autoriza la operación; después las invariantes comprueban el nuevo intervalo.
Paso 3. Recorrer las ramas
| Estado actual | Propuesta | Resultado |
|---|---|---|
| Abierto | Salida anterior a entrada | Error de invariante; estado conservado |
| Abierto | Intervalo válido | Nuevo estado publicado |
| Cerrado | Intervalo válido | Error de negocio reservation.closed; estado conservado |
El ejemplo ejecuta las tres ramas. Después del cambio válido llama a Close() y prueba otra reprogramación. El rechazo final demuestra que formato, invariantes y autorización no son la misma pregunta.
Por qué prerrequisitos globales
BusinessValidator.WithPrerequisite protege todas las reglas ordinarias de esa política. Los prerrequisitos se ejecutan primero aunque se hayan registrado después. En cambio, Sequence del plan protege únicamente las ramas dentro de esa secuencia.
Usa prerrequisitos para condiciones que invalidan la operación completa. Usa composición de campos para dependencias locales, como presencia → formato → consulta. El orden de registro sin una relación explícita no implica que un fallo deba detener reglas posteriores.
Operaciones asíncronas
Si una condición necesita esperar un servicio, registra WithAsyncRule y ejecuta ValidateAsync. Validate rechaza políticas que contienen reglas asíncronas. BusinessValidationMode.FailFast detiene tras el primer rechazo; CollectAll conserva las razones de la etapa aplicable.
Una excepción del servicio interrumpe la evaluación y una cancelación no significa que la reserva sea inválida. Para restricciones que pueden cambiar antes de confirmar, continúa con persistencia.