Self-Contained-Systems

Self-Contained-Systems ist sowohl ein Architektur-, als auch Organisationsansatz, der im Kern auf dem Domain-Driven-Design-Ansatz aufsetzt und erweitert. SCS soll nicht nur die Trennung und Betrieb von Systemen, sondern auch die organisatorische Strukturierung und Trennung größerer Entwicklungsteams erleichtern. Der Fokus von SCS liegt klar auf Anforderungen und nicht auf technische Implementierungsdetails.

Self-Contained-Systems (SCS) ist zunächst einmal ein Architekturansatz. Die Idee ist, Anforderungen durch einzelne, fachlich unabhängige Systeme abzubilden. Diese Systeme beinhalten alles, was für die Abbildung der Anforderungen notwendig ist – die Datenhaltung, die eigentliche Geschäftslogik und die komplette Benutzerschnittstelle. Diese Systeme werden dadurch self-contained.

Jedes SCS soll von einem eigenen Entwicklungsteam betreut werden. Dieser Architekturansatz soll neben der technischen Trennung der Systeme auch die organisatorische Trennung erleichtern. Durch die Vermeidung von Abhängigkeiten werden auch die Teams unabhängiger und damit flexibler. Soweit die Theorie. Nachfolgend werden Vorteile und auch mögliche Probleme bei der Implementierung des SCS-Ansatzes beleuchtet.

Grundlagen von Self-Contained-Systems

Die Idee, fachlich unabhängige Teilsysteme zu definieren, ist nicht neu. Eric Evans hat den Begriff Domain-Driven-Design, kurz DDD, geprägt. DDD stellt die fachlichen Anforderungen und die daraus resultierende fachliche Struktur ganz klar in den Vordergrund. DDD bietet ebenfalls Entwurfsmuster an, um die fachlichen Anforderungen zu dokumentieren und auch zu implementieren. In der DDD-Welt ist ein fachlich abgeschlossener Bereich ein Bounded-Context.

Der SCS-Ansatz baut genau auf diesen Bounded-Contexten auf und erweitert deren Definition. Neben der fachlichen Logik wird in diesem Kontext auch die Benutzerschnittstelle implementiert. Ein wichtiger Punkt ist hier, dass beim Aufruf einer Funktionalität keine synchrone Kommunikation mit Komponenten außerhalb des Self-Contained-Systems erfolgen soll. Wenn Kommunikation stattfindet, so soll sie ausschließlich asynchron erfolgen. Daraus ergeben sich mehrere Konsequenzen. Das System wird dadurch widerstandsfähiger gegenüber möglichen Fehlern, wie zum Beispiel Timeouts. Auch wird die Implementierung der Logik einfacher, da keine Patterns, wie zum Beispiel ein Bulkhead oder ein Circuit-Breaker vor die Kommunikation geschaltet werden und eventuelle Fallback-Szenarien implementiert werden müssen. Die andere Konsequenz ist die Verhinderung der weiteren Zerlegung des Bounded-Context in weitere Artefakte, ein Ansatz der auch gerne Microservices genannt wird. Weshalb? In der DDD-Welt ist der Bounded-Context eine fachliche Einheit, welche alle notwendige Logik kapselt. Wenn ein Change an dieser Einheit vorgenommen werden muss, wie beispielsweise eine Datenbankerweiterung, so betrifft dieser Change vermutlich mehr als nur die Datenbank und erstreckt sich über alle Schichten des Bounded-Context. Wenn nun diese Schichten nicht über mehrere Microservices verteilt sind, ist die Tragweite von solchen Changes einfacher zu überblicken. Es ergibt sich gar nicht mehr die Notwendigkeit auf Breaking-Changes in einer API zu achten. Durch die explizite Vermeidung von synchroner Kommunikation soll zudem das Risiko reduziert werden, einen verteilten Monolithen mit viel zu vielen und langsamen Schnittstellen zu bauen. Die Devise lautet: Weniger ist mehr. Oder pragmatischer ausgedrückt: Was nicht da ist, macht auch keine Fehler.

Auch die Organisation verändert sich

