DEV Community

Cover image for Building A JSON Rules Engine from Scratch with MSRulesEngine β€” Part 1
vamsi dogiparthi
vamsi dogiparthi

Posted on • Edited on • Originally published at Medium

Building A JSON Rules Engine from Scratch with MSRulesEngine β€” Part 1

Image description
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:

  1. Download .NET 6 or Higher version SDK.
  2. Download Visual Studio.
  3. Create a new Console Application.
  4. 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; }
    }
}
Enter fullscreen mode Exit fullscreen mode

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; }

    }
Enter fullscreen mode Exit fullscreen mode

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\""
        }
      ]
    }
  ]
Enter fullscreen mode Exit fullscreen mode
  • Set the Folder to be part of the build package inside the .csproj file
 <ItemGroup>
    <None Update="RulesFiles\**\*">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode
  • 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; }
}
Enter fullscreen mode Exit fullscreen mode
  • 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
};
Enter fullscreen mode Exit fullscreen mode
  • 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}");
}
Enter fullscreen mode Exit fullscreen mode
  • 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
      }
    ]
  }
]
Enter fullscreen mode Exit fullscreen mode
  • 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)