Die Backend-Schnittstelle für die Finanzverwaltung (Teil 2)

Im ersten Teil habe ich das initiale Modell und die ersten Anforderungen der Anwendung beschrieben. Nun geht es um die Backend-Schnittstelle, die die beschriebenen Anwendungsfälle ermöglicht.

Das Projekt für die Anwendung

Als Basis für – insbesondere das Backend – dient Spring Boot. Mit Hilfe einer unterstützenden Entwicklungsumgebung oder auf der Startseite ist das Projekt schnell konfiguriert.

Ich ergänze das Projekt mit den Startern bzw. Abhängigkeiten

  • Spring Web,
  • Lombok,
  • Spring Boot DevTools,
  • Spring Data JPA und
  • H2

Für die (REST-)Schnittstelle benötige ich den Web-Starter. Mit Lombok spare ich mir den Standardcode (Getter, Setter, …). Die DevTools unterstützen die Entwicklung. Beispielsweise startet der Server nach Änderungen automatisch neu, damit die Änderungen wirksam werden.

Die übrigen Starter sind Grundlage für eine erste konkrete Implementierung der beschriebenen Anwendungsfälle.

Entwicklungshilfe

Für die Entwicklung und den Test füge ich Swagger hinzu. Die pom.xml erhält die Abhängigkeiten

<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger2</artifactId>
  <version>${springfox.swagger2.version}</version>
</dependency>

<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger-ui</artifactId>
  <version>${springfox.swagger2.version}</version>
</dependency>		

In den Property setze ich die aktuelle Version (aktuell 2.9.2) ein.

Es fehlt noch die Konfiguration, dann ist die Test-Oberfläche unter http://localhost:8080/swagger-ui.html verfügbar. 

@Configuration
@EnableSwagger2
public class SwaggerConfig {

	@Bean
    public Docket api() { 
        return new Docket(DocumentationType.SWAGGER_2)  
          .select()                                  
          .apis(RequestHandlerSelectors.any())              
          .paths(PathSelectors.any())                          
          .build();                                           
    }
}

Die Backend-Schnittstelle

Domain Modell

Zunächst lege ich das Domain-Modell in einem eigenen Package an. Die Umsetzung entspricht dem Diagramm aus dem vorhergehenden Beitrag.

@Data
public class Buchung {

	private String verwendung;
	private String empfaenger;
	private List<Umsatz> umsaetze = new ArrayList<>();
	
	public void addUmsatz(Umsatz umsatz) {
		getUmsaetze().add(umsatz);
	}
}
@Data
public class Umsatz {

	private LocalDate valuta;
	private BigDecimal betrag;
	private Konto konto;
	private Buchung buchung;
}

Das Konto enthält die Operation verbuche(.), die einen Umsatz zum Konto aufnimmt. Dabei übernimmt sie den Umsatz in die Liste der zum Konto gehörenden Umsätze und addiert gleichzeitig den Betrag aus dem Umsatz zum Saldo. Da Soll- und Haben-Beträge über das Vorzeichen unterschieden sind, ist keine weitere Logik notwendig.

@Data
public class Konto {

	private String name;
	private BigDecimal saldo;
	private List<Umsatz> umsaetze = new ArrayList<>();

	public void verbuche(Umsatz umsatz) {
		umsaetze.add(umsatz);
		saldo = saldo.add(umsatz.getBetrag());
	}
}

Die REST-Schnittstelle der Anwendung

Die Backend-Schnittstelle ist eine REST-Schnittstelle und entsteht in Spring durch eine Controller-Klasse. Dank der Spring Boot ist die Umsetzung einfach. Wer sich dabei unsicher ist, findet in den Spring Boot Guides schnelle Anleitungen.

Die BuchhaltungController-Klasse setzt dabei die im vorausgegangenen Beitrag definierten drei Anwendungsfälle um:

  • Buchen erstellen
  • Konten abfragen
  • Umsatz anzeigen

Die Umsetzung ist noch nicht final, denn beispielsweise ist die Abfrage der Buchungen je Konto ohne Paging nicht sinnvoll. Die Liste wird nach einiger Zeit einfach zu lang, was zu unnötigen Verzögerungen beim Laden, Übertragen und Darstellen in der Oberfläche führt. Auch mag ich das Konto lieber über eine technische Id abfragen, um Problemen mit Leerzeichen oder Sonderzeichen vorzubeugen.

@RestController
@RequestMapping("api/buchhaltung")
public class BuchhaltungController {

	@GetMapping("konto")
	public List<Konto> findAllKonten() {
	    throw new UnsupportedOperationException();
	}
	
	@GetMapping("konto/{kontoName}/umsatz")
	public List<Buchung> findKontoBuchungenByKontoName(
                @PathVariable("kontoName") String kontoName) {
	    throw new UnsupportedOperationException();
	}

	@PostMapping("buche")
	public ResponseEntity<String> buche(
                @RequestBody Buchung buchung) {
	    throw new UnsupportedOperationException();
	}
}

