-1

I want to use design pattern to solve the problem I have at hand. I have 3 set of business rules and depending on each business rules, I determine the payment gateway to call.

  • If amount is 0 to 1000 use xx gateway.
  • If amount is 1100 to 2000 use xxx gateway.

I have created my set of different business rules like this:

public interface IRules
{
   void Validate(decimal amount);
}

Implementations:

public class CheapRule : IRules
{
   public void Validate(decimal amount)
   {
      if (amount <= 20)
      {
         //use cheap Gateway
      }
   }
}

I define a single interface called IPaymentGateway with two concrete implementations CheapPaymentGateway and ExpensivePaymentGateway.

public interface IPaymentGateway 
{
   void MakePayment(PaymentModel model);
}

Implementations:

public class CheapPaymentGateway : IPaymentGateway
{
   public void MakePayment(PaymentModel model)
   {
      //Use CheapPaymentGateway
   }
}

Depending on the amount entered by the user from client code, at runtime I want one of one of the gateway to be used based on the defined business rules. Please how do i achieve this using .net core. Also how do i inject the dependencies at startup since one interface uses multiple implementations. any guidelines will be much appreciated.

cly
  • 661
  • 3
  • 13

2 Answers2

1

This problem is a common case of the least-cost routing problem.

If I had to express your business logic quickly with a snip of code I might write:

//Least-Cost Routing
List<PaymentGateway> gateways = this.GetListOfGatewaysSomehow();
var leastCostGateway = gateways.OrderBy( x => 
    x.CalculateCost(paymentModel.Amount)
    ).First();
this.MakePayment(paymentModel, leastCostGateway);

I would start there, and design my object model to support it.

This way of expressing the logic in code is something a new engineer could pick up immediately.

As for a general design pattern with abstract "rules" that "validate" things... YAGNI.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • The given example will always evaluate cost of all gateways. This could be okay but should be mentioned in case the calculation is resource heavy and the implemented logic needs just the first matching. In that case a `gateways.First(x => x.IsMatching(paymentModel.Amount))` would be better. – cly Feb 21 '21 at 10:26
0

I would modify your IRules.Validate() to return a bool representing that the rule matches or not.

I would implement an evaluator service class, where the rule's and paymentGateway's connection would be defined: any set of rules, any combination of them with exactly one or zero paymentGateway. Something like this pseudo-code:

 foreach ruleset
    if rules-in-current-ruleset.All(i=>i.Validate(amount))
       return paymentGateway-associated-with-current-ruleset
 return default-paymentGateway

A ruleset may look like this:

class Ruleset
{
   public IEnumerable<IRules> Rules;
   public IPaymentGateway AssociatedPaymentGateway;
}

So you can setup any rulesets and evaluate with the evaluator service to get the appropriate paymentGateway. But watch out the ordering of rulesets because the above implementation stops at the first matching one!

cly
  • 661
  • 3
  • 13