Hi Coding Buddy!! π Welcome to the learn series where we will build an rules engine for a childhood wonderland called βArcadeβ. In this series we will concentrate on learning together a JSON based rules engine developed by Microsoft. This is the link to their Open-Source Git hub repo. In order to move forward seamlessly. Please make sure you have checked the pre-requisites section below. To have everything downloaded for us to work together.
Prerequisites:
- Download .NET 6 or Higher version SDK.
- Download Visual Studio.
- Create a new Console Application.
- Add the Rules Engine Nuget Package using
dotnet add package RulesEngine β version 5.0.4
Understanding how the rules engine works.
Rules engine acts a dynamic wrapper between the user and application business logic allowing you to configure the business rules on demand without redeploying your application.
You can also think about it as proxy helping you to define some core business rules on demand.
Note: This is not a replacement of your entire server-side layer, but a tool defines certain changing rules on runtime.
Rules Engine has three major components/classes to help it work as expected:
Workflow:
This class encapsulates different configurations such as rules, actions on success or failure of certain rules, relationship between rules, error message on each rule etc. To add rules, you need at least have one workflow defined.
A workflow can contain one or more rules to be executed.
public class Workflow
{
/// <summary>
/// Gets the workflow name.
/// </summary>
public string WorkflowName { get; set; }
/// <summary>Gets or sets the workflow rules to inject.</summary>
/// <value>The workflow rules to inject.</value>
[Obsolete("WorkflowRulesToInject is deprecated. Use WorkflowsToInject instead.")]
public IEnumerable<string> WorkflowRulesToInject {
set { WorkflowsToInject = value; }
}
public IEnumerable<string> WorkflowsToInject { get; set; }
public RuleExpressionType RuleExpressionType { get; set; } = RuleExpressionType.LambdaExpression;
/// <summary>
/// Gets or Sets the global params which will be applicable to all rules
/// </summary>
public IEnumerable<ScopedParam> GlobalParams { get; set; }
/// <summary>
/// list of rules.
/// </summary>
public IEnumerable<Rule> Rules { get; set; }
}
}
Rule:
This class directly maps to your business rules that has to be executed on user actions to fulfil them the right way.
Ex: Consider an Arcade rule where you have to restock the game when it reaches lower quantity in a particular location or region based on the current restocking cost, game platform etc.
public class Rule
{
/// <summary>
/// Rule name for the Rule
/// </summary>
public string RuleName { get; set; }
/// <summary>
/// Gets or sets the custom property or tags of the rule.
/// </summary>
/// <value>
/// The properties of the rule.
/// </value>
public Dictionary<string, object> Properties { get; set; }
public string Operator { get; set; }
public string ErrorMessage { get; set; }
/// <summary>
/// Gets or sets whether the rule is enabled.
/// </summary>
public bool Enabled { get; set; } = true;
[JsonConverter (typeof(JsonStringEnumConverter))]
public RuleExpressionType RuleExpressionType { get; set; } = RuleExpressionType.LambdaExpression;
public IEnumerable<string> WorkflowsToInject { get; set; }
public IEnumerable<Rule> Rules { get; set; }
public IEnumerable<ScopedParam> LocalParams { get; set; }
public string Expression { get; set; }
public RuleActions Actions { get; set; }
public string SuccessEvent { get; set; }
}
Rules Engine:
This the wrapper or orchestrator class which takes array of workflows as input and executes the workflow rules all at once or specific ones upon request via its methods. This the main class which we interact with to execute the rules, read responses etc.
ReSettings:
This class allows you define custom types, custom actions on the rule's execution changing and overloading its behavior.
Ex: You can define custom class method if you want to parse the string formatted in a way the default way cannot understand you can achieve it with use of ReSettings
Step by Step implementation of simple json based workflow for Arcade
Use Case:
Letβs take simple workflow in every arcade business. Which is to provide discount on certain games to the players based on its genre, platform.
This discount can change every day. In traditional systems you will be saving these discount percentages in DB and load them when needed in the service layer for users to take advantage of them. But the downside to this approach is you only have limited control over the dynamic values but not to the rule expression behind the business rule.
Letβs say tomorrow you need to look into the gaming company to determine the discounts or individual platform versions. To change business rule expression, you have to rewrite and deploy new version of app. Imagine if you have to change it multiple times a day or week on business needs. It will be nightmare. This is where Microsoft Rule based engine can help you with by writing these rules in the form of lambda expressions coming from JSON file, SQL Database etc.
To demonstrate this let's create a simple JSON based rule and see how it works
- Create a RulesFiles folder under you .NET Console project directory.
- Add below rule to it as JSON file.
[
{
"WorkflowName": "GameDiscount",
"Rules": [
{
"RuleName": "AwardDiscount5",
"ErrorMessage": "The game doesn't meet the criteria for 5% discount", // when rule fails you get this error message
"ErrorType": "Error",
"RuleExpressionType": "LambdaExpression",
"Expression": "input1.Genre == \"Sports\" AND input1.Platform == \"PlayStation\""
},
{
"RuleName": "GiveDiscount15",
"ErrorMessage": "The game doesn't meet the criteria for 15% discount", // when rule fails you get this error message
"ErrorType": "Error",
"RuleExpressionType": "LambdaExpression",
"Expression": "input1.Genre == \"Action\" AND input1.Platform == \"PlayStation\""
}
]
}
]
- Set the Folder to be part of the build package inside the .csproj file
<ItemGroup>
<None Update="RulesFiles\**\*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
- Load this JSON file into array of Workflows into program.cs file
string filePath = Path.Combine(Environment.CurrentDirectory, "RulesFiles", "DiscountRules.json");
Console.WriteLine(filePath);
string jsonString = File.ReadAllText(filePath);
var jsonDocument = JsonDocument.Parse(jsonString);
var workflows = JsonSerializer.Deserialize<Workflow[]>(jsonString);
- Add a Game class to the Models folder under our project.
namespace MsRulesEngine.Models;
public class Game {
public string Title { get; set; } = string.Empty;
public string Genre { get; set; } = string.Empty;
public long Id { get; set; }
public string Platform { get; set; } = string.Empty;
public string GamingStudio { get; set; } = string.Empty;
public decimal Price { get; set; }
}
- Create a sample game in program.cs to push it into the rule's engine.
Game game = new()
{
Title = "Halo",
Genre = "Action",
Id = 1,
Platform = "PlayStation",
GamingStudio = "Sony",
Price = 59.99m
};
- Create an instance of Rules Engine and execute the
GameDiscount
Rule for the above created game.
RE.RulesEngine rulesEngine = new(workflows);
var resultList = await rulesEngine.ExecuteAllRulesAsync("GameDiscount", game);
foreach (var result in resultList)
{
Console.WriteLine($"Rule: {result.Rule.RuleName}, Result: {result.IsSuccess}, Message: {result.ExceptionMessage}");
}
Execute the
program.cs
file and observe the output.Let's say the business changed its decision to include a new condition to provide discounts. Which is to include GamingCompany to decide the eligibility for 15% discount. Traditionally it requires a code change, but with rules engine. It is as simple as below.
[
{
"WorkflowName": "GameDiscount",
"Rules": [
{
"RuleName": "AwardDiscount5",
"ErrorMessage": "The game doesn't meet the criteria for 5% discount",
"ErrorType": "Error",
"RuleExpressionType": "LambdaExpression",
"Expression": "input1.Genre == \"Sports\" AND input1.Platform == \"PlayStation\""
},
{
"RuleName": "GiveDiscount15",
"ErrorMessage": "The game doesn't meet the criteria for 15% discount",
"ErrorType": "Error",
"RuleExpressionType": "LambdaExpression",
"Expression": "input1.Genre == \"Action\" AND input1.Platform == \"PlayStation\" AND input1.GamingStudio == \"Sony\"" // new condition
}
]
}
]
- Execute the
program.cs
file and observe the output.
Pros:
Letβs go over some of the Pros of JSON based MS Rules engine below
- Customizable and configurable rule expressions.
- Runtime rule injection.
- Easy to change rules on fly without any code changes.
- Custom Actions to fulfil non existing business use case with built-in actions.
Realtime use cases:
Let's look into some Realtime use cases that the MS Rules engine stands out.
- Car and Home insurance companies which requires frequent rule changes to accommodate customized user offerings.
- Mortgage companies which require dynamic rule changes depending on customers, regions, location etc.
- Notification services raising alerts, events based on business rules which can be changed on fly with JSON based MS rules engine
Conclusion:
Hurray! We did complete a simple tutorial with Microsoft JSON based rules engine to define some of our rules, underlying conditions injected from a JSON file. We also explored how to change the rule condition easily by changing the JSON file on fly. We also explored some pros of the Rules Engine, real-time use cases where we can use this rules engine.
All of this is a good base for us, to continue our journey in learning MS Rules engine and its different use cases. Please refer my part 2 of this article series using this link! It will take your through loading these rules from the Databases such as Sqlite. Donβt miss it!
Before I forget this is the GitHub link to the current project with everything we discussed in this article.
See you soon buddy!! π
Top comments (0)