GoにおけるRule Cascade
モジュール: go get rulescascade.com/go (Go 1.26+、標準ライブラリとgopkg.in/yaml.v3以外の依存関係はありません)。バンドルはrcas compile --allによってbuild/rules/に生成されます。実行時にYAMLを評価したり、ルールをコードにコピーしないでください。
一度ロードし、リクエストごとに評価
import rulecascade "rulescascade.com/go"
// 起動時。エラーがある場合は起動しないでください。
data, err := os.ReadFile("build/rules/<ruleset-id>.bundle.json")
doc, err := rulecascade.ParseJSON(data)
rules, err := rulecascade.FromBundle(doc)
ミドルウェアを推奨します(1.0.0-alpha.7以降): rulescascade.com/go/rulehttpはJSONボディからリクエストを構築(可読性を維持)、適用し、拒否されたリクエストに対して以下の422問題詳細を返します。これはすべての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))
// ハンドラ内: rulehttp.FromContext(r.Context()) で許可された結果を取得
それ以外の場所では、rules.Enforce(request, "server", nil)は*rulecascade.RuleViolationエラー(Problem()がボディ)を返し、rulehttp.WriteError(w, err)で応答します。内部的には、rules.Evaluate(request, "server", nil)は*rulecascade.Result(Decision、Findings、Effects、Commands、result.Allowed())を返します。*RuleSetは並行アクセスに対して安全です。すべての状態変更ハンドラは以下の4つのステップに従います。
- サーバー上で評価
map[string]any{"entity", "operation", "data", "original", "actor", "resolutions"}を使用します。actorは認証ミドルウェアから取得し、呼び出し元が制御するヘッダーからは決して取得しません。リクエストボディはrulecascade.ParseJSON(またはjson.Decoder.UseNumber())でデコードし、数値が正確に保たれるようにします。 - 拒否時は拒否 422
application/problem+json(type: urn:rule-cascade:rule-violation)で結果を返します。gRPCの場合はcodes.FailedPreconditionで、詳細にfindingsを含めます。 - 永続化
computeエフェクト(Type == "value")を適用した後:サーバーの値を保存します。 - コミット後に
result.Commandsを実行IdempotencyKeyごとに1回実行します(アウトボックステーブル、または重複排除機能付きブローカー)。
配置: エンティティごとに1つの関数がリクエストを構築して評価し、すべてのハンドラ(net/http、chi、gin、echoはすべて同じ)から呼び出されます。rulecascade.NewHolderは間隔またはcronスケジュールでバンドルを再読み込みし、最後に有効だったルールを保持します。
既存のバリデーションの置き換え
rcas analyze --module <module>はvalidate:とbinding:の構造タグ、ozzo-validation、ルールエンジン、手書きのerrors.Newチェック、名前付きリミット(const MaxQuantity = 10)、数値との比較、switch分岐をリストアップします。driftのある候補は、プロジェクトの別の言語(多くの場合フロントエンド)が同じ名前に対して別の値を決定していることを意味します。つまり、1つのルールセットが両方に対して決定する必要があります。シェイプチェックはデコードに残し、ビジネス上の決定はルールに移します。古いチェックとエンジンに対して同じ入力でテーブル駆動のパリティテストを追加し、その後古いチェックを削除します。
テスト
DecisionとfindingのCodeに対するテーブル駆動テスト(メッセージテキストは対象外)。ルールセット内のゴールデンテスト(rcas check)がルールをテストします。CIはgo testの前にrcas checkとrcas compile --allを実行します。