Fachliche Regeln

NextIn ISA sind die Systembausteine versionierte, austauschbare Module, die fachliche oder technische Anforderung aus einer eindeutigen Anforderungsliste realisieren und Funktionalitäten u.U. über individuelle, öffentliche Services zur Verfügung stellen.

Ergänzend dazu bietet ISA noch das einfache – aber leistungsfähige Konzept “fachlicher” Regeln (Business Rules) zusammen mit einem zentralen Regelkatalog (siehe auch Katalog Online Demo). Eine Regel ist dabei die Zusammenfassung einer beliebigen Logik und/oder Metadatenbeschreibung unter einem eindeutigen Namen. Das können fachliche Zusammenhänge sein wie zum Beispiel:

  • Regel: “com.my.Rabattaktion-4711” – Wenn mehr als vier Produkte im Warenkorb sind – dann wird im Zeitraum Januar bis Februar 2013 ein Rabatt von 10% auf die Produktgruppen X, Y, Z gewährt und es wird jedem 50sten Kunden auf den dies zutrifft ein Gutschein in Höhe von 5 € per Mail zugestellt – sofern er Online Kunde ist.
  • Prozess Regel: “com.my.AntragsWiedervorlage” – Wenn der Antrag zur Genehmigung eingereicht wurde und noch nicht entschieden ist – dann wird eine Wiedervorlage für in 4 Wochen zu einer erneuten Prüfung des Entscheidungsstandes erstellt.

Oder eine einfache Prüfregel wie z.B.:

  • Regel: “com.my.LaengenRegel” – Wenn die Länge eines Wertes nicht mindestens 3 Zeichen und maximal 8 Zeichen beträgt – dann ist der Wert nicht valide.

Oder auch eine Metabeschreibung wie z.B.

  • Regel: “com.my.UserId” – Ein Datenelement UserId (ausführlicheres Beispiel)
    • ist vom Typ String
    • die Mindestlänge beträgt 3 Zeichen
    • das Format ist “[a-zA-Z0-9]+”
    • das Standardlabel “de” ist “Benutzer ID”
    • … etc.

Eine Regel besteht aus 3 elementaren Teilen:

  • dem Namen: ”com.my.Rabattaktion-4711″, “com.my.LaengenRegel” oder “com.my.UserId”
  • den Parametern oder Daten: 4 Produkte, Zeitraum, 10% Rabatt, UserID, 3 Zeichen, String, Format etc.
  • und der Regel Logik (entfällt bei Metadaten): Wenn … dann … anderenfalls

ISA unterstützt die Kapselung von beliebigen Logiken in einem Regelobjekt mit einer einfachen, vereinheitlichten Schnittstelle.

public interface Rule extends Serializable{

	/**
	 */
	public Map applyTo(Object pValues);

}

Die Methode “applayTo” wendet, die in ihr implementierte Regellogik, auf den übergebenen Input an und erzeugt einen der Regel entsprechenden Output in Form von Key/Value Paaren. Der Input kann dabei ein einzelner Wert oder eine Liste von beliebig vielen Werten (Key/Value Parameter) sein. Der Name der Klasse entspricht dem Namen der Regel. Wichtig ist zudem, dass eine Regel in Bezug auf die In- und Outputdaten zustandslos ist und auch ansonsten keine Daten hält, die umgebungsabhängig sind (Serializable).

Das Entscheidende an diesem Konzept ist, dass fachliche Logik einen eindeutigen Namen bekommt und ihre If-Then-Else Semantic in einem eigenständig handhabbaren (bauen, speichern, einfügen etc.) Objekt gekapselt wird. Auf diese Weise wird sie zum einen eindeutig identifizierbar und zum anderen wird sie austauschbar, was wiederum bedeutet, dass sie sich mit den Anforderungen entwickeln kann ohne dass nutzende System dadurch zu beeinträchtigen.

Regeln zur Datenvalidierung
Eine weitere Anwendung dieses Prinzips ist die Validierung von Daten und Feldinhalten. Der Mechanismus basiert auf der Konvention, dass bei einer Validierung, die im Prinzip ein boolsches Ergebnis im Sinne von – ist valide oder nicht – erzeugt, eine leere Rückgabe ein valides Ergebnis anzeigt und eine nicht-leere Rückgabe die Informationen darüber enthält, was nicht korrekt ist. Schematisches Beispiel:

//Feldlängenvalidierung in einzelnen Schritten
//eine Regel erzeugen
//mit einer Minimallänge von 4 Zeichen
Rule lRule = new SizeRule(4);