Eigene Klassen für die Datenübertragung in der Backend-Schnittstelle

Für die Datenübertragung in der Schnittstelle möchte ich eigene Transfer-Klassen einsetzten. Die Services sollen mit Domain-Objekten arbeiten. Deren Klassen möchte ich allerdings frei von Schnittstellen spezifischen Abhängigkeiten halten, und die wären für die Umwandlung in JSON erforderlich. Insbesondere sind die Relationen bidirektional implementiert, was ohne Zutun wegen zu endlosen Rekursionen führen würde. Die API-Klassen haben das folgende Aussehen:

@Data
public class Buchung {
	private String verwendung;
	private String empfaenger;
	private List<Umsatz> umsaetze = new ArrayList<>();
}

@Data
public class Umsatz {
	private LocalDate valuta;
	private BigDecimal betrag;
	private Konto konto;
}

@Data
@JsonInclude(Include.NON_NULL)
public class Konto {
	private String name;
	private BigDecimal saldo;
}

Im Konto sorgt die JsonInclude-Annation dafür, dass nur Felder in das JSON geschrieben werden, die auch tatsächlich Werte enthalten.

Die Übertragung erfolgt in Mapper-Klassen.

@Component
public class KontoMapper {
	public List<Konto> domainKontoListToApiKontoList(
                List<buchhaltung.domain.Konto> domainKonten) {
	    return domainKonten.stream()
		.map( domainKonto ->
                        domainKontoToApiKonto(domainKonto) )
				.collect(Collectors.toList());
	}

	public Konto domainKontoToApiKonto(
            buchhaltung.domain.Konto domainKonto) {
		Konto apiKonto = new Konto();
		apiKonto.setName(domainKonto.getName());
		apiKonto.setSaldo(domainKonto.getSaldo());
		return apiKonto;
	}
}

Der Konto-Mapper steht als Beispiel für das Codierungsmuster. Die Formalität sollte helfen, diese Transformation später ggf. durch Hilfsbibliotheken zu automatisieren, beispielsweise durch MapStruct.

Das folgende Bild zeigt in der Übersicht die Struktur der Schnittstelle.

Backend-Schnittstelle
Rest-Schnittstelle mit Mapper

Der Abschluss

Ergänzt um Service-Klassen mit Methoden-Signaturen entsteht als Schnittstelle der folgende Controller:

@RestController
@RequestMapping("api/buchhaltung")
public class BuchhaltungController {
	private KontoService kontoService;
	private BuchungService buchungService;
	private KontoMapper kontoMapper;
	private BuchungMapper buchungMapper;

	public BuchhaltungController(
			KontoService kontoService, 
			BuchungService buchungService, 
			KontoMapper kontoMapper,
			BuchungMapper buchungMapper) {

		this.kontoService = kontoService;
		this.buchungService = buchungService;
		this.kontoMapper = kontoMapper;
		this.buchungMapper = buchungMapper;
	}

	@GetMapping("konto")
	public List<Konto> findAllKonten() {
		return kontoMapper.domainKontoListToApiKontoList(
				kontoService.findAll());
	}
	
	@GetMapping("konto/{kontoName}/umsatz")
	public List<Buchung> findKontoBuchungenByKontoName(
           @PathVariable("kontoName") String kontoName) {
	    return buchungMapper
             .domainBuchungListToApiBuchungListe(
                buchungService
                  .findBuchungByKontoName(kontoName));
	}

	@PostMapping("buche")
	public ResponseEntity<String> buche(
                @RequestBody Buchung buchung) {
	  try {
            buchungService.buche(
              buchungMapper.apiBuchungToDomainBuchung(buchung));
	  } catch (Exception e) {
	    return ResponseEntity
                .unprocessableEntity()
                .body("Fehler: " + e.getMessage());
	  }
	  return ResponseEntity.accepted().body("Verarbeitet");
	}
}

finally

Die Schnittstelle ist damit beschrieben und es steht die eigentliche Implementierung an. Mein erster Wurf wird auf einer relationalen Datenbank zur Speicherung basieren. Für die Anbindung auf der grünen Wiese, verwende ich die Java Persistente API (JPA). Doch dazu beim nächsten mal mehr.

Happy Coding!

Anwendung entwickeln für zu Hause – eine Finanzverwaltung

Mein Quicken ist in die Jahre gekommen. Mag ich das aber wirklich erneuern, schließlich arbeite ich als Software-Entwickler? Da reift die Überlegung eine Anwendung selbst zu entwickeln.

Die Anforderungen der Anwendung

Bevor ein erster Prototyp entsteht, gilt es erst mal einfache Anforderungen zusammen zu tragen.

