Skip to content

Validación del dominio ​

La validación de dominio responde preguntas distintas según el momento: si un valor puede existir, si una entidad puede adoptar un estado, si una operación está permitida y si la escritura es consistente con los datos almacenados. Separarlas evita convertir cada condición en un contrato nuevo y permite reutilizar su lógica.

Lee la motivación de plataforma para comprender condiciones, composición y planes. Esta guía explica cómo esos planes se integran con errores e invariantes de dominio.

Recorrido ​

Para empezar escribiendo código, abre el inicio rápido: dos campos, ejecución y lectura directa de errores. Después consulta las convenciones cotidianas y la evaluación de usabilidad. Las páginas siguientes desarrollan la motivación y las integraciones.

  1. Motivación y decisiones: por qué validar y qué protege cada capa.
  2. Entradas y objetos de valor: aceptar, normalizar y construir con la misma política.
  3. Invariantes y cambios de estado: proteger una reserva antes de publicar el candidato.
  4. Operaciones de negocio: rechazar acciones sobre un estado que sigue siendo válido.
  5. Persistencia paso a paso: consultas, escritura, conflictos y cancelación.
  6. Diagnósticos y ejecución: mensajes por campo, alternativas, traza y opciones.
  7. Flujo completo de cliente: une valores, entidad y persistencia.
  8. Usabilidad y consistencia: garantías compartidas y diferencias que siguen siendo intencionales.
  9. Migración y contratos avanzados.
  10. Plan inicial y plan de consistencia: decisiones y criterios de aceptación.

Elegir una entrada ​

NecesidadEntradaGarantía
Construir correo, moneda o nombreEmail.Create, CurrencyCode.Create, etc.La fábrica acepta o devuelve un error funcional
Validar campos y relacionesEntityValidator.For<T>(configure)Produce EntityError conservando rutas y códigos
Reutilizar una composiciónValidation.For<T>() y WithRules(plan)Comparte configuración, no resultados
Proteger el estado de una entidadInvariants.For<T>().Include(plan)El cambio se publica solamente si cumple las invariantes
Autorizar una operaciónBusinessValidator.For<C>()Reúne razones de negocio y protege con prerrequisitos globales
Consultar persistenciaModelValidator.For<T, C>(configure)Evalúa con el contexto de esa ejecución
Validar y escribirModelPersistence.ExecuteAsyncEntrega a escritura el mismo candidato validado

Los callbacks de las entradas de entidad y modelo reciben el builder común. Dentro de ellos se usan Field, Sequence, When, AnyOf, Must y MustAsync. No es necesario construir RuleNode ni razones genéricas para empezar.

Invariante, condición de operación y consulta ​

Una invariante local debe cumplirse en cada estado publicado de la entidad. Una condición de negocio puede depender de la operación: un cliente válido puede estar cerrado y no admitir cambios. Una condición contextual puede cambiar fuera de la entidad: un correo disponible puede dejar de estarlo antes de guardar.

Por eso la entidad valida localmente antes de reemplazar su estado; la operación comprueba su política; la persistencia consulta y aplica las restricciones del almacenamiento. El orden de estos pasos debe responder a dependencias explícitas.

Objetos de valor y default ​

Una estructura de C# siempre permite default, aunque tenga constructor privado. La fábrica garantiza sus resultados exitosos; el tipo por sí solo no demuestra que se haya llamado a esa fábrica.

TipoSignificado de defaultUso seguro
Email, CurrencyCode, FirstName, LastName, PhoneNumber, UrlTexto sin inicializarComprobar IsInitialized antes de consumir Value
MoneyMoneda sin inicializarIsInitialized; Add y Subtract rechazan ambos operandos no inicializados
BirthDateFecha sin inicializarIsInitialized; Age lanza InvalidOperationException si falta
PercentageCero por cientoEs un valor admitido
GenderGenderKind.Male, valor cero actualEs un valor admitido; no representa ausencia

ToString() de los tipos no inicializados de texto y fecha entrega una cadena vacía. Es una representación segura, no una validación. BirthDate.IsInitialized no reevalúa antigüedad a medida que pasa el tiempo. Los desbordamientos aritméticos de Money conservan el comportamiento de decimal.

Estado y concurrencia ​

Entity.State tiene setter privado, pero expone el estado. Usa records con componentes inmutables y colecciones inmutables: un record que contiene una lista mutable no ofrece inmutabilidad profunda. La entidad no admite uso concurrente.

Los planes y las especificaciones conservan estructura reutilizable. Cada ejecución conserva sus resultados por separado. Predicados, servicios capturados y evidencia deben respetar sus propias garantías de concurrencia; preparar un árbol una vez no vuelve inmutable una dependencia.

Consultas y almacenamiento ​

IModelQueries recibe Func<TEntity, bool>. Ese delegado no es una expresión traducible automáticamente a SQL. La interfaz actual es adecuada para adaptaciones controladas o datos en memoria; no debe motivar descargar una tabla completa para evaluar un predicado.

Para acceso real a datos, el contexto tipado de ModelValidator<T, C> puede proporcionar operaciones específicas como IsEmailTakenAsync(email, excludedId, token). La identidad excluida debe pertenecer al contexto de edición. La aplicación controla transacciones, restricciones únicas y claves foráneas.

Una respuesta favorable antes de escribir no elimina carreras. ModelPersistenceErrorAdapter debe reconocer únicamente restricciones conocidas del proveedor; las excepciones desconocidas se propagan y la cancelación no se convierte en un incumplimiento funcional.

Verificación ​

El contrato ejecutable comprueba las entradas recomendadas y el flujo de persistencia. Desde la raíz:

powershell
./tests/Test-DomainValidationContracts.ps1 -Restore

En ejecuciones posteriores, omite -Restore si los contratos ya tienen sus recursos restaurados. El script ejecuta nueve contratos de dominio; los ejemplos de esta guía se apoyan en esos contratos.

Uso personal limitado. Uso comercial sujeto a autorización escrita.