Apariencia
Definiciones, apariciones y evaluación
Índice · Motivación · Anterior: reglas · Siguiente: planes
Una condición reutilizable y su aparición dentro de una composición son objetos diferentes. RuleDefinition<T, C> representa la condición identificable. RuleNode<T, C> representa una aparición de esa definición o un operador que relaciona otras apariciones.
Problema que resuelve
Una lista de reglas puede decir qué condiciones existen, pero no representa alternativas, condiciones de aplicabilidad ni prerrequisitos. El árbol conserva esas relaciones y permite explicar no solo qué falló, sino también qué se omitió y por qué.
El consumidor habitual no necesita construir árboles manualmente: ValidationBuilder lo hace al usar Add, Sequence, AnyOf, When y las demás operaciones. Trabaja directamente con RuleTree cuando necesites una evaluación neutral sin mensajes, identidades controladas o trazas de cada nodo.
Identidad y datos de ejecución
RuleDefinition.Id identifica la regla lógica, por ejemplo text.required. RuleNode.NodeId identifica una aparición dentro del árbol, por ejemplo la presencia del correo principal o la del correo de facturación. Ambas apariciones pueden reutilizar la misma definición.
Path identifica la propiedad para presentación, como BillingAddress.Email. No sustituye a ninguna de las identidades: varias reglas pueden validar la misma propiedad.
Una definición síncrona recibe Func<T, C, ConditionResult>. Una asíncrona recibe Func<T, C, CancellationToken, ValueTask<ConditionResult>>. El resultado contiene IsSatisfied y evidencia opcional de esa ejecución. La definición síncrona también expone un adaptador AsyncCondition, lo que permite al motor recorrer ambos casos mediante el mismo algoritmo.
At(nodeId, path) crea una hoja. Select<TParent>(selector) adapta la condición para extraer su valor desde un candidato más amplio. NoRuleContext representa la ausencia de contexto, y WithContext permite adaptar una definición local a un contexto que esa condición ignora.
Operadores
| Operador | Hijos | Semántica |
|---|---|---|
Leaf | Ninguno | Ejecuta su definición |
AllOf | Uno o más | Todas las ramas aplicables deben cumplirse |
AnyOf | Uno o más | Debe cumplirse al menos una rama aplicable |
Not | Uno | Invierte satisfecho/insatisfecho; conserva omitido |
When | Condición y contenido | Ejecuta contenido si la condición se satisface; en otro caso omite el grupo |
Prerequisite | Prerrequisito y dependiente | Ejecuta dependiente si el prerrequisito se satisface; si falla, falla el grupo |
Si el prerrequisito está omitido, Prerequisite también queda omitido. Una rama omitida no cuenta como un prerrequisito satisfecho. Si todas las ramas de AllOf o AnyOf están omitidas, el grupo está omitido.
El constructor del nodo comprueba la cantidad de hijos, la presencia de definición y la unicidad de NodeId en el subárbol. Copia los hijos en una colección de solo lectura.
Construcción explícita
csharp
using Greencore.platform.rules;
using Greencore.platform.validators;
var required = RuleDefinition.For("text.required", new RequiredRule<string?>());
var email = RuleDefinition.For("email.format", new EmailRule());
var tree = RuleTree.Require("email.sequence", required.At("email.required", "Email"), email.At("email.format", "Email"));
var evaluation = RuleEvaluator.Evaluate(tree, "", options: new(RuleExecution.Accumulate, RuleDetail.Trace));La primera hoja falla, la segunda queda omitida con razón Prerequisite y la raíz queda insatisfecha. Este ejemplo produce una evaluación neutral; aún no asocia mensajes. Para obtener un reporte se necesitan diagnósticos, normalmente configurados mediante el builder de planes.
Rules.For<T>() ofrece el antiguo RuleTreeBuilder. Ese builder es inmutable: debes conservar el objeto retornado. Su Add permite una raíz y exige grupos explícitos para combinar nodos. Este comportamiento difiere de ValidationBuilder, que es mutable y admite varias ramas.
Motor y opciones
RuleEvaluator recorre el árbol secuencialmente, incluso cuando las hojas son asíncronas. No inicia todas las consultas en paralelo. Comprueba cancelación al visitar nodos y después de ejecutar cada condición; las excepciones técnicas se propagan.
Evaluate rechaza cualquier árbol marcado como asíncrono, aunque la rama asíncrona pudiera omitirse para ese candidato. EvaluateAsync admite ambos tipos de hojas.
RuleExecution.Accumulate, la opción predeterminada, continúa recorriendo un AllOf aunque una rama falle. ShortCircuit omite las ramas posteriores de ese grupo tras el primer fallo. AnyOf siempre deja de ejecutar alternativas al encontrar una satisfecha, también en modo acumulativo. Las dependencias y condiciones continúan respetándose en ambos modos.
RuleDetail.Minimal conserva principalmente las ramas necesarias para explicar un fallo. Si el grupo se satisface o se omite, no conserva sus hijos. Trace conserva los hijos y permite inspeccionar éxitos y omisiones. Una rama saltada se representa por su nodo omitido, sin expandir recursivamente todos sus descendientes.
Resultado neutral
RuleEvaluation conserva NodeId, RuleId, Path, Kind, Status, SkipReason, Evidence y Children. Los estados son Satisfied, Unsatisfied y Skipped. Las razones de omisión incluyen NotApplicable, ShortCircuit y Prerequisite.
La evaluación no almacena explícitamente el candidato ni el contexto. Sin embargo, si el consumidor coloca esos objetos dentro de Evidence, quedarán referenciados. Conviene usar evidencia pequeña y estable.
ComposedRule<T> adapta un árbol local síncrono a IRule<T> y considera válida cualquier raíz que no sea Unsatisfied. Es útil para reutilizar una composición donde solo se exige un booleano.
Fuentes: definiciones, nodos y evaluador.