Tags: Database | Oracle

The never ending story of business rules

Rulegen business rules toon koppelaars

Door Toon Koppelaars

Bij elk informatiesysteem komen we het fenomeen business rules tegen. De term wordt vaak in de mond genomen zonder dat deze helder gedefinieerd wordt. Wat is nou precies een business rule? Er is geen algemeen geaccepteerde definitie voor dit concept.

Er is echter wel een ander, zeer helder gedefinieerd (en ook algemeen geaccepteerd) concept, wat erg in de buurt komt van wat men bedoelt met de term business rules. Dit betreft het concept van data integriteit constraints.

Data integriteit constraints

Data integriteits constraints zijn beperkingsregels op de data die we toestaan in de database. Het zijn dus regels die beschrijven welke vulling we wel en welke we niet toestaan in de tabellen van ons database design. In de praktijk zien we dat business rules vaak één-op-één te vertalen zijn naar een beperkingsregel in het onderliggende database design van een informatiesysteem. De business rules die niet als constraint beschouwd kunnen worden, blijken vaak rules te zijn (beperkingen, condities, automatische akties, etc.) met betrekking tot de functies en bedrijfsprocessen die het informatiesysteem ondersteunt. Het is belangrijk om duidelijk onderscheid te maken tussen de data integriteit constraints, en deze overige rules (laten we ze voor het gemak process rules noemen). De constraints hebben met de data te maken. De process rules hebben met de business logic van het informatiesysteem te maken.

Ten tijde van implementeren blijken process rules en constraints twee wezenlijk verschillende zaken te zijn.

  • Code om process rules af te dwingen wordt min of meer altijd automatisch op één plek geïmplementeerd. Namelijk in het betreffende process waarop die (process) rule van toepassing is.
  • Code om constraints te controleren dient echter in principe in elk process geïmplementeerd te worden wat transacties genereert op de bij de constraint betrokken tabelstructuren. Dit betreft doorgaans meerdere processen.
Met andere woorden als je niet oppast, worden constraints zeer slecht onderhoudbaar, omdat je code om die constraints af te dwingen op meerdere plekken in je informatiesysteem hebt geïmplementeerd. Het traceren van alle plekken waar code is geschreven om een gegeven constraint te implementeren, is doorgaans in dure en zeer foutgevoelige effort.

Een voor de hand liggende manier om dit dreigend onderhoudsprobleem van constraints te voorkomen, is om alle constraint implementerende code éénmalig en liefst zo dicht mogelijk bij de data te implementeren. Een SQL DBMS biedt ons hiertoe twee opties: declaratieve constraints en tabel triggers.

Implementeren van constraints

Bij de implementatie van data integriteit constraints is het raadzaam om een classificatie van constraints te hanteren. Hierdoor ben je in staat om standaarden met betrekking tot het implementeren van constraints, per klasse van constraints te ontwikkelen. De algemeen geaccepteerde classificatie van data integriteit constraints gaat uit van de scope die een constraint in het database design heeft. Deze classificatie blijkt tevens een erg praktische te zijn, omdat de scope van een constraint een sterke correlatie heeft met de benodigde implementatie effort voor die constraint.
  • Attribuut constraints beperken de waarden van één attribuut (scope = één column value). Bijvoorbeeld: salaris moet in de range van 500 tot 10000 zijn.
  • Tuple constraints beperken combinaties van attribuut waarden binnen één tuple (row). Bijvoorbeeld: een president verdient altijd meer dan 8000.
  • Tabel constraints beperken combinaties van tuples binnen één tabel. Bijvoorbeeld: maximaal één manager per afdeling. Database constraints beperken combinaties van tabellen binnen de database. Bijvoorbeeld: de som van de salarissen binnen één afdeling mag het afdelingsbudget niet overschrijden.
  • Database constraints beperken combinaties van tabellen binnen de database. Bijvoorbeeld: de som van de salarissen binnen één afdeling mag het afdelingsbudget niet overschrijden.

NB: er is nog een vijfde klasse van constraints, de transitie constraints. Deze laten we in dit artikel verder buiten beschouwing.

De ANSI SQL standaard levert ons declaratieve check constraints, primary en unique key constraints, en foreign key constraints. Deze zijn ook allemaal beschikbaar in de SQL implementatie van de gangbare RDBMS-en, waaronder die van Oracle. Deze SQL constraints stellen ons in staat om een groot deel van de data integriteit constraints declaratief te implementeren. Doorgaans doe je dit ten tijde van het aanmaken van de tabelstructuren in je database design. We zullen nu één en ander toelichten met een welbekende EMP-DEPT database design.

