Op het net zijn over dit onderwerp vele artikelen te vinden. Vaak beschrijven ze een onderdeel van een pattern of is de oplossing geschikt in een omgeving waar meer dan ervaren developers actief zijn. De auteur was van mening om zelf ermee aan de slag te gaan met als doelstelling:

  • Een oplossing die door medior ontwikkelaars kan worden onderhouden en doorontwikkeld
  • Een oplossing waarbij de engine onafhankelijk van het domein kan worden ingezet

 

Het principe van de engine werkt op basis van een zogenaamd “Policy”. Een policy wordt beschouwd als een verzameling van regels, de “Specifications” welke een enkelvoudige controle uitvoeren. De controle wordt uitgevoerd op een domein object. Het domein object (het object welke wordt gecontroleerd) wordt gecontroleerd samen met een attribuut, het “Control object”. Dit “Control Object” kan worden gezien als een voorstel tot aanpassing van een bepaalde waarde in het domein object. Samengevat ziet de samenhang er statisch als volgt uit:

business rule engine 1Om de policy en haar specificaties echter te gebruiken moeten er nog 2 stappen worden doorlopen:

  • Het instantiëren van het policy object zelf
  • Het toevoegen van de specificaties door de policy aan zichzelf

De tweede stap wordt opgelost door middel van een PolicyBuilder. Deze moet worden gezien als een soort bootstrapper voor een policy klasse. De PolicyBuilder is een generieke klasse die het domein object en control object specificeert en waar vervolgens via de AddSpecification methode de specificaties kunnen worden toegevoegd.

De implementatie van de Policy ziet er als volgt uit:

Business Rule Engine 2Het instantiëren van de policy (het gebruiken ervan), doen we door het maken van het object. De interne constructor zorgt dat de PolicyBuilder wordt aangeroepen en de juiste specificaties worden toegevoegd. Het instantiëren van een policy gebeurt meestal in een service applicatie. In deze applicatie kan vervolgens na het aanmaken van het Policy object de Apply methode worden aangeropen voor het toepassen van de business rules.

Het resultaat van de Apply methode wordt gegeven door middel van collectie van RuleCheckResult objecten. Een RuleCheckResult heeft een uitkomst “Outcome” en een bericht die de overtreding van de controle aangeeft.

Code documenteert zichzelf als het goed is…. daarom een voorbeeld.

We nemen een eenvoudige Person class als domein object met daarin een geboortedatum:

Business Rule Engine 3De policy die we willen implementeren is dat een persoon een minimale leeftijd moet hebben.  De controle zelf implementeren we als een Specification als volgt:

Business Rule Engine 4De policy wordt vervolgens als volgt geïmplementeerd:

Business Rule Engine 5Het voorbeeld is een eenvoudig scenario. Via de PolicyBuilder is het mogelijk om meerdere specifications toe te voegen aan de policy zodat een complexere samenhang van de controles ontstaat. De stelregel moet zijn dat een klasse die een Specification implementeert maar 1 heel klein deel van de policy controleert. Het resultaat is dan dat specifieke meldingen kunnen worden gegeven over een bepaalde overtreding (bepaald door de Specification). In bijvoorbeeld in een console app, kunnen de volgende regels worden gebruikt om de policy toe te passen in een runtime scenario:

Business Rule Engine 6In het bovenstaande scenario wordt de policy gebruikt om te controleren of de leeftijd van de persoon minimaal 18 jaar is.

Voor opdrachtgevers heeft de auteur het patroon zoals hier beschreven, meermaals toegepast. Door het loskoppelen van Policy, Specification en PolicyBuilder is het patroon uitstekend geschikt om unit testen mee te implementeren. Het resultaat is een testbaar product waarbij “happy flow” en “unhappy flow” situaties eenvoudig kunnen worden nagebootst.