Was sollte ein erster Prototyp können? Eine einfache Tabelle mit Datum, Beschreibung und Betrag reicht nicht. Das wäre praktisch ein Kassenbuch. Ich habe dann doch mehr als ein Konto und – Hand aufs Herz – ein Kassenbuch geht mit jeder Tabellenkalkulation.
Die doppelte Buchführung sollte es schon sein. Das ist es in Quicken auch, die Gegenkonten heißen dort halt nur Kategorie. Mehrteilige Buchungen sollten auch möglich sein. Vom Giro-Konto buchen beispielsweise die Stadtwerke ab, der Betrag setzt sich dabei aus den Strom- und den Gas-Kosten zusammen. Wollte ich einen Vergleich dieser Kosten für alternative Anbieter machen, müßte ich das schon trennen können.

Die Modellierung ist nicht neu. Was auf der Arbeit funktioniert, gilt auch für die Software zu Hause: das Rad nicht neu erfinden. Zumindest ist das Datenmodell schon da – zumindest im Prinzip. Martin Fowler hat sich schon vor einiger Zeit Gedanken dazu gemacht: https://martinfowler.com/eaaDev/AccountingNarrative.html

Das Datenmodell

Die erste Wurf für das Modell der Anwendung sieht wie folgt aus. Das ist eine eingedeutschte Variante des Fowler-Vorschlages.

Klassenmodell
Klassenmodell der Anwendung

Die gute doppelte Buchführung: eine Buchung ist immer Soll an Haben oder als Text-Format: “Per Soll-Konto, Betrag an Haben-Konto, Betrag”, wobei es jeweils mehrer Soll- und Haben-Konten geben kann. Die Summe der Soll und Haben-Beträge muss 0 sein. Soll-Beträge sind positiv und Haben-Beträge negativ. Das entspricht dem gewohnten Gefüge, dass Guthaben positiv sind und Schulden negativ.

Die Buchung fasst die (Konto-)Umsätze zusammen. Jeder Umsatz kennt seine Buchung – kann ohne diese auch gar nicht existieren – und er kennt das Konto, welches er anspricht.

Der Saldo des Kontos ergibt sich formal als die Summe der Beträge aller Umsätze des Kontos. Fraglich ist, ob der immer aufs Neue berechnet wird oder der letzte Stand gespeichert ist. Im Unternehmen wäre die Antwort das speichern des Saldos, da die Berechnung auf sehr viele Umsätze zu lange dauert und im Verlauf der Zeit immer länger. Ich lasses es dabei.

Erste Anwendungsfälle der Anwendung

Die folgenden Anwendungsfälle mag ist initial umsetzten:

  • Buchen erstellen
    Das ist praktisch das Aufbauen des Hauptbuches. Initial mag ich ein abgesprochenes Konto automatisch anlegen, wenn es noch nicht existiert. Das spart erst mal einen Anwendungsfall “Konto anlegen”.
  • Konten abfragen
    Ich bekomme eine Liste aller existierenden Konten mit deren aktuellen Salden. Das ist praktisch das Grundbuch.
  • Umsatz anzeigen
    Zuletzt mag ich noch die Umsätze eines Kontos ansehen können. Hier sind die Details sichtbar, um zum Beispiel Ausgaben zu vergleichen oder Positionen zu kontrollieren.

Die technische Basis – ein erster Ansatz

Finanzdaten mag ich nicht unnötig offen haben, also ist der private Rechner meine Zielplattform. Was liegt näher als das für eine kaufmännische Anwendung gewohnte Setup zu wählen.

Auf einem Rechner einen Browser für die Anwendung? Warum nicht, denn was ist mit HTML, CSS und Javascript nicht machbar. Diesen Ansatz verfolgt beispielsweise das Project Electronjs. Hier kommt vollständig Javascript zum Einsatz und es entsteht eine ausführbare Binärdatei, die für unterschiedliche Betriebssysteme erstellt werden kann.

Nun bin ich in – gerade was die Fachlogik angeht – mehr in der Java-Welt zu Hause. Dank der Graal VM ist auch hier mal eine kompilierte Version denkbar und die Entwicklung geht schneller von der Hand.

Aus der täglichen Gewohnheit mag ich mit den Mitteln arbeiten, die ich bereits gut kenne. Das bedeutet: relationale Datenbank, Spring Boot als Backend und Angular für die Oberfläche.

Natürlich bleibt das ganze zu überdenken. Für den Zugang ist ein Browser mit der entsprechenden URL zu starten. Das ist allerdings lösbar.

Für die Datenbank bietet sich eine eingebundene (embedded) Variante an, um zusätzliche Installationen zu vermeiden, z.B. H2.
Da das Datenmodell auf der “grünen Wiese” entsteht, möchte ich als Anbindung an die Datenbank die Java Persistente API (JPA) nutzen, da JPA das Schema direkt erstellen kann und somit Arbeit spart.

Die nächsten Schritte zur Anwendung

Im nächsten Beitrag beschreibe ich zunächst die Schnittstelle des Backends, welches die umzusetzenden Anwendungsfälle darstellt.

Dann schau ich mir die zugehörige Implementierung an. Da steckt etwas Forschung drin. Denn diese Anwendung ist nicht für ein großes Finanzunternehmen gedacht. Im Sinne des KISS-Prinzips gibt es vielleicht Alternativen.

Bis dahin: Happy coding!