A flexible and extensible rules engine built as an Azure Function App that allows you to define, configure, and execute business rules dynamically based on context and criteria.
This project provides a robust rules engine implementation that can:
- Execute different rules based on configurable criteria
- Handle dynamic rule resolution using expression trees
- Support multiple rule types (Escalate, Forward, etc.)
- Provide HTTP endpoints for different services
- Scale automatically with Azure Functions
- .NET 6.0 SDK
- Azure Functions Core Tools (for local development)
- Visual Studio 2022 or Visual Studio Code with Azure Functions extension
Adi.FunctionApp.RulesEngine/
βββ Documentation/ # Documentation project
β βββ Adi.FunctionApp.RulesEngine.Documentation/
βββ Source/ # Main source code
β βββ Adi.FunctionApp.RulesEngine.Service/ # Azure Function App
β β βββ RulesEngineService.cs # Main HTTP trigger function
β β βββ Startup.cs # Dependency injection configuration
β β βββ appsettings.json # Rules configuration
β β βββ host.json # Function host settings
β βββ Shared/
β βββ Adi.FunctionApp.RulesEngine.Domain/ # Core business logic
β βββ Builder/ # Rule expression builders
β βββ Executor/ # Rule execution engine
β βββ Interfaces/ # Abstractions and contracts
β βββ Models/ # Data models
β βββ Rules/ # Rule implementations
βββ Testing/ # Test projects
βββ Adi.FunctionApp.RulesEngine.UnitTest/
βββ Adi.FunctionApp.RulesEngine.IntegrationTest/
git clone https://github.com/AdiThakker/Adi.FunctionApp.RulesEngine.git
cd Adi.FunctionApp.RulesEnginedotnet buildcd Source/Adi.FunctionApp.RulesEngine.Service
func startThe function will be available at http://localhost:7071
Rules are configured in appsettings.json using a flexible criteria-based system:
{
"RulesConfiguration": {
"Configurations": [
{
"Criteria": "Source-ContextType == Order-Product1",
"Rules": [ "Escalate", "Forward" ]
},
{
"Criteria": "Source == Account",
"Rules": [ "Forward" ]
},
{
"Criteria": "Source-Error == Order-QuantityError",
"Rules": ["Escalate"]
}
]
}
}- Use
==for equality comparisons - Combine properties with
-(e.g.,Source-ContextType) - Access dynamic parameters using property names (e.g.,
Source-Error)
Endpoint: GET/POST /{service}
Executes rules based on the service type and returns the results.
- AccountService:
GET http://localhost:7071/AccountService - OrderService:
GET http://localhost:7071/OrderService
# Account Service
curl http://localhost:7071/AccountService
# Order Service
curl http://localhost:7071/OrderServiceAccount Forwarded
The RuleContext class carries the information needed for rule evaluation:
public class RuleContext
{
public string Source { get; set; } // Service source (e.g., "Account", "Order")
public string ContextType { get; set; } // Context type for more specific matching
public Dictionary<string, string> Parameters { get; init; } // Dynamic parameters
}- HTTP request comes in with service name
RulesExecutorbuilds aRuleContextbased on the service- Configuration criteria are evaluated against the context
- Matching rules are retrieved and executed
- Results are aggregated and returned
public class EscalateRule : IRule<RuleContext, RuleResult>
{
public Task<RuleResult> ExecuteAsync(RuleContext input)
{
return Task.FromResult(new RuleResult(input, $"{input.Source} Escalated"));
}
}public class ForwardRule : IRule<RuleContext, RuleResult>
{
public Task<RuleResult> ExecuteAsync(RuleContext input)
{
return Task.FromResult(new RuleResult(input, $"{input.Source} Forwarded"));
}
}dotnet test Testing/Adi.FunctionApp.RulesEngine.UnitTestdotnet test Testing/Adi.FunctionApp.RulesEngine.IntegrationTestdotnet test- Create Azure Function App:
az functionapp create --resource-group myResourceGroup \
--consumption-plan-location westus2 \
--runtime dotnet \
--functions-version 4 \
--name myFunctionApp \
--storage-account mystorageaccount- Deploy the function:
func azure functionapp publish myFunctionApp- Right-click the
Adi.FunctionApp.RulesEngine.Serviceproject - Select "Publish..."
- Choose "Azure" and follow the wizard
- Create a new rule class:
public class MyCustomRule : IRule<RuleContext, RuleResult>
{
public Task<RuleResult> ExecuteAsync(RuleContext input)
{
// Your custom logic here
return Task.FromResult(new RuleResult(input, "Custom logic executed"));
}
}- Register the rule in Startup.cs:
services.AddTransient<IRule<RuleContext, RuleResult>, MyCustomRule>();- Add configuration in appsettings.json:
{
"Criteria": "Source == MyService",
"Rules": ["MyCustomRule"]
}Update the Dispatcher method in RulesEngineService.cs:
var response = service switch
{
"AccountService" => /* existing logic */,
"OrderService" => /* existing logic */,
"MyNewService" => rulesExecutor.Execute(new RuleContext() { Source = "MyNew" }),
_ => "Invalid request"
};- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- Follow existing code style and patterns
- Add unit tests for new functionality
- Update documentation for new features
- Ensure all tests pass before submitting PR
This project is licensed under the MIT License - see the LICENSE file for details.
For questions, issues, or contributions, please:
- Open an issue on GitHub
- Contact the maintainer: AdiThakker
β Star this repository if you find it helpful!