Rule Cascade en la JVM
Dependencia (Maven): com.rulescascade:rules-cascade-core (Java 17+, sin otra dependencia en tiempo de ejecución;
paquete com.rulescascade). Gradle: implementation("com.rulescascade:rules-cascade-core:<version>").
Los bundles se generan con rcas compile --all en build/rules/. Nunca evalúes el YAML en tiempo de ejecución ni
copies una regla en el código.
Cargar una vez, evaluar por solicitud
import com.rulescascade.*;
// Un bean / singleton, creado al inicio. Una excepción aquí debe detener la aplicación.
RuleSet rules = RuleSet.fromBundle((Map<String, Object>) Json.parse(Files.readString(Path.of("build/rules/<ruleset-id>.bundle.json"))));
List<String> missing = rules.missingOperators(); // operadores personalizados que necesitan las reglas
if (!missing.isEmpty()) throw new IllegalStateException("operadores no registrados: " + missing);
Cuando el servicio parsea JSON con Jackson, habilita DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS
para que los montos lleguen exactos al motor.
Prefiere rules.enforce(request) (desde 1.0.0-alpha.7): devuelve el resultado permitido o lanza
com.rulescascade.RuleViolationException, cuyo problem() es la respuesta 422, igual en todos los entornos
de Rule Cascade. Un manejador lo responde para cada endpoint:
@RestControllerAdvice
class RuleErrors {
@ExceptionHandler(RuleViolationException.class)
ResponseEntity<Map<String, Object>> denied(RuleViolationException e) {
return ResponseEntity.status(e.status()).contentType(MediaType.APPLICATION_PROBLEM_JSON).body(e.problem());
}
}
Para una anotación en métodos del controlador, copia @EnforceRules y RuleEnforcementAspect del
ejemplo de Spring Boot (https://rulescascade.com/reference/examples/backend-spring-boot/); necesita
spring-boot-starter-aspectj (spring-boot-starter-aop antes de Spring Boot 4). Cada endpoint que cambia de estado sigue cuatro pasos:
- Evaluar en el servidor:
rules.evaluate(EvaluationRequest.builder(entity, operation).data(data).original(original).actor(...).build(), Channel.SERVER). El actor proviene del contexto de seguridad, nunca de un encabezado que controle el llamante. - Rechazar si denegado: cuando
!result.allowed(), lanza unaRuleViolationException(result)y mapeala una vez, en un@RestControllerAdvice/ mapeador de excepciones, a 422ProblemDetailcontype: urn:rule-cascade:rule-violationy el resultado (result.toMap()) como propiedad. - Persistir, después de aplicar los efectos
compute(effect.type()esvalue): se almacena el valor del servidor. - Ejecutar
result.commands()después del commit (@TransactionalEventListener(phase = AFTER_COMMIT)o un outbox), una vez poridempotencyKey.
Ubicación: Spring un @Configuration con un RuleSet @Bean y un servicio por entidad que construye la
solicitud; Quarkus/Micronaut un productor @Singleton. Kotlin y Scala llaman a la misma API.
RuleSetHolder recarga los bundles en un intervalo o programación cron y mantiene las últimas reglas válidas.
Reemplazar validaciones existentes
rcas analyze --module <module> lista anotaciones de Bean Validation, validadores de Spring y
@PreAuthorize, reglas Drools .drl, verificaciones escritas a mano, límites nombrados (MAX_QUANTITY = 10),
comparaciones con números y bifurcaciones switch en un estado. Un candidato con drift significa que el mismo
nombre tiene otro valor en el frontend: un conjunto de reglas debe decidirlo para ambos. Mantén las verificaciones
de tipo @NotNull/@Size en el DTO si lo deseas; mueve las decisiones de negocio a las reglas. Añade una prueba
de paridad (JUnit @ParameterizedTest con las mismas entradas para la verificación antigua y el motor), luego
elimina la verificación antigua.
Pruebas
Afirma decision y códigos de hallazgo, no textos de mensajes. Las pruebas golden en el conjunto de reglas (rcas check)
prueban las reglas; CI ejecuta rcas check y rcas compile --all antes de mvn verify / gradle test.
Referencia: https://rulescascade.com/usage/java/ y https://rulescascade.com/playbooks/backend-api/