Der SCS-Architekturansatz hat Konsequenzen auf die Organisation und die Teamstruktur. Wie schon einleitend erwähnt, soll jedes Self-Contained-System von einem Team verantwortlich betreut werden. Das bedeutet, dass sich auch die dafür notwendigen Kompetenzen in diesem Team bündeln müssen. Diese Kompetenzen wären unter anderem das Product-Ownership, Entwicklungs- sowie QA-Kompetenzen und auch Ops oder User-Experience (UX). Durch die Bündelung dieser Kompetenzen in einem Team wird das Team handlungsfähig und kann Geschwindigkeit bei der Umsetzung von neuen Features aufnehmen. Idealerweise ist also jedes Teammitglied im Full-Stack unterwegs. Architekturentscheidungen, welche nur ein SCS betreffen, werden auch Mikro-Architektur-Entscheidungen genannt. Diese Entscheidungen können schnell getroffen und revidiert werden, da die Tragweite auf ein SCS beschränkt ist. Es sind lokale Entscheidungen, wie zum Beispiel:

  • welche Java-Version,
  • welches Framework,
  • welche Persistenz-API oder
  • ob eine hexagonale Struktur verwendet wird.

Dem gegenüber stehen Makro-Architektur-Entscheidungen. Diese Entscheidungen betreffen die komplette Systemlandschaft, haben in der Regel eine größere Tragweite, erfordern mehr Abstimmungsaufwand und sind meistens schwerer zu revidieren. Solche Entscheidungen sind zum Beispiel:

  • die Anzahl der SCS,
  • welche systemübergreifenden Protokolle verwendet werden sollen oder
  • welche Standards bei der Präsentationstechnologie einzuhalten sind.

Die Makroarchitektur ist also auch und maßgeblich Organisationsarchitektur. Wie groß kann nun ein SCS und das betreuende Team werden? Das ist eine sehr interessante Frage auf die es jedoch keine absolute Antwort gibt. Ein Team sollte ein SCS betreuen, eventuell auch mehrere, abhängig von der fachlichen Struktur und die daraus resultierende Komplexität. Der treibende Faktor hinter der Teamgröße ist also ausschließlich der fachliche Umfang. Bei SCS handelt es sich also nicht nur um eine Sammlung technischer Grundsätze, sondern vielmehr um eine Richtlinie zur Organisation von Entwicklungsteams. Ohne Anpassungen innerhalb der Organisation kann der Ansatz nicht seine Vorteile ausspielen. Im Idealfall entspricht das Organigramm der Context-Map aus der DDD-Welt (Abb. 1).

Beispiel Context-Map (Abb. 1)

Datenhaltung und Co.

Ein Self-Contained-System soll alle Funktionalitäten beinhalten, die für die Ausführung der darin enthaltenen Anwendungsfälle notwendig sind. Dies beinhaltet auch die dafür notwendigen Daten. Auf Ebene Datenhaltung ergeben sich dadurch einige Herausforderungen. Ein Auftragsmanagement SCS benötigt wahrscheinlich die Daten für die Aufträge, und hat über diese Daten auch die Datenhoheit. Es benötigt vermutlich auch die Kundendaten, hat aber eventuell über diese Daten nicht die Datenhoheit. Es benutzt diese Daten, ist abhängig davon, verwaltet sie aber nicht. Wie kann diese Problematik nun umgesetzt werden? Die Antwort lautet: mit Datenreplikation.

Mit Datenreplikation ist hier die fachliche Replikation der Daten gemeint, und nicht die technische Replikation, wie sie zum Beispiel mit einem Master-Slave-Datenbank-Setup möglich wäre. Was ist hier der Unterschied? Eine Replikation auf technischer Ebene, sprich via Datenbankreplikation, würde zu viele Implementierungsdetails offenlegen und die Systeme zu stark aneinanderbinden. Änderungen am Datenbankschema betreffen nicht mehr nur ein SCS. Es würden technische und organisatorische Abhängigkeiten entstehen, doch genau dies soll der SCS-Ansatz ja verhindern. Ein anderes Argument ist, dass nicht alle Systeme eine vollständige Kopie der Daten benötigen und vermutlich auch anders auf die Daten zugreifen müssen. Eventuell dürfen bestimmte Daten auch nicht das Ursprungssystem verlassen, da sie besonders schutzbedürftig sind. Auch kann beispielsweise System A für den schreibenden Zugriff auf Kundendaten optimiert sein und System B ausschließlich für den lesenden Zugriff optimiert werden. Daraus ergeben sich dann andere Anforderungen an die zu verwendende Datenbank, das Datenbankschema und Normalisierungsformen etc.

