BT5
Design & Code
11/17/2016 11:30:00 AM
Them’s the Rules: Using a Rules Engine
to Wrangle Complexity
Presented by:
Micah Breedlove
iostudio
Brought to you by:
350 Corporate Way, Suite 400, Orange Park, FL 32073
888--‐268--‐8770 ·∙ 904--‐278--‐0524 - info@techwell.com - http://www.stareast.techwell.com/
Micah Breedlove
iostudio
Micah Breedlove is a senior developer and dev team lead at iostudio. He began
working with PHP in late 1998 and has been developing full time using Symfony
since 2009. Micah has written and maintained applications for companies
including Procter & Gamble, Walmart, and the National Guard. Previously a
senior developer at Franklin American Mortgage, he was instrumental in building
the PHP development team. Micah lives with his wife and three kids in Nashville
where he enjoys growing fruit trees, making maple syrup, and vegetable
gardening. Micah is a firearms enthusiast and enjoys spending time at the range.
Find more about Micah at his website or follow him on Twitter @druid628.
Them’s the Rules
by
Micah Breedlove
Using a Rules Engine to Wrangle
Complexity
PHP Dev since ‘98
Symfony Evangelist
@druid628
Sr. Developer / Team Lead @ iostudio
Who is this guy?
Rules Engines
Drools
Ruler (PHP)
Ruler
Written by Justin Hileman(BobTheCow)
GitHub: https://github.com/bobthecow/Ruler
Packagist: ruler/ruler
Some definitions
Conditional Logic
“hypothetical logic (of a proposition) consisting
of two component propositions... so that the
proposition is false only when the antecedent is
true and the consequent false.”
How we handle the unknown.
Rules Engines
“A business rules engine is a software system
that executes one or more business rules in a
runtime production environment.”
“An added layer of complexity to carry out,
often complicated, business logic of the
application”
in other words...
Business Logic?
● Company policies
● Legal Regulations
Examples
● Discounts on products purchased
○ Combination of products
○ Subscribed services
● Additional Fees
○ Geographical assessment
○ Risk assessment
What is wrong with this?
if ( $obj->getName() == ‘abc’) { } elseif ($obj->getName() == ‘def’) {
} elseif ($obj->getName() == ‘ghi’) { } elseif ($obj->getName() == ‘jkl’ ) {
} elseif ($obj->getName() == ‘mno’ ) {
} elseif ($obj->getName() == ‘pqr’ && $obj->getClient()->getCategory() !==
‘hotChkn’) {
}elseif ($obj->getName() == ‘Hg(CNO)2’ && $obj->getClientKey() == ‘tuco’){
}elseif ($obj->getName() == ‘blueSky’ && $obj->getClientKey() ==
‘losPollosHermanos’) {
} elseif ($obj->getName() == ‘blueSky’ && $obj->getClientKey() ==
‘heisenberg’){}
...
Should be kept trimmed,
maintained and under control
Or else...
Pandemonium Ensues
Conditional statements are like Kudzu
Fixable?
Something to …
● Abstractly handle multiple conditions
With
● Minimal conditional logic
and is
● Reusable
Simplifies a block into a generic block
Support more than a simple/singular condition
Abstraction
Minimize Conditional Logic
● Drop the ever-building conditionals
● Replace with smaller defined block
Reusability
Ability to support all existing conditions
Individual rules can be evaluated in other
RuleSets
The saga begins…
if ($bond->getClient()== ‘client1’ && $event->getName() == ‘bond.create’) {
$this->calculateClient1BondAdjustments($bond);
} elseif ($bond->getClient() == ‘client1’ && $event->getName() == ‘payment.made’) {
$this->calculateClient1Payment($bond, $payment);
} elseif ($bond->getClient() == ‘client2’ && $event->getName() == ‘bond.create’) {
$this->calculateClient2BondAdjustments($bond);
} elseif ($bond->getClient() == ‘client3’ && $event->getName() == ‘bond.create’) {
$this->calculateClient3BondAdjustments($bond);
} ...
$rule = $ruleFactory->getBondRules();
$clientRule = [
‘district’ => ‘collegedale’,
‘district_fee’ => ‘25’,
‘fee_adjustment’ => ‘+’
];
$context = new Context([
‘bond_district’ => $bond->getDistrict(),
‘district’ => $clientRule[‘district’],
‘district_fee’ => $clientRule[‘district_fee’],
‘fee_adjustment’ => $clientRule[‘’fee_adjustment’]
]);
$rule->execute($context);
Rule Elements
Ruler - Elements of the Equation
3 Parts
● Context
● Rule
● Execution/Evaluation
Context - All Applicable Data
Variable Data:
‘bond_district’ => ‘collegedale’,
Control Data:
‘district’ => ‘collegedale’,
‘district_fee’ => ‘25’,
‘fee_adjustment’ => ‘+’
● Variable Data
○ Request
○ User Specific
(includes session)
● Control Data
○ Specific to the Rule
Rule
$rule = $rb->create(
$rb['bond_district']->EqualTo($rb['district'])
);
How the Context Stacks Up
● How to evaluate the data
● What to evaluate
Variable Data:
‘bond_district’ => ‘collegedale’,
Control Data:
‘district’ => ‘collegedale’,
‘district_fee’ => ‘25’,
‘fee_adjustment’ => ‘+’
Rule (cont.)
Compound Rules
$rule = $rb->create(
$rb->LogicalAnd(
$rb['bond_district']->EqualTo($rb['district']),
$rb['bond_amount']->GreaterThan('20000')
)
);
Evaluation/Execution
Evaluate - (boolean) Did it meet the condition?
Execution - (void) If it’s true do something
Evaluation Example
Rule:
$rule = $rb->create(
$rb['bond_district']->EqualTo($rb['district'])
);
$status = $rule->evaluate($context);
echo $status; // false
Control Data:
‘district’ =>
‘collegedale’,
‘district_fee’ => ‘25’,
‘fee_adjustment’ => ‘+’
Variable Data:
‘bond_district’ => ‘collegedale’
Execution Example
$rule->execute($context);
// output: An additional mileage fee will be charged.
Control Data:
‘district’ =>
‘collegedale’,
‘district_fee’ => ‘25’,
‘fee_adjustment’ => ‘+’
Variable Data:
‘bond_district’ => ‘collegedale’
Rule:
$rule = $rb->create(
$rb['bond_district']->EqualTo($rb['district']),
function() {
echo 'An additional mileage fee will be charged.';
}
);
if ($bond->getClient()== ‘client1’ && $event->getName() == ‘bond.create’) {
$this->calculateClient1BondAdjustments($bond);
} elseif ($bond->getClient() == ‘client1’ && $event->getName() == ‘payment.made’) {
$this->calculateClient1Payment($bond, $payment);
} elseif ($bond->getClient() == ‘client2’ && $event->getName() == ‘bond.create’) {
$this->calculateClient2BondAdjustments($bond);
} elseif ($bond->getClient() == ‘client3’ && $event->getName() == ‘bond.create’) {
$this->calculateClient3BondAdjustments($bond);
} ...
Finally this…
...becomes this
$ruleObject = $rulesRepo->get($event->getName()); // assume bond.create
$context = $ruleObject->getContext();
$context[‘bond’] = $bond;
$rule = $ruleObject->getRule();
$rule->execute($context); // one rule to rule them all
Questions?
Demo Time
Thanks!
Slides: http://sthen.es/mqh4ub20
citations
Jesse (Breaking Bad) - http://replygif.net/i/1323.gif
Kudzu - http://2.bp.blogspot.com/_RTswKzWDHeM/SE198zJTgzI/AAAAAAAAA80/ZKj4haQbiSE/s320/kudzu+house.jpg
MC Hammer - http://24.media.tumblr.com/26d1484f43ff55401f8ead9d03432bb9/tumblr_moctg0LbmC1sn8pc2o1_400.gif