Hier is de definitie van een EMP tabel. Embedded zijn de definities van diverse constraints, waaronder enkele die we hierboven bij de classificatie als voorbeeld hebben genoemd. (1) En de definitie van een DEPT tabel met enkele constraints. (2) Middels een check constraint kunnen we alle attribuut en tuple constraints declaratief implementeren. De primary key en foreign key zijn taal constructies om respectievelijk een vaak voorkomende tabel, en een vaak voorkomende database constraint declaratief te implementeren. Wat in de ANSI SQL standaard ook beschreven wordt, zijn declaratieve assertion constraints. Echter geen enkele DBMS leverancier heeft deze (zinvol) geïmplementeerd. Hieronder is het taal constructie diagram voor assertion constraints. (3) De <condition> component representeert een willekeurige boolean expressie waarin ook subqueries voor mogen komen. Een assertion is een losstaand object, i.e. niet direct gekoppeld aan één of meerdere tabel structuren. De ANSI SQL standaard beschrijft dat het de taak van het DBMS is om alle gedefi nieerde assertions te controleren tijdens transacties: alle <condition> expressies van opgevoerde assertions dienen ten alle tijde tot TRUE te evalueren. Stelt u zich eens voor dat assertion constraints beschikbaar zouden zijn. Dan zijn we in staat om alle constraints op een declaratieve manier in het DBMS te specificeren en hoeven we zelf verder geen enkele regel code te schrijven om deze constraints af te dwingen. Constraints zijn dan zeer goed te onderhouden: we hoeven slechts op één plek, in het DBMS, de assertion aan te passen als de constraint gewijzigd moet worden.

We kijken even naar een voorbeeld. Stel dat in het nevenstaande EMP-DEPT database design de volgende business rule geïmplementeerd moet worden: Er mag maximaal één manager (i.e. een employee met JOB=’MANAGER”) per afdeling zijn. Deze busines rule kan als volgt met een assertion constraint gespecificeerd worden. (4) Hier is nog een voorbeeld. De som van de salarissen van de employees in een afdeling moet onder het afdelingsbudget (SALBUDGET) liggen. (5) Het is bijzonder jammer dat declaratieve assertions niet beschikbaar zijn. Ik ben er volledig van overtuigd dat U ze zou gebruiken, net zoals U nu ook check, key en foreign key constraints gebruikt. Ze lossen immers het beheersprobleem rondom constraints voor eens en altijd op. Maar dat niet alleen. Assertion support zou ook aanzienlijke besparingen opleveren bij het bouwen van informatiesystemen, en ze geven U gegarandeerde data integriteit. Met name dit laatste aspect is bijzonder moeilijk bereikbaar als je zelf complexe procedurele code moet gaan schrijven om constraints af te dwingen.

Laten we eens onderzoeken hoe we zelf middels tabel triggers constraints zouden kunnen afdwingen. We willen een efficiënte implementatie. Dat wil zeggen we willen tijdens een transactie een constraint alleen controleren als deze transactie ook dusdanig is dat de constraint potentieel geschonden zou kunnen worden. Bijvoorbeeld in het geval van de “maximaal één manager per afdeling” is het alleen zinvol om te controleren wanneer:
  • de EMP tabel bij de transactie betrokken is, en wel
  • als in EMP een manager ge-insert wordt, of
  • als in EMP een employee tot manager gepromoveerd wordt, of
  • als in EMP een manager naar een andere afdeling verhuist.
Bij alle andere type transacties op EMP is het overbodig om deze constraint te controleren. Verder observeren we dat we deze constraint per afdeling kunnen controleren. Dus als in afdeling 10 een manager ge-insert wordt, hoeven we alleen maar afdeling 10 te controleren. Alle afdelingen controleren zou in dat geval zeer ineffi ciënt zijn. Dus idealiter willen we in een after statement trigger op de EMP tabel de volgende constraint validerende query runnen. (6) Hierbij is :p_deptno een bind variabele met als waarde de te controleren afdeling. Als de query een resultaat teruggeeft, dan forceren we een roll-back van de transaction middels een raise_application_error.

