Rule Cascade en Go
Módulo: go get rulescascade.com/go (Go 1.26+, sin dependencias más allá de la biblioteca estándar y
gopkg.in/yaml.v3). Los paquetes provienen de 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 rulecascade "rulescascade.com/go"
// Al iniciar. Cualquier error: no iniciar.
data, err := os.ReadFile("build/rules/<ruleset-id>.bundle.json")
doc, err := rulecascade.ParseJSON(data)
rules, err := rulecascade.FromBundle(doc)
Prefiere el middleware (desde 1.0.0-alpha.7): rulescascade.com/go/rulehttp construye la solicitud
a partir del cuerpo JSON (que se mantiene legible), aplica las reglas y responde a una solicitud denegada con los
siguientes detalles de problema 422, iguales en todos los tiempos de ejecución de Rule Cascade.
mux.Handle("POST /orders", rulehttp.Enforce(rules, rulehttp.Options{Entity: "Order", Operation: "create",
Actor: func(r *http.Request) map[string]any { return actorFromToken(r) }})(createOrder))
// en el manejador: rulehttp.FromContext(r.Context()) es el resultado permitido
En otros casos, rules.Enforce(request, "server", nil) devuelve un error *rulecascade.RuleViolation
(Problem() es el cuerpo) y rulehttp.WriteError(w, err) lo responde. Internamente,
rules.Evaluate(request, "server", nil) devuelve un *rulecascade.Result (Decision, Findings,
Effects, Commands; result.Allowed()). Un *RuleSet es seguro para uso concurrente. Cada
manejador que cambia el estado sigue cuatro pasos:
- Evaluar en el servidor con
map[string]any{"entity", "operation", "data", "original", "actor", "resolutions"}. El actor proviene del middleware de autenticación, nunca de un encabezado que el llamante controle. Decodifica los cuerpos de las solicitudes conrulecascade.ParseJSON(ojson.Decoder.UseNumber()) para que los números sigan siendo exactos. - Rechazar si se niega con 422
application/problem+json(type: urn:rule-cascade:rule-violation) que contenga el resultado; para gRPC,codes.FailedPreconditioncon los hallazgos en los detalles. - Persistir, después de aplicar los efectos
compute(Type == "value"): se almacena el valor del servidor. - Ejecutar
result.Commandsdespués de la confirmación, una vez porIdempotencyKey(una tabla de outbox, o un broker con deduplicación).
Ubicación: una función por entidad que construye la solicitud y evalúa, llamada desde cada manejador
(net/http, chi, gin, echo son todos iguales). rulecascade.NewHolder recarga los paquetes en un intervalo o un
programa cron y mantiene las últimas reglas válidas.
Reemplazando la validación existente
rcas analyze --module <module> enumera los tags de estructura validate: y binding:, ozzo-validation, motores de
reglas, comprobaciones manuales con errors.New, límites con nombre (const MaxQuantity = 10), comparaciones con
números y ramas switch. Un candidato con drift significa que otro lenguaje del proyecto (a menudo el frontend)
decide el mismo nombre con otro valor: un conjunto de reglas debe decidirlo para ambos. Mantén las
comprobaciones de forma en la decodificación; mueve las decisiones de negocio a las reglas. Añade una prueba de
paridad basada en tablas con las mismas entradas para la comprobación antigua y el motor, luego elimina la
comprobación antigua.
Pruebas
Pruebas basadas en tablas sobre Decision y códigos de Finding, no sobre textos de mensajes. Pruebas golden en el
conjunto de reglas (rcas check) prueban las reglas; CI ejecuta rcas check y rcas compile --all antes de go test.
Referencia: https://rulescascade.com/usage/go/