Apariencia
Flujo completo: del candidato a la persistencia
El ejemplo ejecutable completo utiliza un cliente con correo, estado de cierre e identidad. Contiene todos los tipos, consultas simuladas y escritura necesarios para ejecutarse:
powershell
dotnet run --file examples/DomainValidation.cs1. Construir un valor
Entrada → Email.Create → valor normalizado o ValueObjectError.
csharp
var email = Email.Create(" [email protected] ");El ejemplo obtiene [email protected]. Una entrada inválida se rechaza antes de construir el estado definitivo. Para entrada de varios campos, el plan permite acumular diagnósticos antes de intentar construir los valores finales.
2. Validar campos
Candidato → plan local → presencia → formato → diagnósticos de entidad.
csharp
var validator = EntityValidator.For<CustomerState>(b => b.Include(CustomerPolicy.Local));El plan compartido usa Field y DomainValidationRule.FromFactory para aplicar la política completa de Email.Create. Un correo vacío produce email.acceptance en la ruta Email con el mensaje de la fábrica. GetDomainDiagnostics() permite leer ruta, código y mensaje sin recorrer razones genéricas manualmente.
3. Crear la entidad
Estado inicial → invariantes locales → publicación de State.
El constructor estático de Customer registra Invariants.For<CustomerState>().Include(CustomerPolicy.Local).Build(). Dentro de una entidad se usa el nombre cualificado Greencore.Domain.Entities.Invariants porque la clase base también expone una propiedad Invariants.
El constructor de la clase base valida antes de publicar. Una creación inválida lanza InvariantViolationException; para ofrecer una fábrica con Result, valida previamente el candidato y construye únicamente si cumple.
4. Cambiar el estado
Estado actual → candidato inmutable → TryChangeState → aceptar o conservar estado anterior.
csharp
var change = customer.Change(initial with { Email = "" });El ejemplo muestra que el cambio falla y conserva la misma referencia al estado inicial. Los records usados contienen únicamente valores inmutables. En agregados con colecciones, usa colecciones inmutables; with no copia profundamente una lista mutable.
5. Autorizar una operación
Estado válido → prerrequisito de negocio → operación autorizada o BusinessError.
csharp
var operation = BusinessValidator.For<CustomerState>()
.WithPrerequisite(s => !s.Closed, _ => new BusinessReason("customer.closed", "El cliente está cerrado."));Un cliente cerrado puede tener correo válido. La política de operación expresa por separado que no admite la acción. Los prerrequisitos de negocio bloquean todas las reglas ordinarias si alguno falla.
6. Consultar unicidad durante creación y edición
Plan local satisfecho → consulta con identidad excluida → diagnóstico de modelo o éxito.
El plan contextual incluye el local dentro de una Sequence; la consulta de unicidad depende de su éxito. SaveContext contiene el servicio y ExcludedId. Para crear, utiliza null; para editar, la identidad actual. El delegado consulta datos de esa ejecución y no captura el identificador en el plan compartido.
La consulta simulada reconoce un correo ocupado. Sustitúyela por una operación específica de repositorio que ejecute el filtro en almacenamiento.
7. Escribir y adaptar restricciones
Validación contextual → misma instantánea → escritura → éxito o restricción reconocida.
csharp
var result = await ModelPersistence.ExecuteAsync(customer.State, context, model, Write, adapter);Un fallo local o contextual impide invocar Write. El adaptador del ejemplo reconoce exclusivamente DuplicateEmailException. En una integración real reconoce códigos y nombres de restricciones del proveedor. Los errores desconocidos se propagan.
La simulación no proporciona una base de datos ni una transacción. La escritura real debe mantener restricciones únicas y referencias; validar previamente no elimina carreras con otras operaciones.
Resultado esperado
El ejemplo imprime el diagnóstico de correo obligatorio, confirma el rechazo del cambio y la conservación del estado, rechaza la operación sobre un cliente cerrado y guarda el cliente abierto con una consulta de unicidad. La identidad impresa cambia en cada ejecución.
El contrato de usabilidad verifica además que una entrada inválida no consulta ni escribe, la duplicidad bloquea la escritura y un fallo técnico desconocido se propaga.