In het geval van de “som van salaris per afdeling moet onder het afdelingsbudget liggen” constraint is het alleen zinvol om te controleren wanneer:
  • de EMP tabel, of de DEPT tabel bij de transactie betrokken zijn, en wel,
  • als in EMP een nieuwe employee toegevoegd wordt, of
  • als in EMP het salaris van een bestaande employee verhoogd wordt, of
  • als in EMP een employee naar een andere afdeling verhuist, of
  • als in DEPT het salarisbudget van de afdeling verlaagd wordt.
In transacties die niet aan één van bovenstaande kenmerken voldoen, hoeft deze constraint niet gecontroleerd te worden. De controle zelf zou middels volgende query kunnen plaatsvinden (:p_deptno representeert de te controleren afdeling). (7) Het eerder genoemde “wanneer is controle zinvol” kan ook middels queries gespecificeerd worden, door de rows die zojuist ge-insert, ge-update, of gedelete zijn, te inspecteren. Deze zojuist ge-DML-de rows, worden in de literatuur het transitie-effect van een DML statement genoemd. Zo is er een transitieeffect voor een insert-statement genaamd inserted_rows, voor een update-statement updated_rows, en voor een deletestatement deleted_rows. Als deze transitieeffecten benaderbaar zouden zijn vanuit after statement triggers, bijvoorbeeld als temporary tables, dan kan op een elegante manier bewaakt worden dat de constraint validerende queries alleen afgaan als dat zinvol is.

Laten we het eerste voorbeeld gebruiken om dit toe te lichten. Het was alleen zinvol om de “maximaal één manager per afdeling” constraint te controleren als er een nieuwe manager ge-insert wordt. Dit kunnen we specifi ceren door een query op inserted_rows te schrijven die nagaat of dat het geval is. (8) We gaan nu alleen de validatie query uitvoeren als bovenstaande query een resultaat oplevert. Door de betreffende deptno waarde te selecteren, zijn we in staat om deze als bind-value door te geven aan de validatie query. Op soortgelijke wijze schrijven we een query op updated_rows die aangeeft wanneer we de constraints in geval van een update-statement moeten controleren: (9) Bovenstaande query detecteert de update statements die ofwel iemand tot manager promoveren, ofwel een manager naar een andere afdeling verhuizen. In beide gevallen willen we de constraint voor de new_deptno afdeling gaan controleren. Natuurlijk moeten we nog de nodige PL/SQL eromheen coderen om te zorgen dat de queries afgaan, de bind-value wordt doorgegeven, en indien nodig de raise_application_error wordt uitgevoerd. Het is natuurlijk nog mooier als we deze code helemaal laten genereren middels het RuleGen framework (zie www.rulegen.com).

The neverending story

Rulegen business rules never ending De afgelopen decennia hebben we overal code geïmplementeerd om data integriteit constraints te controleren.
  • We hebben code in forms gebouwd en vaak gedupliceerd.
  • We hebben code in centrale stored procedures gebouwd.
  • We hebben code in triggers gebouwd (complex, mutating table probleem).
  • We hebben code gegenereerd met een framework als CDM*RuleFrame.
  • We hebben trucs met materialized views + check constraints bedacht.
  • We hebben code in de JEE/Java laag geïmplementeerd (ADF-BC).
  • We hebben code in mid-tier oriented ‘rules engines’ gespecificeerd (bijv. Oracle Business Rules).
  • We dreigen nu weer code in Apex pages te gaan bouwen en dupliceren.
  • En we kunnen nu code genereren met een tweede generatie database framework als RuleGen.
Hoe U ook besluit code voor data integriteit constraints te gaan implementeren belangrijk is en blijft, om het op één plek te doen. Doet U dat niet dan is één van de meest belangrijke aspecten van uw database onbeheersbaar geworden. Met name constraint controlerende code leent zich uitstekend om grotendeels gegenereerd te worden.

Data integriteits constraints zullen altijd een integraal onderdeel van de informatiesystemen vormen die we nu en in de toekomst ontwikkelen. Pas als DBMS leveranciers ons een efficiënte implementatie van assertion constraints gaan bieden, zal dat waarschijnlijk het laatste hoofdstuk van de neverending story rondom constraints gaan worden. Dan zal de belofte van Codd’s relationele data model eindelijk ingevuld zijn.

Toon Koppelaars is managing partner van RuleGen BV.
U kunt hem bereiken via toon(at)rulegen.com.

Lees meer over RuleGen
Ga terug naar We Love IT uitgave #2 - 2008
Advertentie