Extracting Business Rules from PL/SQL-Code Version 7, 13.07.03 Michael Rabben Knowledge Engineer Semantec GmbH, Germany
Why? Where are the business rules? Business Rules are already hidden as logic in your programming code Why extract business rules from code? To ease the process of business rule management, you need to extract the business rules from code and place them into a separate repository Major Advantages Business rules will be readable easily accessible understood more easily manageable maintainable reusable changed more quickly -> reduced time to market
Methodology is a defined and well structured reengineering process => Re-engineering is risk-free Example Determining billing due date of insurance contracts
Traditional PL/SQL code (1): PROCEDURE ICIS_Faell_datum_ermitteln ( p_vvs_begin IN DATE, /* Vertragsversionsbeginndatum */ p_aenderungstyp IN VARCHAR2, /* Änderungstyp der Deckung */ p_zws_code IN varchar2, /* Code für Zahlweise */ p_hauptfaelligkeit IN DATE, /* Hauptfälligkeit */ p_dpr_typ IN VARCHAR2, /* DPR-TYP */ p_datum_von IN DATE, /* Datum, ab dem erhoben wird */ p_datum_bis IN DATE, /* Datum, bis zu dem erhoben wird */ p_abr_in_monate IN NUMBER, /* Monatskennzeichen aus ZBD */ p_anz_monate IN NUMBER, /* Anzahl Monate gemäß p_zws_code (Falls 99 >> =0)*/ p_datum_faellig IN OUT DATE, /* Datum, an dem abgerechnet wird */ p_fpr_batch_entitaet IN OUT VARCHAR2, p_fpr_auspraegung IN OUT VARCHAR2, p_fpr_feh_fehlercode IN OUT VARCHAR2, p_fpr_fehlertext IN OUT VARCHAR2, p_returncode IN OUT NUMBER) IS /* Returncode der Prozedur */ /* Ermittlung des Fälligkeitsdatums gemäß Funktionsbeschreibung 9834a in der Fassung vom 2.10.1997 mw, 6.10.1997 mw, 25.11.1997 keine Sonderbehandlung für monatliche Zahlweise */ F_ORACLE CONSTANT NUMBER(1) := -2; F_SACH CONSTANT NUMBER(1) := -3; v_tagesdatum DATE; v_fr_erh_datum DATE; v_plus_monate NUMBER(2); BEGIN p_datum_faellig := TO_DATE ('01.01.1990', 'dd.mm.yyyy'); p_returncode := 0; v_tagesdatum := sysd_te; IF ( p_zws_code IN ('0', '99') OR p_dpr_typ = 'EP') THEN -- Einmalprämie p_datum_faellig := v_tagesdatum;
Traditional PL/SQL code (2): ELSIF p_zws_code = '81' --20020313_029HK: Neue Zahlweise berücksichtigen THEN -- Projektprämie IF p_datum_von < v_tagesdatum THEN p_datum_faellig := v_tagesdatum; ELSE p_datum_faellig := p_datum_von; END IF; --20020313_029HK: THEN --20020313_029HK: -- monatliche Zahlweise --20020313_029HK: ELSIF p_zws_code = '88' --20020313_029HK: IF p_datum_von < v_tagesdatum --20020313_029HK: THEN --20020313_029HK: p_datum_faellig := v_tagesdatum; --20020313_029HK: ELSE --20020313_029HK: p_datum_faellig := p_datum_von; --20020313_029HK: END IF; ELSIF p_zws_code IN ('1', '2', '4', '12') THEN -- ganz-, halb, viertel-jährliche ZW v_plus_monate := ROUND (12 / p_anz_monate); IF p_aenderungstyp = '7' THEN -- Folge p_datum_faellig := ADD_MONTHS (p_datum_von, NVL(p_abr_in_monate, 0)); ELSE -- Neuanlage, Änderungen /* Ermittlung des frühesten Erhebungsdatums */ v_fr_erh_datum := TO_DATE (LTRIM (TO_CHAR (p_hauptfaelligkeit, 'DDMM')) LTRIM (TO_CHAR (p_vvs_begin, 'YYYY')), 'DDMMYYYY'); -- HF im Versicherungsbeginnjahr IF v_fr_erh_datum > p_vvs_begin THEN v_fr_erh_datum := ADD_MONTHS (v_fr_erh_datum, -12); END IF;
Traditional PL/SQL code (3): WHILE v_fr_erh_datum <= ADD_MONTHS (p_vvs_begin, 0 - v_plus_monate) LOOP v_fr_erh_datum := ADD_MONTHS (v_fr_erh_datum, v_plus_monate); END LOOP; v_fr_erh_datum := ADD_MONTHS (v_fr_erh_datum, NVL(p_abr_in_monate,0)); IF p_datum_von < v_fr_erh_datum THEN IF v_fr_erh_datum > v_tagesdatum THEN p_datum_faellig := v_fr_erh_datum; ELSE p_datum_faellig := v_tagesdatum; END IF; ELSE IF p_datum_von > v_tagesdatum THEN p_datum_faellig := p_datum_von; ELSE p_datum_faellig := v_tagesdatum; END IF; END IF; END IF; ELSE p_returncode := F_SACH; p_fpr_batch_entitaet := 'Faelligkeitsdatum'; p_fpr_fehlertext := 'Zahlweise nicht definiert!'; RETURN; END IF; EXCEPTION WHEN OTHERS THEN p_returncode := F_ORACLE; p_fpr_batch_entitaet := 'Faelligkeitsdatum'; p_fpr_auspraegung := NULL; p_fpr_feh_fehlercode := 'ORA-' to_char(sqlcode); p_fpr_fehlertext := SQLERRM; RETURN; END ICIS_Faell_datum_ermitteln;
Extracted Business Rules If type_of_coverage is "single premium" Then update due_date to today If mode_of_billing is "once Then update due_date to today If mode_of_billing is "project_premium Then update due_date to latest among (today, charge_starting_date) If mode_of_billing is "periodic and change is automatic Then update due_date to charge_starting_date + months_to_billing If mode_of_billing is "periodic and change isn t automatic Then update due_date to latest among (today, charge_starting_date, next_periodic_date + months_to_billing)
Remaining PL/SQL code PROCEDURE ICIS_Faell_datum_ermitteln ( p_vvs_begin IN DATE, /* Vertragsversionsbeginndatum */ p_aenderungstyp IN VARCHAR2, /* Änderungstyp der Deckung */ p_zws_code IN varchar2, /* Code für Zahlweise */ p_hauptfaelligkeit IN DATE, /* Hauptfälligkeit */ p_dpr_typ IN VARCHAR2, /* DPR-TYP */ p_datum_von IN DATE, /* Datum, ab dem erhoben wird */ p_datum_bis IN DATE, /* Datum, bis zu dem erhoben wird */ p_abr_in_monate IN NUMBER, /* Monatskennzeichen aus ZBD */ p_anz_monate IN NUMBER, /* Anzahl Monate gemäß p_zws_code (Falls 99 >> =0)*/ p_datum_faellig IN OUT DATE, /* Datum, an dem abgerechnet wird */ p_fpr_batch_entitaet IN OUT VARCHAR2, p_fpr_auspraegung IN OUT VARCHAR2, p_fpr_feh_fehlercode IN OUT VARCHAR2, p_fpr_fehlertext IN OUT VARCHAR2, p_returncode IN OUT NUMBER) IS input_data = BR_interface.input_data; output_data = BR_interface.output_data; BEGIN BR_interface.Map_input_data (input_data "Contract", "beginning_of_new_version", p_vvs_begin, "automatic_change", p_aenderungstyp, "mode_of_billing", p_zws_code, "single_premium", p_dpr_typ, "beginning_of_new_version", p_datum_von, "months_to_billing", p_abr_in_monate, "next_periodic_date", v_fr_erh_datum); BR_Interface.DSR (input_data, output_data); BR_interface.Map_output_data (output_data, "Contract", "due_date", due_date); END ICIS_Faell_datum_ermitteln;
How is the re-engineering done? Overview 1. Analyze code 2. Create decision table 3. Generate V&V Report (automatically) 4. Generate decision tree (automatically) 5. Verify decision tree with code 6. Generate & optimize rules (automatically) 7. Replace code by calls to Business Rule repository
Process of re-engineering 1. Analyze code and extract conditions and actions to create decision table Conditions Actions 1. Due today 2. Due latest among (today, charge_starting_date) 3. Due charge_starting_date + months_to_billing 4. Due latest among (today, charge_starting_date, next_periodic_date)
Process of re-engineering 2. Create decision table and enter decisions into table Full table Optimized table
Process of re-engineering 3. Generate V & V Report and verify correctness and completeness of analysis
Process of re-engineering 4.+ 5. Create decision tree and verify analysis by comparing it with code
Process of re-engineering 6. Generate and optimize rules (a) Generated Rules (see bottom left)
Process of re-engineering 6. Generate and optimize rules (b) If type_of_coverage is "single premium" Then update due_date to today If mode_of_billing is "once Then update due_date to today Generated rules (in text form) If mode_of_billing is "project_premium Then update due_date to latest among (today, charge_starting_date) If mode_of_billing is "periodic and change is automatic Then update due_date to charge_starting_date + months_to_billing If mode_of_billing is "periodic and change isn t automatic Then update due_date to latest among (today, charge_starting_date, next_periodic_date + months_to_billing)
Process of re-engineering 6. Generate and optimize rules (c) You can modify rules easily
Process of re-engineering 7.Replace code by calls to Business Rules Repository (a) Call to Business Rule Engine: BR_Interface.DSR(input_data, output_data) DSR = DECISION SUPPORT REQUEST Data mapping: Map_input_data ( input_data BR_Class_name, BR_attribute_name_1, variable_name_1, ) BR_attribute_name_n, variable_name_n Map_output_data ( output_data, BR_Class_name, BR_attribute_name_1, variable_name_1, BR_attribute_name_n, variable_name_n )
Process of re-engineering 7.Replace code by calls to Business Rules Repository (b) Example PROCEDURE ICIS_Faell_datum_ermitteln ( p_vvs_begin IN DATE, /* Vertragsversionsbeginndatum */ p_aenderungstyp IN VARCHAR2, /* Änderungstyp der Deckung */ p_zws_code IN varchar2, /* Code für Zahlweise */ p_hauptfaelligkeit IN DATE, /* Hauptfälligkeit */ p_dpr_typ IN VARCHAR2, /* DPR-TYP */ p_datum_von IN DATE, /* Datum, ab dem erhoben wird */ p_datum_bis IN DATE, /* Datum, bis zu dem erhoben wird */ p_abr_in_monate IN NUMBER, /* Monatskennzeichen aus ZBD */ p_anz_monate IN NUMBER, /* Anzahl Monate gemäß p_zws_code (Falls 99 >> =0)*/ p_datum_faellig IN OUT DATE, /* Datum, an dem abgerechnet wird */ p_fpr_batch_entitaet IN OUT VARCHAR2, p_fpr_auspraegung IN OUT VARCHAR2, p_fpr_feh_fehlercode IN OUT VARCHAR2, p_fpr_fehlertext IN OUT VARCHAR2, p_returncode IN OUT NUMBER) IS input_data = BR_interface.input_data; output_data = BR_interface.output_data; BEGIN BR_interface.Map_input_data (input_data "Contract", "beginning_of_new_version", p_vvs_begin, "automatic_change", p_aenderungstyp, "mode_of_billing", p_zws_code, "single_premium", p_dpr_typ, "beginning_of_new_version", p_datum_von, "months_to_billing", p_abr_in_monate, "next_periodic_date", v_fr_erh_datum); BR_Interface.DSR (input_data, output_data); BR_interface.Map_output_data (output_data, "Contract", "due_date", due_date); END ICIS_Faell_datum_ermitteln;
Summary Our approach is a well-defined and structured re-engineering process Drastic reduction of traditional code Visibility of business rules Less cost of maintenance Allowing better management