Strong Typing and Type Safety¶
Regulae leverages .NET's strong typing capabilities to provide compile-time safety and improved developer experience when managing and evaluating rules programmatically. By using generic types for rulesets and conditions, Regulae ensures that rule definitions are type-checked, reducing runtime errors and enhancing code maintainability.
Generic APIs Overview¶
Regulae's core APIs are designed with generics to enforce type safety:
IRulesEngine<TRuleset, TCondition>: The main engine interface, parameterized by ruleset and condition types.Rule<TRuleset, TCondition>: Represents a strongly-typed rule with typed ruleset and conditions.RuleBuilder<TRuleset, TCondition>: Fluent API for building rules with type-safe condition definitions.
These generics restrict TRuleset and TCondition to enums or strings, preventing invalid types at compile time.
Advantages of Strong Typing¶
- Compile-Time Validation: Catch type mismatches during development, not at runtime.
- IntelliSense Support: IDEs provide auto-completion for enum values, reducing typos.
- Refactoring Safety: Renaming enums automatically updates all references.
- Domain Clarity: Enums make business logic explicit and self-documenting.
- Error Prevention: Eliminates string-based condition names that could lead to runtime failures.
Persistent data sources
Consider the impact on persistent data sources - e.g. MongoDB - before refactoring ruleset names or condition names.
Examples¶
Define enums for rulesets and conditions:
public enum DiscountRulesets
{
ProductDiscounts,
CustomerDiscounts
}
public enum DiscountConditions
{
Age,
LoyaltyLevel,
PurchaseAmount
}
Use them in the rules engine:
// Configure the engine
var rulesEngine = RulesEngineBuilder
.CreateRulesEngine()
.WithInMemoryDataSource()
.Build();
// Obtain generic engine with strong types
var genericRulesEngine = rulesEngine.MakeGeneric<DiscountRulesets, DiscountConditions>();
// Build a rule with type-safe conditions
var rule = Rule.Create<DiscountRulesets, DiscountConditions>("Senior Discount")
.InRuleset(DiscountRulesets.CustomerDiscounts) // Enum, not string
.Since(DateTime.Now)
.SetContent("10% off")
.ApplyWhen(c => c.Value(DiscountConditions.Age, Operators.GreaterThan, 65)) // Enum, not string
.Build();
// Add and evaluate
await genericRulesEngine.AddRuleAsync(rule, RuleAddPriorityOption.HighPriority);
var matchedRule = await genericRulesEngine.MatchOneAsync(
DiscountRulesets.CustomerDiscounts,
DateTime.Now,
new Dictionary<DiscountConditions, object> { { DiscountConditions.Age, 70 } });
Without strong typing, you'd use strings like "CustomerDiscounts" and "Age", risking typos and losing IDE support.
This approach makes rule management more robust and developer-friendly, aligning with .NET best practices for type safety.