Welche Möglichkeiten gibt es nun? Als gut funktionierend hat sich Event-Sourcing in Verbindung mit einem Transaktionslog erwiesen. Eine Änderung an einem Kundendatensatz im Kundenmanagement-SCS würde das Event KundeWurdeGeändert mit den neuen Kundendaten generieren und in ein Transaktionslog schreiben. Das Auftragsmanagement-SCS würde dieses Event aus dem Transaktionslog konsumieren, die für die Auftragsverwaltung notwendigen Daten extrahieren und in seine eigene Datenbank schreiben.

Als Transaktionslog sind Lösungen wie Apache-Kafka sehr interessant. Neben der Tatsache, dass es sich hier um eine Pull-Lösung handelt und die Komplexität auf Ebene Kafka-Server dadurch sehr klein gehalten werden kann, ist ein anderer Aspekt viel wichtiger: Kafka ermöglicht eine Zeitreise! Der Konsument von Events kann seinen Offset innerhalb des Transaktionslog zurücksetzen, um Events aus der Vergangenheit nochmals zu verarbeiten. Diese Funktionalität ist wichtig für ein Desaster-Recovery. Fehler können und werden passieren, sei es technische Probleme oder einfach nur Fehler in der Software. Durch die Zeitreise können wir eine neue Version der Software installieren, Events aus der Vergangenheit konsumieren und eventuell beschädigte Datenbestände nachträglich reparieren. Eine Funktionalität die sich als sehr nützlich herausgestellt hat.

Ein Nachteil dieser Datenreplikation ist die dadurch entstehenden Redundanz und der damit verbundene Platzverbrauch. Wenn zum Beispiel mehrere Millionen Artikeldatensätze über mehrere Systeme repliziert werden, ist die Größenordnung nicht zu unterschätzen und kann auch ein Kostentreiber werden. Aus Kostensicht zu beachten ist ebenfalls das Lizenzmodell der Datenbank. Jedes SCS sollte seinen eigenen Datenbankserver bekommen, um beispielsweise bei Wartungsarbeiten an dem Datenbankserver nicht gleich mehrerer Systeme beeinträchtigen zu müssen. Die Lizenzkosten können explodieren. Hier gilt es, die Risiken, Vor- und Nachteile abzuwägen und eine pragmatische Lösung zu finden und diese auch so dokumentieren und zu kommunizieren.

Bei Datenreplikation darf das Thema Datenschutz nicht vernachlässigt werden. Wo dürfen Kundendaten gespeichert werden? Was ist mit Kreditkartendaten? Welche Möglichkeiten zur Auditierung müssen vorgesehen werden? Welche Daten müssen nachträglich löschbar sein? Welche Daten müssen anonymisiert werden, eventuell auch nachträglich? Betrifft das nur das Zielsystem oder auch das Transaktionslog dazwischen? Wie sieht es mit der DSGVO-Konformität aus? Gibt es noch zusätzliche Auflagen? Der zuständige Datenschutzbeauftragte ist auf jeden Fall zu konsultieren, denn aus der technischen Datenreplikation kann sehr schnell ein fachliches und rechtliches Problem entstehen.

Das Wichtigste ist die Geschäftslogik

Bei der Implementierung der Geschäftslogik sind einige Dinge aus konzeptioneller Sicht zu beachten. Self-Contained-Systems bauen im Kern auf Ansätzen aus dem Domain-Driven-Design auf. Domain-Driven-Design hilft auch bei der Implementierung der Geschäftslogik. Patterns wie Aggregate, Entity, Value-Object und Repository helfen, die Geschäftslogik gut zu strukturieren und zu implementieren. Eine hexagonale Architektur unterstützt diese Patterns weiter und hilft dabei, ein modulare und gut testbare Systemstruktur zu erhalten.