//die Regel anwenden
Map lResult = lRule.applyTo("123");

//das Ergebnis auswerten
if(lResult.isEmpty()==false){
	String lMsg = lResult.get("message");
	System.out.println(lMsg);
}

>
> Die Länge muss minimal [4] Zeichen betragen.
>

Sicherstellung des DRY Prinzips 
Das Motiv für den Einsatz von Regelobjekten, ist die Kapselung und Zentralisierung von fachlicher Logik. Zum einen zur Vermeidung von Redundanz (siehe DRY Prinzip). Und zum anderen um fachliche Logik identifizierbar und damit sicher pflegbar und austauschbar zu gestalten.

Ein System kann so z.B. einen globalen Regelkatalog oder ein Regelmodul zur Verfügung stellen, in dem alle fachlichen Regeln zentral hinterlegt sind und das dynamisch um neue oder geänderte Regeln erweitert werden kann. Das Prinzip ist ähnlich wie das bekannte Datadictionary – nur das hier nicht nur rein statische Metainformationen hinterlegt werden können, sondern auch dynamische Funktionen bzw. Logiken und deren Steuerdaten. Beispiel:

//Für das Login gilt z.B. die Regel
//userid und password
//müssen mindestens 3 bzw. 8 Zeichen lang sein
//die Regel heißt com.my.LoginDataRule
//und ist in der Login Modulspezifikation definiert

//Regel aus Katalog holen
Rule lRule = ruleCatalog.getRule("com.my.LoginDataRule");

//aktuelle Inputdaten ermitteln
Map lValues = new HashMap(2);
lValues.put("user.name", tfUser.getValue());
lValues.put("user.password", tfPassword.getValue());

//Regel anwenden
lLoginCommand.setEnabled(lRule.applayTo(lValues).isEmpty());

Das entscheidende an dem Beispiel ist, dass sowohl die Logik selbst (if…then…else) als auch ihre festen Daten (3 und 8) nicht Teil der individuellen, potentiell mehrfach vorkommenden Codierung sind – sondern nur an einer einzeigen Stelle und unter einem eindeutigen Namen im Regelkatalog liegen. Auf diese Weise kann die GUI eines Clients gesteuert werden und gleichzeitig ein Service auf dem Server, der für das Anlegen neuer Accounts verantwortlich ist. Beide nutzen zur Validierung die gleiche, eindeutige Regel1.

Übliche Verstreuung von Logiken und deren Daten

//Eine ManagedBean eines Service z.B. com.my.CreateUserAccount
...
	@Size(3, 8)
	private String userid = "";
	@Size(8, 16)
	private String password = "";
...

//Code in einem Swing Client Login Dialog
...
	validateUserCredentials(){
		if(tfUser.getValue()>=3 && tfUser.getValue()<=8){
			//do something
		}
	}
...