Them’s the Rules: Using a Rules Engine to Wrangle Complexity

  • 1.
    BT5 Design & Code 11/17/201611:30:00 AM Them’s the Rules: Using a Rules Engine to Wrangle Complexity Presented by: Micah Breedlove iostudio Brought to you by: 350 Corporate Way, Suite 400, Orange Park, FL 32073 888--‐268--‐8770 ·∙ 904--‐278--‐0524 - info@techwell.com - http://www.stareast.techwell.com/
  • 2.
    Micah Breedlove iostudio Micah Breedloveis a senior developer and dev team lead at iostudio. He began working with PHP in late 1998 and has been developing full time using Symfony since 2009. Micah has written and maintained applications for companies including Procter & Gamble, Walmart, and the National Guard. Previously a senior developer at Franklin American Mortgage, he was instrumental in building the PHP development team. Micah lives with his wife and three kids in Nashville where he enjoys growing fruit trees, making maple syrup, and vegetable gardening. Micah is a firearms enthusiast and enjoys spending time at the range. Find more about Micah at his website or follow him on Twitter @druid628.
  • 3.
    Them’s the Rules by MicahBreedlove Using a Rules Engine to Wrangle Complexity PHP Dev since ‘98 Symfony Evangelist @druid628 Sr. Developer / Team Lead @ iostudio Who is this guy?
  • 4.
    Rules Engines Drools Ruler (PHP) Ruler Writtenby Justin Hileman(BobTheCow) GitHub: https://github.com/bobthecow/Ruler Packagist: ruler/ruler
  • 5.
    Some definitions Conditional Logic “hypotheticallogic (of a proposition) consisting of two component propositions... so that the proposition is false only when the antecedent is true and the consequent false.” How we handle the unknown.
  • 6.
    Rules Engines “A businessrules engine is a software system that executes one or more business rules in a runtime production environment.” “An added layer of complexity to carry out, often complicated, business logic of the application” in other words... Business Logic? ● Company policies ● Legal Regulations
  • 7.
    Examples ● Discounts onproducts purchased ○ Combination of products ○ Subscribed services ● Additional Fees ○ Geographical assessment ○ Risk assessment What is wrong with this? if ( $obj->getName() == ‘abc’) { } elseif ($obj->getName() == ‘def’) { } elseif ($obj->getName() == ‘ghi’) { } elseif ($obj->getName() == ‘jkl’ ) { } elseif ($obj->getName() == ‘mno’ ) { } elseif ($obj->getName() == ‘pqr’ && $obj->getClient()->getCategory() !== ‘hotChkn’) { }elseif ($obj->getName() == ‘Hg(CNO)2’ && $obj->getClientKey() == ‘tuco’){ }elseif ($obj->getName() == ‘blueSky’ && $obj->getClientKey() == ‘losPollosHermanos’) { } elseif ($obj->getName() == ‘blueSky’ && $obj->getClientKey() == ‘heisenberg’){}
  • 8.
    ... Should be kepttrimmed, maintained and under control Or else... Pandemonium Ensues Conditional statements are like Kudzu
  • 9.
    Fixable? Something to … ●Abstractly handle multiple conditions With ● Minimal conditional logic and is ● Reusable Simplifies a block into a generic block Support more than a simple/singular condition Abstraction
  • 10.
    Minimize Conditional Logic ●Drop the ever-building conditionals ● Replace with smaller defined block Reusability Ability to support all existing conditions Individual rules can be evaluated in other RuleSets
  • 11.
    The saga begins… if($bond->getClient()== ‘client1’ && $event->getName() == ‘bond.create’) { $this->calculateClient1BondAdjustments($bond); } elseif ($bond->getClient() == ‘client1’ && $event->getName() == ‘payment.made’) { $this->calculateClient1Payment($bond, $payment); } elseif ($bond->getClient() == ‘client2’ && $event->getName() == ‘bond.create’) { $this->calculateClient2BondAdjustments($bond); } elseif ($bond->getClient() == ‘client3’ && $event->getName() == ‘bond.create’) { $this->calculateClient3BondAdjustments($bond); } ... $rule = $ruleFactory->getBondRules(); $clientRule = [ ‘district’ => ‘collegedale’, ‘district_fee’ => ‘25’, ‘fee_adjustment’ => ‘+’ ]; $context = new Context([ ‘bond_district’ => $bond->getDistrict(), ‘district’ => $clientRule[‘district’], ‘district_fee’ => $clientRule[‘district_fee’], ‘fee_adjustment’ => $clientRule[‘’fee_adjustment’] ]); $rule->execute($context); Rule Elements
  • 12.
    Ruler - Elementsof the Equation 3 Parts ● Context ● Rule ● Execution/Evaluation Context - All Applicable Data Variable Data: ‘bond_district’ => ‘collegedale’, Control Data: ‘district’ => ‘collegedale’, ‘district_fee’ => ‘25’, ‘fee_adjustment’ => ‘+’ ● Variable Data ○ Request ○ User Specific (includes session) ● Control Data ○ Specific to the Rule
  • 13.
    Rule $rule = $rb->create( $rb['bond_district']->EqualTo($rb['district']) ); Howthe Context Stacks Up ● How to evaluate the data ● What to evaluate Variable Data: ‘bond_district’ => ‘collegedale’, Control Data: ‘district’ => ‘collegedale’, ‘district_fee’ => ‘25’, ‘fee_adjustment’ => ‘+’ Rule (cont.) Compound Rules $rule = $rb->create( $rb->LogicalAnd( $rb['bond_district']->EqualTo($rb['district']), $rb['bond_amount']->GreaterThan('20000') ) );
  • 14.
    Evaluation/Execution Evaluate - (boolean)Did it meet the condition? Execution - (void) If it’s true do something Evaluation Example Rule: $rule = $rb->create( $rb['bond_district']->EqualTo($rb['district']) ); $status = $rule->evaluate($context); echo $status; // false Control Data: ‘district’ => ‘collegedale’, ‘district_fee’ => ‘25’, ‘fee_adjustment’ => ‘+’ Variable Data: ‘bond_district’ => ‘collegedale’
  • 15.
    Execution Example $rule->execute($context); // output:An additional mileage fee will be charged. Control Data: ‘district’ => ‘collegedale’, ‘district_fee’ => ‘25’, ‘fee_adjustment’ => ‘+’ Variable Data: ‘bond_district’ => ‘collegedale’ Rule: $rule = $rb->create( $rb['bond_district']->EqualTo($rb['district']), function() { echo 'An additional mileage fee will be charged.'; } ); if ($bond->getClient()== ‘client1’ && $event->getName() == ‘bond.create’) { $this->calculateClient1BondAdjustments($bond); } elseif ($bond->getClient() == ‘client1’ && $event->getName() == ‘payment.made’) { $this->calculateClient1Payment($bond, $payment); } elseif ($bond->getClient() == ‘client2’ && $event->getName() == ‘bond.create’) { $this->calculateClient2BondAdjustments($bond); } elseif ($bond->getClient() == ‘client3’ && $event->getName() == ‘bond.create’) { $this->calculateClient3BondAdjustments($bond); } ... Finally this…
  • 16.
    ...becomes this $ruleObject =$rulesRepo->get($event->getName()); // assume bond.create $context = $ruleObject->getContext(); $context[‘bond’] = $bond; $rule = $ruleObject->getRule(); $rule->execute($context); // one rule to rule them all Questions?
  • 17.
  • 18.
    Slides: http://sthen.es/mqh4ub20 citations Jesse (BreakingBad) - http://replygif.net/i/1323.gif Kudzu - http://2.bp.blogspot.com/_RTswKzWDHeM/SE198zJTgzI/AAAAAAAAA80/ZKj4haQbiSE/s320/kudzu+house.jpg MC Hammer - http://24.media.tumblr.com/26d1484f43ff55401f8ead9d03432bb9/tumblr_moctg0LbmC1sn8pc2o1_400.gif