Probleme ergeben sich bei der Implementierung von Anwendungsfällen, welche nicht komplett innerhalb eines SCS umgesetzt werden können, weil unter anderem Zulieferer oder Geschäftspartner involviert sind. Dies verdeutlicht ein Blick auf den Anwendungsfall Kaufen und Lagerbestand reservieren: Wenn ein Kunde etwas bestellt, soll der Auftrag im System verbucht werden. Abhängig davon ob der Artikel noch auf Lager ist oder nicht, soll der Lagerbestand für den Kunden reserviert oder eine Nachbestellung veranlasst werden. Das Ergebnis der Verarbeitung soll dem Kunden angezeigt werden. Dieser Anwendungsfall hat es in sich. Einen Auftrag im System verbuchen ist einfach zu implementieren. Interessant ist die Reservierung von Lagerbeständen und die Nachbestellung. Die Lagerverwaltung wird vermutlich von einem anderen SCS verantwortet, genauso wie die Nachbestellungen. Die Kommunikation soll asynchron erfolgen, um die Systeme zu entkoppeln. Trotzdem soll dem Kunden nach dem Auftragseingang das Ergebnis der Verarbeitung angezeigt werden. Welche Optionen haben wir? Aus Sicht von Design-Patterns ist das SAGA-Pattern interessant. Das SAGA-Pattern soll helfen verteilte Transaktionen zu implementieren. Neben der technischen Implementierung des Patterns müssen auch die fachlichen Anforderungen implementiert werden, wie der Aufruf der Lagerverwaltung und der Nachbestellung inklusive Fehlerbehandlung. Dies ist aus technischer Sicht herausfordernd genug. Gerade die Fehlerbehandlung ist in diesem Fall mehr fachlich als technisch getrieben. Was passiert, wenn der Aufruf der Lagerverwaltung zu lange dauert? Was passiert, wenn bei der Nachbestellung ein Fehler auftritt? Soll das System es nochmals versuchen? Wie breche ich die Verarbeitung komplett ab? Und wie bekomme ich das Ganze sinnvoll in das SCS integriert?

Eine mögliche Lösung ist hier der Einsatz einer leichtgewichtigen Workflow-Engine, welche in das Auftragsverwaltungs-SCS eingebettet ist. Eine Workflow-Engine bringt die technisch notwendige Struktur für die korrekte Umsetzung des SAGA-Patterns mit. Die Workflow-Definition kann sehr gut mit den Verantwortlichen abgestimmt und transparent gemacht werden. Der Fokus auf die Implementierung der Fachlogik geht nicht verloren. Eingebettete Workflow-Engines haben noch einen anderen Vorteil: In der Regel bildet die Workflow-Definition eine Einheit mit dem SCS. Ein Change an dem Einen betrifft meistens auch das Andere. Es macht daher Sinn, diese zusammen zu versionieren, zu paketieren und auch zusammen zu deployen. In diesem Zusammenhang sei noch erwähnt, dass der Aufruf eines anderen Systems kein synchroner Aufruf sein sollte, da die systemübergreifende Kommunikation idealerweise asynchron erfolgen sollte. Der Aufruf ist also der Versand eines Events an das Zielsystem und das Warten auf ein entsprechendes Antwort-Event des Zielsystems inklusive Timeout-Behandlung usw. Gerade bei der Modellierung und Implementierung solcher Funktionalitäten helfen Workflow-Engines enorm.

In Verbindung mit dem Kaufen und Lagerbestand reservieren Anwendungsfall sieht die Implementierung dann so aus: Beim Klick auf den Kaufen-Button wird innerhalb des Auftragsmanagement-SCS der entsprechende Workflow über die Workflow-Engine gestartet. Wenn dieser Workflow innerhalb ein paar Dutzend Millisekunden erfolgreich durchläuft, kann als Antwort der Auftragsverwaltung dem Kunden der entsprechende Status des Auftrages angezeigt werden. Wenn der Workflow nicht komplett durchläuft, können wir auch dies dem Kunden mit einem entsprechenden Hinweis mitteilen und ihn dann später via E-Mail oder Push-Notification über die Verarbeitung benachrichtigen. Dieser Mechanismus hilft den Anwendungsfall korrekt zu implementieren, Lastspitzen auf den Systemen besser zu skalieren und auch noch eine kundenfreundliche Fehlerbehandlung zu implementieren.

Geschäftslogik soll in einem SCS gekapselt werden. Das bedeutet aber nicht, dass Java für die Implementierung aller Anwendungsfälle genutzt werden muss. Gerade bei datenintensiven Anwendungen kann es sinnvoll sein, auf natives SQL zurückzugreifen anstatt ORM-Frameworks wie Hibernate zu verwenden. Der Einsatz von SQL unterwandert die Kapselung nicht, sondern bringt Geschäftslogik und Daten nur näher zusammen. Hier handelt es sich um ein Beispiel für die Mikroarchitektur, welche lokal auf ein SCS beschränkt ist. Diese Architektur und Designentscheidungen sollen vom zuständigen SCS-Team getroffen werden.

Die Benutzerschnittstelle

