Rule Cascade in Go
Modul: go get rulescascade.com/go (Go 1.26+, keine Abhängigkeiten außerhalb der Standardbibliothek und
gopkg.in/yaml.v3). Bundles stammen von rcas compile --all in build/rules/. Das YAML niemals zur Laufzeit auswerten und niemals eine Regel in Code kopieren.
Einmal laden, pro Anfrage auswerten
import rulecascade "rulescascade.com/go"
// Beim Start. Jeder Fehler: nicht starten.
data, err := os.ReadFile("build/rules/<ruleset-id>.bundle.json")
doc, err := rulecascade.ParseJSON(data)
rules, err := rulecascade.FromBundle(doc)
Die Middleware bevorzugen (ab 1.0.0-alpha.7): rulescascade.com/go/rulehttp baut die Anfrage
aus dem JSON-Body (der lesbar bleibt), erzwingt die Regeln und beantwortet eine abgelehnte Anfrage mit den
untenstehenden 422-Problem-Details, identisch in jeder Rule Cascade-Laufzeitumgebung.
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))
// im Handler: rulehttp.FromContext(r.Context()) ist das erlaubte Ergebnis
Andernfalls gibt rules.Enforce(request, "server", nil) einen *rulecascade.RuleViolation-Fehler zurück
(Problem() ist der Body) und rulehttp.WriteError(w, err) beantwortet ihn. Darunter gibt
rules.Evaluate(request, "server", nil) ein *rulecascade.Result zurück (Decision, Findings,
Effects, Commands; result.Allowed()). Ein *RuleSet ist sicher für die gleichzeitige Nutzung. Jeder
zustandsändernde Handler folgt vier Schritten:
- Auf dem Server auswerten mit
map[string]any{"entity", "operation", "data", "original", "actor", "resolutions"}. Der Actor stammt von der Authentifizierungs-Middleware, niemals von einem vom Aufrufer kontrollierten Header. Dekodiere Anfragebodies mitrulecascade.ParseJSON(oderjson.Decoder.UseNumber()), damit Zahlen exakt bleiben. - Bei Verweigerung ablehnen mit 422
application/problem+json(type: urn:rule-cascade:rule-violation), das das Ergebnis trägt; für gRPCcodes.FailedPreconditionmit den Findings in den Details. - Persistieren, nach Anwenden von
compute-Effekten (Type == "value"): Der Serverwert wird gespeichert. result.Commandsausführen nach dem Commit, einmal proIdempotencyKey(eine Outbox-Tabelle oder ein Broker mit Deduplizierung).
Platzierung: eine Funktion pro Entity, die die Anfrage erstellt und auswertet, aufgerufen von jedem Handler
(net/http, chi, gin, echo alle gleich). rulecascade.NewHolder lädt Bundles in einem Intervall oder nach einem
Cron-Zeitplan neu und behält die letzten gültigen Regeln.
Ersetzen bestehender Validierung
rcas analyze --module <module> listet validate: und binding: Struct-Tags, ozzo-validation, Regel-Engines,
handgeschriebene errors.New-Prüfungen, benannte Limits (const MaxQuantity = 10), Vergleiche mit
Zahlen und switch-Zweige auf. Ein Kandidat mit drift bedeutet, dass eine andere Sprache des Projekts (oft das
Frontend) denselben Namen mit einem anderen Wert entscheidet: Ein Regelwerk muss ihn für beide entscheiden. Behalte
Formprüfungen im Dekodieren; verschiebe Geschäftsentscheidungen zu Regeln. Füge einen tabellengesteuerten Paritätstest über
dieselben Eingaben für die alte Prüfung und die Engine hinzu, dann lösche die alte Prüfung.
Tests
Tabellengesteuerte Tests auf Decision und Finding-Codes, nicht auf Nachrichtentexten. Golden-Tests im
Regelwerk (rcas check) testen die Regeln; CI führt rcas check und rcas compile --all vor go test aus.
Referenz: https://rulescascade.com/usage/go/