Tuesday, November 4, 2008

Broken Rules in the Domain

Technorati Tags:

I've been using the broken rules scheme in my past few projects and I like the way it raises the visibility of rules by making them a first class citizen of the domain. Rules will no longer be buried with the other "stuff" of a business transaction.  No sir, these rules have names! 

Take for example, a couple of rules around issuing inventory for a title insurance company:

  1. Provide the ability for management to block requests for new inventory from a specific agency.
  2. Provide the ability to stop issuing inventory to agencies whose contract has been canceled.
  3. Provide the ability to stop issuing inventory to agencies that have failed to meet minimum reporting requirements .
  4. Provide the ability to stop agencies from being issued inventory they are not authorized for.

Speaking close to the language of the domain, these rules are grouped as a set.  And for the sake of brevity, let us assume that these rules are independent of each other:

 

        private List<ValidationRuleBase> GetRulesForIssuingInventory(Agent agent, PolicyType requestedPolicyType, List<PolicyType> policyTypesAuthorizedForTheAgent)

        {

            var rules = new List<ValidationRuleBase>();

 

            rules.Add(new ShouldNot_Issue_To_Agents_That_Has_Been_Blocked_To_RequestInventory(agent));

            rules.Add(new ShouldNot_Issue_To_Cancelled_Agents(agent));

            rules.Add(new ShouldNot_Issue_To_Agents_That_Has_Not_Met_Minimum_Reporting_Requirements(agent));

            rules.Add(new Agent_ShouldBe_Authorized_To_Order_The_Requested_PolicyTypes(agent,requestedPolicyType, policyTypesAuthorizedForTheAgent));

 

            return rules;

        }

 


In this particular scenario, this set of rules hangs off the domain object 'Order'  and is checked when a request for inventory is received.

 

        public void PlaceAnOrder(Order order)

        {

            List<ValidationRuleBase> rules;

            List<ValidationRuleBase> brokenRules;

 

            rules = order.GetRulesForIssuingInventory(order.Agent, order.PolicyType, order.Agent.AuthorizedPolicyTypes);

            brokenRules = GetBrokenRules(rules);

            if (brokenRules.Count>0)

            {

                throw new BusinessRuleException(brokenRules);

            }

 

            // continue with the rest of the placing an order transaction

        }

 

 

An example of a rule implementation is:

 

    public class Agent_ShouldBe_Authorized_To_Order_The_Requested_PolicyTypes : ValidationRuleBase

    {

        private Agent agent;

        private PolicyType requestedPolicyType;

        private List<PolicyType> policyTypesAuthorizedForTheAgent;

 

        public Agent_ShouldBe_Authorized_To_Order_The_Requested_PolicyTypes(Agent agent, PolicyType requestedPolicyType, List<PolicyType> policyTypesAuthorizedForTheAgent)

        {

            this.agent                            = agent;

            this.requestedPolicyType              = requestedPolicyType;

            this.policyTypesAuthorizedForTheAgent = policyTypesAuthorizedForTheAgent;

        }

 

        override public bool IsValid

        {

            get

            {

                return this.policyTypesAuthorizedForTheAgent.Contains(this.requestedPolicyType);

            }

        }

 

        public override string Message

        {

            get

            {

                return String.Format("Policy Type {0} is not in the list of policy types Agent {1} can order.",this.requestedPolicyType.Name,this.agent.Name);

            }

        }

    }

 

 

Note in the IsValid() override that the heart of the rule is for the most part just plain logic and is devoid of UI and persistence concerns.   This leads to a more focused expression of the rule.