Jedes Self-Contained-System soll die komplette Benutzerschnittstelle für die darin enthaltenen Anwendungsfälle beinhalten. Im Idealfall ist es eine Web-Applikation, sprich die Benutzerschnittstelle ist HTML, CSS und JavaScript. Im Zusammenhang mit SCS ist aber ein besonderes Augenmerk auf die Integration von mehreren SCS zu einer einheitlichen Benutzerschnittstelle zu lenken. Benutzer möchten unterschiedliche Funktionalitäten nutzen. Diese Funktionalitäten sollen via Link zu dem zuständigen SCS führen. Am Beispiel eines Online-Shops ergibt sich dann die Frage, wo zum Beispiel die Top-Navigation herkommt, die auf fast jeder Seite angezeigt wird. Soll diese Funktionalität redundant in mehreren SCS implementiert werden? Die Antwort lautet natürlich: nein.

Es gibt ein System, das für die Top-Navigation zuständig ist. Ein anderes System ist z.B. für die Berechnung von Empfehlungen zuständig und ein weiteres System kann Artikellisten anhand von Filterkriterien generieren. Jedes dieser Systeme liefert den HTML-Code für genau diesen Teil der Seite aus. Die einzelnen Seitenbestandteile müssen zu einer Einheit zusammengefasst werden. Hier gibt es mehrere Möglichkeiten. Eine Möglichkeit wäre, ein Content-Management-System zu nutzen, welches die Aggregation übernimmt. Diese Möglichkeit ist die mächtigste, stellt aber auch wieder die organisatorische Frage, wer dieses System betreut und weiterentwickelt. Zusätzlich entstehen auch neue Abhängigkeiten zwischen den zuständigen Teams und nicht zuletzt ein Single-Point-Of-Failure für die ganze Architektur. Eine andere, etwas leichtgewichtigere Möglichkeit ist, das Seitenlayout und Platzhalter für systemfremde Inhalte in Form von Templates in das SCS zu integrieren. Diese Platzhalter könnten SSI-Direktiven (Server-Side-Includes) oder ESI-Direktiven (Edge-Side-Includes) sein. Das Auflösen dieser Platzhalter würde dann von einem vorgelagerten Web-Server oder Varnish-Cache durchgeführt werden. In der Praxis ist eher eine leichtgewichtige Lösung ratsam, die später weiter ausgebaut werden kann, falls dies wirklich notwendig ist. Einmal in Produktion gebrachte Komplexität ist schwer zurückzubauen.

Beim Thema Edge-Side-Includes oder Caching sind noch Details bei personalisierten Inhalten zu beachten. Es soll eine möglichst hohe Cache-Hit-Rate erreicht werden. Personalisierte Inhalte sind meistens sehr schwer cachebar. Eine Lösung ist, immer das gleiche HTML-Grundgerüst für alle Nutzer auszuspielen und die personalisierten Inhalte via JavaScript nachzuladen. Diese Strategie ist auch ohne Cache im Hintergrund sinnvoll. In Verbindung mit Progressive-Enhancement ergeben sich interessante Möglichkeiten. Warum Inhalte ausliefern, wenn sie auf dem Zielgerät nicht sichtbar sind, weil der Bildschirm zu klein ist? Warum Videos anzeigen, wenn das Zielgerät das nicht kann? All diese Fragen können nur auf dem Zielgerät und nicht auf dem Applikations-Server beantwortet werden. Der Server liefert nur das Grundgerüst der Seite über semantisches HTML aus. Der Rest wird auf dem Zielgerät entschieden und zusammengebaut.

Wie lässt sich eine einheitliche UX erreichen und gibt es eine zentrale Ablage für Styles? Was passiert mit JavaScript-Code? Jedes SCS enthält seinen eigenen JavaScipt-Code sowie eigene Styles und liefert diese aus. Der Trick ist, einen komponentenbasierten Ansatz in Verbindung mit einer Modularisierung der Stylesheets und des JavaScripts zu fahren. Eine Komponente ist beispielsweise die Top-Navigation oder eine Artikelliste. Jede Komponente bringt das dafür notwendige HTML, CSS und JavaScript mit. Es sollten so wenige globale Styles wie möglich vorgegeben werden. Nur so können Funktionalitäten unabhängig und innerhalb eines SCS umgesetzt und damit letztendlich Geschwindigkeit in der Entwicklung und kurze Time-to-Market umgesetzt werden. Hier gilt es einen pragmatischen Weg zu finden.

Die Konsequenz ist, dass es kein globales Stylesheet mehr für all das gibt. Dies ist so gewollt, aus Sicht von UX aber erst einmal ein Albtraum. Bei genauerer Betrachtung von großen Online-Shops wie Amazon, stellt man jedoch auch fest, dass das Seitenlayout in den Artikellisten komplett anders ist als im Check-out. Vermutlich, weil diese Seiten von einem anderen System ausgeliefert werden. Vermutlich ist auch hier eine pragmatische Entscheidung zwischen einheitlichem Layout, Unabhängigkeit von Systemen und Teams sowie von Entwicklungszeit und Entwicklungskosten getroffen worden.

