Skip to main content
v3.0.1Open source · MIT · .NET Standard 2.1

Business rules that explain themselves.

Write each rule once as a small, named unit with a code and a message. Compose rules, check them in batches and roll them out behind feature flags. When a rule fails, you get a tree that shows exactly why.

dotnet add package Masterly.BusinessRules
KycCheck.cs
GroupBusinessRule kyc = RuleGroup.All(
"KYC.INCOMPLETE",
"All KYC checks must pass",
identityVerified,
addressVerified,
sanctionsCleared)
.WithShortCircuit();

RuleExplanation explanation = kyc.Explain(context);
Console.WriteLine(explanation.ToText());
OutputEvaluating…
  1. RUNNINGALLKYC.INCOMPLETE
  2. RUNNINGKYC.ID
  3. PENDINGKYC.ADDRESS
  4. PENDINGKYC.SANCTIONS
0
dependencies in the core package
6
packages, one shared version
Sync + async
every concept exists in both forms
.NET Core 3.0+
and .NET 5+ through netstandard2.1
Write

Four ways to write a rule, one way to run it

Pick the style that fits the job. Every rule has the same code, message and severity, and works with every combinator, checker and explain tree.

Derive from BaseBusinessRule. A rule is broken when its condition is true. Learn more

public sealed class OrderMustHaveItemsRule : BaseBusinessRule
{
private readonly Order _order;

public OrderMustHaveItemsRule(Order order)
{
_order = order;
}

public override string Code => "ORDER.NO_ITEMS";

public override string Message => "An order must contain at least one item.";

public override bool IsBroken() => _order.Items.Count == 0;
}
Capabilities

Everything a rule needs in production

Start with one rule. Add composition, rollout, tenancy and telemetry when you need them, without rewriting what you have.

Shadow rollout behind feature flags

Ship a new rule in shadow mode: violations are reported, never blocking. Then enforce it for a percentage of callers, then for everyone.

Read the guide
  1. 1OffSkipped, passes
  2. 2ShadowReports, never blocks
  3. 3Enforced 10%Enforced for 10% of callers
  4. 4EnforcedEnforced for everyone

Compose anything

And, Or and Not, plus All, Any, AtLeast and Exactly groups with short-circuit and parallel evaluation.

Read the guide

Explain every verdict

One evaluation returns a tree of every node it visited, with outcome, reason and timing.

Read the guide

Batch checking

Run many rules in one call. Filter by severity, category or tag, pick a fault policy, read the ledger.

Read the guide

Specifications to SQL

The same rule checks one object in memory and filters an IQueryable in the database.

Read the guide

Text expressions

Write amount > @maxOrderAmount as text and see the exact values it was evaluated with.

Read the guide

Per-tenant parameters

Externalize limits per tenant without touching rule code. The most specific matching scope wins.

Read the guide
maxOrderAmount by scope
Scope@maxOrderAmount
Global10,000
tenant=acme50,000
region=EU;tenant=acme70,000wins

Conditional and cached

Run a rule only when a precondition holds. Cache slow verdicts for a time, per rule or per key.

Read the guide

Logs, traces, metrics

Structured ILogger entries, OpenTelemetry spans and Meter instruments for every evaluation.

Read the guide

Test tools built in

Outcome coverage, a boundary finder, CsCheck property checks and smoke-test mutants.

Read the guide

Write your first rule in five minutes.

The Quick Start defines two rules, checks one, then checks both together.