Stored Procedure
Prozedurale Logik in der Datenbank, oft fuer Validierung, Berechnung oder Massenverarbeitung.
Tabellen, Views, Packages, Trigger, Stored Procedures und Reporting-Abfragen als versteckte Fachlogik.
Viele Legacy-Systeme enthalten einen grossen Teil ihrer Fachlogik in Stored Procedures, Packages, Triggern, Views und Datenkorrekturskripten. Das ist nicht automatisch schlecht: Datenbanklogik kann sehr stabil, performant und transaktional konsistent sein.
Problematisch wird es, wenn Anwendungscode und Datenbanklogik denselben fachlichen Zustand aus unterschiedlichen Perspektiven veraendern. Dann entstehen schwer testbare Seiteneffekte: Trigger setzen Status, Packages korrigieren Beträge, Views verstecken Filterregeln.
Modernisierung beginnt mit Dateninventar und Regelkatalog. Danach kann man entscheiden, welche Regeln in der Datenbank bleiben, welche in Services wandern und wo ein Parallelbetrieb mit Vergleichsabfragen notwendig ist.
Prozedurale Logik in der Datenbank, oft fuer Validierung, Berechnung oder Massenverarbeitung.
Automatisch ausgeloeste Logik bei Insert, Update oder Delete.
Vorberechnete Sicht fuer Reporting oder Performance.
Nachvollziehbarkeit, wo Daten entstehen, transformiert und konsumiert werden.
Die Beispiele sind bewusst nicht minimalistisch. Sie zeigen typische Artefakte, die man in echten Legacy-Analysen findet: Schnittstellenverträge, Containerkonfiguration, SQL/PL-SQL, Jobdefinitionen, Queue-Regeln oder Adaptercode.
CREATE OR REPLACE PACKAGE billing_rules AS
PROCEDURE approve_invoice(p_invoice_id IN NUMBER, p_user IN VARCHAR2);
FUNCTION calculate_tax(p_net_amount IN NUMBER, p_country IN VARCHAR2) RETURN NUMBER;
END billing_rules;
/
CREATE OR REPLACE PACKAGE BODY billing_rules AS
PROCEDURE approve_invoice(p_invoice_id IN NUMBER, p_user IN VARCHAR2) IS
v_status VARCHAR2(20);
BEGIN
SELECT status INTO v_status FROM invoice WHERE id = p_invoice_id FOR UPDATE;
IF v_status <> 'READY' THEN
RAISE_APPLICATION_ERROR(-20010, 'Invoice is not ready for approval');
END IF;
UPDATE invoice SET status = 'APPROVED', approved_by = p_user, approved_at = SYSTIMESTAMP
WHERE id = p_invoice_id;
END;
FUNCTION calculate_tax(p_net_amount IN NUMBER, p_country IN VARCHAR2) RETURN NUMBER IS
BEGIN
RETURN CASE WHEN p_country = 'AT' THEN p_net_amount * 0.20 ELSE p_net_amount * 0.19 END;
END;
END billing_rules;
CREATE OR REPLACE TRIGGER trg_invoice_status_hist
AFTER UPDATE OF status ON invoice
FOR EACH ROW
BEGIN
INSERT INTO invoice_status_history(invoice_id, old_status, new_status, changed_at)
VALUES (:OLD.id, :OLD.status, :NEW.status, SYSTIMESTAMP);
END;
public class InvoiceApprovalRepository {
private final DataSource dataSource;
public void approve(long invoiceId, String user) throws SQLException {
try (Connection con = dataSource.getConnection();
CallableStatement st = con.prepareCall("{call billing_rules.approve_invoice(?, ?)}")) {
st.setLong(1, invoiceId);
st.setString(2, user);
st.execute();
}
}
}
| Aspekt | Beschreibung |
|---|---|
| Fachliches Risiko | Unklare Verantwortung fuer Kundenbestand fuehrt zu widerspruechlichen Entscheidungen zwischen Alt- und Neusystem. |
| Technisches Risiko | Oracle und angrenzende Komponenten werden isoliert betrachtet; Laufzeitkopplung bleibt verborgen. |
| Betriebsrisiko | Fehlerkanal, Monitoring, Restart oder manuelle Klaerung sind nicht ausreichend dokumentiert. |
| Migrationsrisiko | Neue Architektur uebernimmt Daten oder Schnittstellen, ohne fachliche Invarianten und historische Sonderfaelle abzusichern. |
| Pattern | Einsatz in diesem System |
|---|---|
| Strangler Fig Pattern | Neue Funktionalitaet vor das Altsystem setzen und Altanteile schrittweise herausloesen. |
| Anti-Corruption Layer | Altbegriffe, technische Codes und Datenformate vom neuen Domänenmodell trennen. |
| Facade | Komplexe Legacy-Operationen hinter klaren fachlichen Use-Case-Methoden kapseln. |
| Adapter | Protokolle und Formate wie SOAP, MQ, Copybook, SQL oder File in Ports uebersetzen. |
| Golden Master Test | Bestehendes Verhalten mit Referenzdaten erfassen und gegen neue Implementierung vergleichen. |
Suche in einer fiktiven Datenbank alle Trigger und Packages rund um Rechnung. Ordne sie fachlichen Regeln zu. Entscheide pro Regel: bleibt in DB, wird gekapselt, wird getestet oder wandert in Application Service.