Dieses Abschnitt ist sehr HTML-lastig. HTML ist jedoch die Wahl für die Implementierung der Präsentationsschicht eines SCS. Problematisch wird es, wenn gemischte Präsentationstechnologien verwendet werden müssen. Dies ist besonders der Fall, wenn bestimmte Anwendungsfälle via App zur Verfügung gestellt werden sollen. Wenn es sich bei der App um eine hybride App handelt und bereits eine Web-View integriert ist, kann HTML verwendet werden. Soll die App allerdings die nativen Möglichkeiten des Endgerätes nutzen oder komplett offlinefähig sein, so muss für die App eine andere Schnittstelle zur Verfügung gestellt werden. In diesem Fall stößt der SCS-Ansatz an seine Grenzen, da die Benutzerschnittstelle nicht mehr Teil des SCS ist, und vermutlich auch die App-Entwicklung nicht mehr Teil des eigentlichen SCS-Teams ist. In diesem Fall wäre es ein Kompromiss, das SCS um eine (REST-)API zu erweitern und diese der App zur Verfügung zu stellen. Die Funktionalität bleibt dann im SCS gekapselt und nur der App-spezifische Teil der Präsentationsschicht wird externalisiert (Abb. 2).

Systemübersicht (Abb. 2)

Fazit:

Self-Contained-Systems können helfen, die Arbeit größerer Entwicklungsteams zu organisieren und zu strukturieren. Aber auch für kleinere Projekte und Teams kann es ein interessanter Ansatz sein, welcher den Fokus klar auf Anforderungen und nicht auf technische Implementierungsdetails lenkt. Die Prinzipien hinter Domain-Driven-Design werden hier angewendet. Der Grundsatz „Weniger ist mehr“ zeigt sich auch in den aktuellen Trends der Softwareentwicklung und der Rückkehr zu wenigen, aber dafür wichtigen und richtigen Services. Wie bei allen Architekturentscheidungen handelt es sich auch hier nicht um die Entscheidung zwischen Schwarz oder Weiß. Es ist immer ein Abwägen zwischen Vor- und Nachteilen und das Leben mit den damit verbundenen Konsequenzen.

Welches Framework und welche Programmiersprache sind nun die besten, um ein SCS zu implementieren? Grundsätzlich sind alle geeignet, solange sich damit die fachlichen Anforderungen umsetzen lassen und natürlich auch das Entwicklungsteam damit produktiv und nachhaltig arbeiten kann. Der SCS-Ansatz soll gerade dabei helfen, pro SCS die jeweils sinnvollste Technologie zu verwenden. In der Praxis muss hier ein Kompromiss zwischen innovativen Ansätzen und technologischen Wildwuchs gefunden werden.

Kritisch betrachtet ist der SCS-Architekturansatz zu grobgranular und lässt zu viele Details offen. Gerade der Punkt Makroarchitektur könnte mehr Details und Hinweise liefern, da genau hier die Faktoren für eine erfolgreiche Einführung versteckt liegen.

 

Links & Quellen:

  1. Self-Contained-Systems – https://bit.ly/scs-s1
  2. Evans, Eric: Domain-Driven-Design: Tackling Complexity in the Heart of Software
  3. Apache Kafka – https://bit.ly/kafka-a3
  4. SAGA Pattern – https://bit.ly/saga-s4
  5. Zeebe – https://bit.ly/zeebe-z5
  6. Canoo ULC – https://bit.ly/ulc-c6

 

 

Mirko Sertic

Mirko ist Software-Craftsman im Web-/E-Commerce-Umfeld. In Funktionen als Software-Engineer, Architekt und Consultant in Projekten in Deutschland und der Schweiz sammelte er Erfahrungen mit einer Vielzahl von Frameworks, Technologien und Methoden. Heute arbeitet er als IT-Analyst bei der Thalia Bücher GmbH in Münster mit Schwerpunkt auf Java, E-Commerce sowie Such- und Empfehlungs-Technologien. Seine Freizeit verbringt er mit seiner Familie und hin- und wieder mit Open-Source Projekten.

Webseite | Twitter | GitHub | LinkedIn | Xing | E-Mail 

 

 

 

 

 

 

 

 

 

 

 

Redaktion


Leave a Reply