Sto lavorando su un'interfaccia per l'implementazione delle regole di business al fine di migliorare SOLID-ity; quindi posso spostare molta logica dai controller API Web e in una libreria aziendale. Il problema comune è che un'azione dovrebbe verificarsi se una o più condizioni sono soddisfatte e alcune di queste condizioni sono probabilmente richieste in tutto il sistema con diverse azioni come risultato finale. Ho fatto qualche ricerca e ho trovato il codice qui sotto. Questo è conforme a un modello di design esistente? Ho cercato nell'elenco dei GoF e non ho trovato nessuna corrispondenza lì.
/// <summary>
/// Use for designing a business rule where conditions are evaluated and the actions are executed based on the evaluation.
/// Rules can be chained by setting the "Action" as another business rule.
/// </summary>
/// <typeparam name="TCondition">The type of the condition or conditions.</typeparam>
/// <typeparam name="TAction">The type of the action or actions to be executed.</typeparam>
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <seealso cref="Core.Interfaces.IBusinessRule" />
internal interface IBusinessRule<TCondition, TAction, TResult> : IBusinessRule
where TCondition : IRulePredicate where TAction : IRuleAction<TResult>
{
ICollection<TAction> Actions { get; set; }
ICollection<TCondition> Preconditions { get; set; }
}
internal interface IBusinessRule
{
IEnumerable Results { get; }
RuleState State { get; }
Task Execute();
}
public enum RuleState
{
None,
Initialized,
InProgress,
Faulted,
FailedConditions,
Completed
}
public interface IRulePredicate
{
bool Evaluate();
}
public interface IRuleAction<TResult>
{
Task<TResult> Execute();
}
public abstract class RuleBase<TCondition, TAction, TResult> :
IBusinessRule<TCondition, TAction, TResult> where TCondition : IRulePredicate
where TAction : IRuleAction<TResult>
{
public ICollection<TResult> Results { get; } = new List<TResult>();
public ICollection<TCondition> Preconditions { get; set; } = new List<TCondition>();
public ICollection<TAction> Actions { get; set; } = new List<TAction>();
IEnumerable IBusinessRule.Results => Results;
public RuleState State { get; private set; } = RuleState.Initialized;
public async Task Execute()
{
State = RuleState.InProgress;
try
{
var isValid = true;
foreach (var item in Preconditions)
{
isValid &= item.Evaluate();
if (!isValid)
{
State = RuleState.FailedConditions;
return;
}
}
foreach (var item in Actions)
{
var result = await item.Execute();
Results.Add(result);
}
}
catch (Exception)
{
State = RuleState.Faulted;
throw;
}
State = RuleState.Completed;
}
}
public class TestRule1 : RuleBase<FakePredicateAlwaysReturnsTrue, WriteHelloAction, string>
{
public TestRule1()
{
Preconditions = new[] { new FakePredicateAlwaysReturnsTrue() };
Actions = new[] { new WriteHelloAction() };
}
}
public class FakePredicateAlwaysReturnsTrue : IRulePredicate
{
public bool Evaluate()
{
return true;
}
}
public class WriteHelloAction : IRuleAction<string>
{
public async Task<string> Execute()
{
return await Task.Run(() => "hello world!");
}
}
public static class Program
{
public static async Task Main()
{
IBusinessRule rule = null;
try
{
rule = new TestRule1();
await rule.Execute();
foreach (string item in rule.Results)
{
// Prints "hello world!"
Console.WriteLine(item);
}
}
catch (Exception ex)
{
if (rule != null && rule.State == RuleState.Faulted)
{
throw new Exception("Error in rule execution", ex);
}
throw;
}
}
}