user1358072
user1358072

Reputation: 447

Need a good architecture for rules of validations

I hope you guys can give me some good suggestions. I am thinking how to make a good architecture for C# development. I am trying my best to explain scenarios because im not good at english:

1) Two classes: Blue Bank and Red Bank

2) Third class : Rules Of Validations

3) Blue and Red Banks have several fields (values) such as AccountNumber, Amount, InvoicePeriod etc... Example here (xml):

Blue Bank

  <AccountNumber>Maria 5987593457</AccountNumber>
  <Amount>200.00</Amount>
  <InvoicePeriod>1</InvoicePeriod>

Red Bank

  <AccountNumber>8529458</AccountNumber>
  <Amount>300.00</Amount>
  <InvoicePeriod>0</InvoicePeriod>

Red/Blue Banks have some same rules of validations like Amount fields that must be numberic. But Red/Blue Banks have different rules of validations -- AccountNumber field must be alphanumberic in Blue Bank whereas AccountNumber must be numberic in Red Bank otherwise failed. InvoicePeriod field must be default 1 in Red Bank whereas it must be default 0 in Blue Bank otherwise failed.

My idea is:

I want to create each class of Red / Blue Bank for different rules of validation and then i also create a rules of validation class for the same rules that Blue/Red banks have.

My code here:

Blue Bank class:

Red Bank class:

RulesOfValidation class

How does it work with dictonary <,> with these classes? Or any better suggestion with your example codes?

Your help would be highly appreciated.

Upvotes: 4

Views: 1193

Answers (3)

Mateusz
Mateusz

Reputation: 2317

I would go with IRule interface with Validate() method which could be implemented in concrete validation classes which could contain validation logic. Then you will be able to precess several custom rules in bank. Pass the list of IRule type objects to bank class and run Validate() on each passing bank parameters. So each bank could validate itself based on passed rules.

interface IRule
{
    bool Validate(Bank someBank);
}

abstract class Bank
{
    public string AccountNumber;
    public string Amount;
    public string InvoicePeriod;

    private List<IRule> listOfRules = new List<IRule>();

    public void ValidateAllRules(){
        foreach (var ite in listOfRules){
            ite.Validate(this);
            //if validation will not pass then I don't know what you want to do ;)
        }
    }

    public void AddRule(IRule rule)
    {
        listOfRules.Add(rule);
    }
}

class RedBank : Bank
{
    public RedBank(){
        listOfRules.Add(new SimpleRule());
        listOfRules.Add(new SimpleRule2());
    }
}

class SimpleRule : IRule
{
    public bool Validate(Bank someBank)
    {
        return someBank.AccountNumber.Contains("567");
    }
}

class SimpleRule2 : IRule
{
    public bool Validate(Bank someBank)
    {
        return someBank.Amount.Contains(".") && someBank.InvoicePeriod.Contains("-");
    }
}

Upvotes: 3

&#237;gor
&#237;gor

Reputation: 1164

You should use System.ComponentModel.DataAnnotations

First Create abstract class Bank

abstract class Bank
{
    #region fields
    private List<string> errorMessages = new List<string>();
    #endregion

    #region publioc methods
    public virtual void Validate()
    {
        ValidateRulesAtributes();
    }

    #endregion

    #region helper methods
    private void ValidateRulesAtributes()
    {
        var validationContext = new ValidationContext(this, null, null); //ValidationContext -> Reference System.ComponentModel.DataAnnotations
        var result = new List<ValidationResult>();
        Validator.TryValidateObject(this, validationContext, result, true);



        result.ForEach(p => { errorMessages.Add(p.ErrorMessage); });

        if (errorMessages.Any())
            throw new Exception(errorMessages.Aggregate((m1, m2) => String.Concat(m1, Environment.NewLine, m2)));
    }

    protected void Validate(List<string> messages)
    {
        if (errorMessages == null)
            errorMessages = new List<string>();

        if (messages != null)
            messages.ForEach(p => { errorMessages.Add(p); });
        ValidateRulesAtributes();

    }
    #endregion
    #region properties
    //Abstract to indicate Validation atributes 

    public abstract string AccountNumber { get; set; }

    public abstract double Amount { get; set; }
    public abstract int InvoicePeriod { get; set; }
    #endregion

}

Second Create Red and Blue Bank with data anotations

class BlueBank : Bank
    {
        //All is ok, no validate 
        public override string AccountNumber { get; set; }

        public override double Amount { get; set; }

        public override int InvoicePeriod { get; set; }

    }
class RedBank : Bank
    {
        [Required()]
        public override string AccountNumber { get; set; }

        public override double Amount { get; set; }
        [Range(0,0)]
        public override int InvoicePeriod { get; set; }


        public override void Validate()
        {
            List<string> errors=new List<string>();
            if (AccountNumber != "Test")
                errors.Add("Worng Account");
            base.Validate(errors);
        }

    }

Use Atributes to validate ranges, required,etc.. Override Validate() to complex validations

This a simple example

class Program
    {
        static void Main(string[] args)
        {
            RedBank red = new RedBank();
            red.AccountNumber = "Test";

            red.Amount=0;
            red.Validate(); //this No fail

            red.InvoicePeriod = 3; //This Fail;
            red.Validate(); 

            red.AccountNumber = "PD";
            red.Validate(); //this fal:

        }
    }

Upvotes: 0

giacomelli
giacomelli

Reputation: 7407

Specification Pattern is a good option for your problem. You can create a specification class for common bank rules and others specification classes for each specific need.

There is some c# libraries for this pattern:

Upvotes: 5

Related Questions