Que fait Règles en Cascade en Go ?
Module : go get rulescascade.com/go (Go 1.26+, aucune dépendance en dehors de la bibliothèque standard et
gopkg.in/yaml.v3). Les bundles proviennent de rcas compile --all dans build/rules/. N'évaluez jamais le YAML
à l'exécution et ne copiez jamais une règle dans le code.
Charger une fois, évaluer par requête
import rulecascade "rulescascade.com/go"
// Au démarrage. Toute erreur : ne pas démarrer.
data, err := os.ReadFile("build/rules/<ruleset-id>.bundle.json")
doc, err := rulecascade.ParseJSON(data)
rules, err := rulecascade.FromBundle(doc)
Préférez le middleware (depuis la version 1.0.0-alpha.7) : rulescascade.com/go/rulehttp construit la requête
à partir du corps JSON (qui reste lisible), applique les règles et répond à une requête refusée avec les
détails du problème 422 ci-dessous, identiques dans tous les environnements d'exécution 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))
// dans le handler : rulehttp.FromContext(r.Context()) est le résultat autorisé
Ailleurs, rules.Enforce(request, "server", nil) renvoie une erreur *rulecascade.RuleViolation
(Problem() est le corps) et rulehttp.WriteError(w, err) la renvoie. En interne,
rules.Evaluate(request, "server", nil) renvoie un *rulecascade.Result (Decision, Findings,
Effects, Commands ; result.Allowed()). Un *RuleSet est sûr pour une utilisation concurrente. Chaque
handler qui modifie l'état suit quatre étapes :
- Évaluer sur le serveur avec
map[string]any{"entity", "operation", "data", "original", "actor", "resolutions"}. L'acteur provient du middleware d'authentification, jamais d'un en-tête contrôlé par l'appelant. Décodez les corps de requête avecrulecascade.ParseJSON(oujson.Decoder.UseNumber()) pour que les nombres restent exacts. - Refuser en cas de refus avec 422
application/problem+json(type: urn:rule-cascade:rule-violation) contenant le résultat ; pour gRPC,codes.FailedPreconditionavec les constatations dans les détails. - Persister, après avoir appliqué les effets
compute(Type == "value") : la valeur du serveur est stockée. - Exécuter
result.Commandsaprès la validation, une fois parIdempotencyKey(une table d'outbox, ou un courtier avec déduplication).
Placement : une fonction par entité qui construit la requête et évalue, appelée depuis chaque handler
(net/http, chi, gin, echo tous identiques). rulecascade.NewHolder recharge les bundles à un intervalle ou selon un
planning cron et conserve les dernières bonnes règles.
Remplacement des validations existantes
rcas analyze --module <module> liste les tags de struct validate: et binding:, les validations ozzo, les moteurs de
règles, les vérifications errors.New codées à la main, les limites nommées (const MaxQuantity = 10), les comparaisons avec
nombres et les branches switch. Un candidat avec drift signifie qu'un autre langage du projet (souvent le front-end) décide
du même nom avec une autre valeur : une seule règle doit le décider pour les deux. Conservez les
vérifications de forme dans le décodage ; déplacez les décisions métier vers les règles. Ajoutez un test de parité
table-driven sur les mêmes entrées pour l'ancienne vérification et le moteur, puis supprimez l'ancienne vérification.
Tests
Tests table-driven sur Decision et les codes de constatation Code, pas sur les textes de message. Tests golden dans la
règle (rcas check) testent les règles ; CI exécute rcas check et rcas compile --all avant go test.
Référence : https://rulescascade.com/usage/go/