//Code in einem JavaScript Web Client Login Dialog
...
function checkLoginData(userid, password){

	if(userid.length>=3 && userid.length<=8){
		//do something
	}
...

Die Codefragmente zeigen drei Dinge

  1. fachliche Logiken und deren Steuerdaten sind oft hardcodiert und zwingen daher dazu, bei fachlichen Änderung das System neu zu bauen
  2. semantisch/fachlich gleiche Logiken und deren Steuerdaten werden häufig auch an verschiedenen Stellen und in verschiedenen Systemteilen verwendet (Redundanz)
  3. die Codierung hat keinen eindeutig nachvollziehbaren Bezug zu einer fachlichen Anforderung und umgekehrt, es fehlt ein eindeutiger Name

Alle drei Punkte sind gängige Praxis und führen zu Aufwänden, Inkonsistenzen, Unflexibilität und Fehlern – was in Summe die Risiken erhöht und Geld kostet2.

Das ISA Rule Konzept schließt daher auch die Web Entwicklung, die auf der Client Seite häufig mit JavaScript arbeitet mit ein. Das geschieht indem Regeln direkt in JavaScript implementiert werden können und gleichzeitig über einen Logik und damit Redundanz freien Wrapper in Java zur Verfügung stehen.

Beispiel SizeRule Wrapper Implementierung Java:
isa.common.rules.SizeRule.java

public class SizeRule extends AbstractScriptableRule {

	private Object[] values = null;

	/**
	 */
	protected SizeRule(Object[] pArgs) {
		values = pArgs;
	}

	/**
	 * Delegiert applayTo an das JavaScript für diese Regel
	 */
	public Map applyTo(Object pValue) {
		return jsApplyTo(values, pValue);
	}
}

Beispiel SizeRule Implementierung in JavaScript:
isa.common.rules.SizeRule.js

function SizeRule(pMin, pMax) {
	//msg-start
	var messages = ['too small', 'too large'];
    	//msg-end

	var min = pMin;
	var max = pMax;

	this.applyTo = function (pValue) {
		var lResult = new Object();
		if(min>-1 && pValue.length < min) {
			lResult['message'] = messages[0].replace('{0}', min);
		}
		if(max>-1 && pValue.length > max) {
			lResult['message'] = messages[1].replace('{0}', max);
		}
		return lResult;
	};
};

Die Nutzung von JavaScript (seit Release 6 Bestandteil der JSE) als Implementierungssprache für Regeln schließt nicht nur die Web Entwicklung transparent und konsistent ein – sondern bietet auch fachliche Dynamik unabhängig vom ISA Modulsystem selbst – weil Scripte unabhängig von Compilierung und Paketierung gehandhabt und verwaltet werden können.

Auf diese Weise können beliebige Regeln und Metadaten in einem zentralen Regelkatalog hinterlegt werden, der unabhängig von den compilierten Modulen des Systems gepflegt und geändert werden kann – wobei diese Veränderungen ohne weiteren Schritt immer direkt verfügbar sind.

//die DRY Variante
//Regelpaket aus dem Katalog lesen
String lPackage = "isa.common.rules";
Map lRulePackage = ruleService.getRules(lPackage, Locale.GERMAN);

//Regeln lokal verfügbar machen
rules.defineRules(lRulePackage);

//Dynamische Regel erzeugen
Rule lSizeRule = rules.newRule(lPackage+".SizeRule", new Object[]{3, 8});

//Regel nutzen
if(!lSizeRule.applayTo("123").isEmpty()){
	//do something
}

//die EXTRA DRY Variante
//auch die Datenelementregeln aus dem Katalog lesen
Map lDataElementDocs = ruleService.getDataElements(lPackage);

//und Regeln anhand einer Definition erzeugen
DataElementDoc lElemDoc = (DataElementDoc)lDataElementDocs.get(lPackage+".UserId");

lSizeRule = rules.newRule(lPackage+".SizeRule", new Object[]{lElemDoc.getMin(), lElemDoc.getMax()});

...

_______________________
1: Regeln sind unabhängige, kontextfreie Funktionen – ein Element, das in der Realität der Fachlichkeit häufig vorkommt – das aber gerade in objektorientierten Umgebungen wenig beachtet und eingesetzt wird, weil hier in erster Linie in Abstraktionen und hierarchischen Strukturen gedacht wird.

2: Die Beispiele klingen zunächst trivial, aber folgendes Szenario ist durchaus realistisch: 1. Januar das System geht in Version 1.0 in die Produktion. 10. Januar eine Gesetzesänderung, die in der Spezifikation übersehen wurde, verlangt die Umstellung von mindestens 5 auf 8 Zeichen für ein Passwort. Ausgehend von den beispielhaften Codefragmenten muss nun außerhalb des normalen Releasezyklus, der vielleicht 3 Monate beträgt, ein Zwischenrelease (1.1) gebaut werden. Dabei müssen alle Stellen im System gefunden und geändert werden, an denen die Passwortlänge geprüft wird. Gleichzeitig müssen auch alle Spezifikationen (Swing UseCase, Web Login, AccoutService Fachkonzept) angepasst werden. Allerdings kann konstruktionsbedingt niemand mit Sicherheit sagen an welchen Stellen überall solche Änderungen notwendig sind. Daher wird übersehen, dass eine schon immer genutzte Bibliotheksfunktion des Rich Clients die Passwortlänge aus einer Konfigurationsdatei ließt. 20. Januar Release 1.1 geht in Produktion. 30. Januar, nach langen Suchen stellt man fest, dass eine selten genutzte Funktion des Rich Client weiter 5 stellige Passwörter zulässt. Die Konfigurationsdatei, in der das steht, ist aber Bestandteil von 100 Client Installationen … usw.

Die Szene verdeutlicht das Problem von Redundanz selbst bei harmlos erscheinenden Logiken und Informationen und zeigt gleichzeitig wie einfach eine zentrale, änderbare Information das Problem erst gar nicht aufkommen läßt.