Podcast

Jetzt doch wieder ein Monolith/Modulith?

Die Architekturfrage neu gestellt

In dieser Folge des INNOQ Podcasts sprechen Torsten Mandry und Sven Johann über ihre praktischen Erfahrungen mit unterschiedlichen Architekturansätzen. Im Mittelpunkt steht die Frage: Wann sind Microservices sinnvoll – und wann ist ein Modulith die bessere Wahl? Gemeinsam reflektieren Torsten und Sven, welche Kriterien bei der Architekturwahl eine Rolle spielen: Teamgröße, Projektphase, technisches Umfeld und strategische Ziele. Ein Gespräch darüber, wie man zu einer passenden Architektur kommt – und was dabei hilft, gute Entscheidungen zu treffen.
Listen to other episodes

Shownotes & Links

Transkript

show / hide transcript

Sven Johann:

Hallo und herzlich willkommen zu einer neuen Episode des INNOQ Podcasts. Heute mit Torsten Mandry. Wir wollen über Torstens Gedanken sprechen, von Microservices zu Modulithen zu wechseln. Das wird bestimmt lustig. Normalerweise reden die Leute ja immer über das Gegenteil. Torsten, stell dich doch mal kurz vor.

Torsten Mandry:

Hallo, ich bin Torsten Mandry , Senior Consultant bei INNOQ und bin hauptsächlich im Entwicklungs- und Architekturbereich unterwegs. Das schon seit etlichen Jahren.

Sven Johann:

Torsten schreibt auch Blogposts, die absolut lesenswert sind. Die könnt ihr euch auf jeden Fall reinziehen. Ich muss sagen, dein Blogpost zu Prometheus Counter fand ich wirklich gut. Ich glaube, das ist einer der Top-Blogposts aller Zeiten, den kann ich echt nur empfehlen. Alle Blogposts von Torsten sind wie Rezepte, denen man gut folgen kann. Von Microservices zum Modulithen. Ich glaube, wir wissen alle, was Microservices sind, aber ich würde trotzdem noch mal kurz mit einer Art Definition einsteigen, damit klar ist, worüber wir reden. In unserer Microservices Schulung FLEX und auch im Foundation Level sagen wir, es gibt eigentlich gar keine Definition. Aber ich bringe trotzdem mal eine von James Lewis und Martin Fowler. Kurz gesagt, ist die Microservice-Architektur ein Ansatz für die Entwicklung einer einzigen Anwendung als eine Reihe kleiner Services, die jeweils in einem eigenen Prozess ausgeführt werden und mit leichtgewichtigen Mechanismen, häufig einer HTTP Ressourcen API kommunizieren, natürlich auch asynchron sein. Diese Services sind um die Geschäftsfunktionen herum aufgebaut und können unabhängig voneinander automatisch deployt werden. Ich glaube, dieses unabhängig voneinander deployt werden ist wirklich ganz wichtig. Diese Services, die in verschiedenen Programmiersprachen geschrieben sein können und unterschiedliche Datenbanktechnologien verwenden können, werden nur minimal zentral verwaltet. So habe ich es tatsächlich auch mal kennengelernt, mit diesem automatisch unabhängig deployt werden. Der erste Talk war auch von ein paar Thoughtworks Leuten, ich glaube, Rachel Laycock. Die hat einen Vortrag gemacht, Architecture for Continuous Delivery, 2012 oder 2013, und da habe ich das zum ersten Mal mitbekommen. Sam Newman, als er das Buch geschrieben hat, wollte auch Architecture for Continuous Delivery, sollte das Buch eigentlich lauten. Das ist sozusagen die Motivation und Definition davon. Jetzt haben wir hier noch so ein Modulith. Ich weiß gar nicht so genau, was ist eigentlich ein Modulith? Kannst du das sagen, Torsten?

Torsten Mandry:

Ich weiß gar nicht, ob es da die eindeutige Definition für gibt. Letztendlich, wie ich diesen Begriff kennengelernt habe oder wie ich diesen Architekturstil kennengelernt habe, ist, wir kennen alle den klassischen Monolithen, den wir in der Regel kennenlernen, wenn der 20, 30 Jahre gewachsen ist und man sich nicht mehr traut, weil alles mit allem verknüpft ist und man nie weiß, was passiert, wenn man eine Stelle ändert. Der Modulith versucht, das Beste aus beiden Welten zu schaffen. Er versucht, diese Modularisierung hinzustellen, und da ist, glaube ich, nicht definiert, wie das passiert, aber definiert ist, dass ich meine einzelnen Module habe, so wie ich das mit Microservices auch habe, aber dass ich die nicht einzeln und unabhängig voneinander deploye, sondern spätestens zum Deploy-Zeitpunkt oder wahrscheinlich in der Regel zum Build-Zeitpunkt, spätestens zu einer Einheit zusammenfasse und als eine Einheit deploye. Im Grunde genommen ein modularisierter Monolith oder Martin Fowler hat das irgendwann auch mal Deployment Monolith genannt. Ich kann es noch mal raussuchen und es können wir dann auch verlinken. Den anderen Artikel, wo ich diesen Begriff Modulith zum ersten Mal gelesen habe, den können wir auch mal raussuchen. Ich bin mir nicht sicher, ob das die erste Stelle ist, wo dieser Begriff aufgetaucht ist, aber das war zumindest die erste Stelle, wo ich ihn gesehen habe.

Sven Johann:

Interessant, als dieser Microservice-Hype losging, da hat Simon Brown, bekannter Buchautor in der Software Architecture Community, Typ hinter C4, auch immer von Deployment Monoliten gesprochen und meinte aber Microservices, die nicht unabhängig voneinander deployt werden können, sondern du sagst, okay, wir haben jetzt diese Servicelandschaft, aber wir müssen die alle mehr oder weniger gleichzeitig deployen, weil alles von allem abhängt, also Microservices gone wrong.

Torsten Mandry:

Dann habe ich vielleicht das Schlechteste von beiden Welten, ich kann es nicht unabhängig deployen, aber auf der anderen Seite dann doch nicht.

Sven Johann:

Ich hatte von Spring Moduliths gehört und habe letztes Jahr einen Vortrag von Oliver Gierke dazu gehört. Ich bilde mir ein, dass er auch in die Richtung gegangen ist, wir deployen das als ein Ding, aber wir müssen diese Modulgrenzen enforcen.

Sam Newman hat ein Buch geschrieben von Monolith zu Microservices, wie komme ich von dem Monolithen zu Microservices? was gibt’s dafür Methodiken, um den Monolithen aufzubrechen? Wir haben uns bei INNOQ auch super viel die letzten Jahrzehnte damit beschäftigt. Jetzt hast du den Gedankengang in die andere Richtung gemacht. Ihr habt ein System, das Microservice-System, und da kam die Frage auf, wäre das nicht vielleicht besser ein Modulith? Habe ich das so richtig wiedergegeben?

Torsten Mandry:

Das war so ein bisschen die Motivation, wie ich zu diesem Thema gekommen bin, und das war nicht aus einem Projekt, sondern aus mehreren Projekten. Ich war in den letzten Jahren in mehreren Projekten, wo wir ein System neu aufgesetzt haben und sehr schnell bei der Entscheidung waren, vielleicht sogar zu schnell, wir machen das als Microservices. Spannend ist der Kontext, von dem ich jetzt spreche, weil das waren immer Situationen, wo wir als ein Entwicklerteam uns entschieden haben, unsere Domäne, die wir verantworten, noch mal in mehrere Microservices aufzubrechen. Wir waren ein Team, das für mehrere Services zuständig war. Was ein bisschen besonders war, war, dass alle unsere Services sehr uniform waren. Wir haben extra drauf geachtet, dass die nicht großartig in unterschiedlichen Technologien oder komplett unterschiedlich geschrieben sind, weil wir innerhalb des Teams immer mal wieder zwischen den Services hin und her gewechselt haben, so dass die alle sehr ähnlich waren. Die waren beispielsweise alle in Java, alle mit Spring, alle ungefähr auf dem gleichen Versionsstand. In all diesen Projekten sind wir irgendwann in Diskussionen gekommen, wo wir eigentlich vermeintlich ein kleines Feature abbilden wollten, was sich aber durch zwei oder vielleicht sogar noch mehr Services durchgezogen hat, und wo wir an den Service-Grenzen, also an den Schnittstellen, immer wieder Dinge übertragen mussten, und das Gefühl hatten, das ist alles so umständlich. Eigentlich wollen wir nur eine Kleinigkeit machen, aber wir müssen zig Schnittstellen anpassen und zwischen den, um das über die Schnittstellen zu schicken, unendlich mappen und viele Dinge tun. Da ist immer wieder die Frage hochgekommen, tun wir uns wirklich einen Gefallen damit, dass wir das hier gerade als Microservices versuchen zu betreiben, oder würden wir uns das Leben nicht viel einfacher machen, wenn wir das als, da kannte ich den Begriff Modulith noch nicht, wenn wir das als Monolithen sehen würden und betreiben würden. In all diesen Projekten haben wir den Gedanken aber nie weitergeführt, weil klassische Projektsituation, du hast gerade keine Zeit, dich mit solchen Gedanken zu beschäftigen, dann ist das auch ein Gedanke, der sehr grundsätzlich ist, das heißt, wenn du dich damit beschäftigst, dann ist das auch direkt ein ziemlich großes Fass, was du dann vielleicht aufmachst. Das haben wir nie getan, so dass ich nie so die richtige Vorstellung hatte, wie würde denn so ein Modulith, wie ich ihn jetzt nennen würde, aussehen? Wie würde der sich anfühlen, wenn ich damit solche Features entwickle? Was würde sich ändern gegenüber dem, was ich bei Microservices mache, und was würde wirklich besser werden und was vielleicht nur anders oder im schlimmsten Fall sogar schlechter oder schwieriger? Dadurch, dass das in fast allen Projekten immer mal wieder hochkam, die Frage, und ich da nie so eine Antwort oder so ein Gefühl dafür hatte, was das denn bedeuten würde, habe ich mich hingesetzt und habe das in einem kleinen Experiment für mich, in einem kleinen Spielprojekt mal ausprobiert und mal geguckt, wie sich das anfühlt, und habe einiges daraus gelernt.

Sven Johann:

Du referenzierst auch einen Artikel, der schon ein bisschen älter ist, von Martin Fowler: “You need to be this tall to use Microservices”. In dem Artikel geht er ziemlich viel auf Infrastruktur ein, dass die Infrastrukturkomplexität enorm zunimmt, wie wir alle wissen, aber das hört sich jetzt so an, als wäre das bei euch, das hat vielleicht ein bisschen mehr Geld gekostet, als irgendwo deployen. Ich kenne mich gar nicht mal so aus, aber früher gab es noch sowas wie AWS Beanstalk, wo wir unsere Monolithen deployt haben. Ich weiß nicht, ob das heute immer noch gibt.

Torsten Mandry:

Das gibt’s immer noch.

Sven Johann:

Also, wir machen jetzt einfach Beanstalk und fertig ist die Laube, aber da hört sich jetzt so an, als wäre das ein gelöstes Problem gewesen mehr oder weniger.

Torsten Mandry:

Also, zumindest war das nicht primär die Stelle, an der wir immer wieder hängen geblieben sind. Vielleicht hat sich an der Infrastruktur hinterher auch nicht mehr so wahnsinnig viel geändert. Wenn du deine Services deployt hast und die prinzipiell laufen, war es in den Projekten, wo ich unterwegs war, eher so, dass du in diesen Services irgendwas gemacht hast, aber das hat am Deployment und an der Infrastruktur relativ wenig geändert. Du musstest die zwar immer wieder mit auf dem Schirm haben, und die waren in den Projekten, wo ich war, auch deutlich unterschiedlich. Aber das war aus meiner Erfahrung nicht so die Stelle, wo du kontinuierlich im Projekt immer wieder dran hängen bleibst, sondern das war eher dieses Schnittstellenthema, wo du Aufwand reinsteckst. Und dass du Daten über HTTP verschickst in JSON Maps und auf der anderen Seite wieder ausliest und parst und mit Timeouts und Retries dich irgendwie rumschlagen musst. Das waren die Dinge, die uns ein bisschen wehgetan haben. In vielen Projekten gab es auch eine Unterscheidung auf zwei Ebenen. Ich erinnere mich an das erste Projekt, was relativ groß war und wo wir viele Teams hatten. Das war ein E-Commerce Projekt, klassische E-Commerce Vertikalisierung, und da waren die Teams anhand dieser Vertikalisierung geschnitten. Dadurch, dass es so groß war und alle Teams die Herausforderungen hatten, irgendwas deployen zu müssen, gab es eine Plattform, die zur Verfügung gestellt wurde oder die wir als Team einfach nur genutzt haben. Da hatten wir mit der vollumfänglichen Infrastruktur gar nicht so wahnsinnig viel zu tun, das wurde für uns gemanagt. Wir mussten sie nur verstehen, in dem Fall war das Kubernetes, und uns quasi da drauf setzen, so dass das Infrastrukturthema gar nicht mehr so wahnsinnig groß war, als wenn wir jetzt selber dieses Kubernetes hätten aufsetzen müssen.

Sven Johann:

Ja, stimmt. Wenn ich an meine letzten Kund:innen denke, mit Ausnahme von einem, eigentlich zwei, da war es auch so, die haben eigentlich schon alles parat. Da ist die Infrastruktur schon da, und ob ich da jetzt zehn Services deploye oder einen, ist egal. Bevor wir über Modularisierung reden, weil ich glaube, das ist der spannendste Punkt dabei, habe ich mir hier noch einen Einstieg aufgeschrieben, weil den finde ich ziemlich interessant. Ich habe ja schon Sam Newman erwähnt, er hat zwei Microservices-Bücher geschrieben. Mit dem hatte ich mal eine Aufnahme gemacht, und da sagt er interessanterweise, wenn Kunden zu ihm kommen und sagen, wir wollen Microservices, bitte berate uns, sagt er: “Nee, das mache ich nicht, sorry Leute.” Dann sind die mal ganz verwundert, und dann sagt er: “Hier sind die ganzen Nachteile, die Microservices haben. Geht mal mit der Hausaufgabe nach Hause und kommt mal zurück und überzeugt mich, dass das trotzdem noch eine gute Idee ist.” Er meinte, die kosten immer mehr Geld. Die können natürlich irgendwie helfen, mehr Geld zu machen, aber ihr müsst halt schon irgendwie begründen, weil da gibt’s einen Overhead. Das fand ich ganz interessant, weil dieser Stil eigentlich dafür da ist, wenn unser Ziel Time to Market ist. Wenn jemand sagt, schnelle Time to Market, und ich habe 100 Entwickler:innen, das wirst du mit einem Monolithen niemals schaffen, würde ich mal behaupten. Also ganz große Anwendungen, Time to Market, Monolith, vergiss es. Das ist die Motivation zu Microservices, was auch für mich nachvollziehbar ist. In unserem Vorgespräch hast du ja auch gesagt, bei uns kam das schon fast immer so automatisch. Wir haben Bounded Contexts identifiziert, und dann machen wir das. Kannst du noch mal euren Gedankengang nacherzählen?

Torsten Mandry:

Ich bin mir gar nicht so hundertprozentig sicher, wie der Gedankengang genau gewesen ist. Bei diesem allerersten Projekt, da war das, bevor ich überhaupt ins Projekt gekommen bin, die Entscheidung schon gefallen, und da habe ich das eher von den Entwicklern und Entwicklerinnen immer mal wieder so erzählt bekommen, dass das sehr schnell in Richtung Microservices ging. An anderen Stellen war ich, glaube ich, auch selber jemand, der sofort in Richtung Microservices gedacht hat, weil du bei einem Microservice eine sehr saubere und vor allen Dingen sehr starke Entkopplung voneinander hast. Du hast starke Grenzen, starke Mauern hochgezogen zwischen deinen einzelnen Modulen, und ohne mir dessen so richtig bewusst zu sein, war das eine Erkenntnis, die ich aus meinem Experiment gewonnen habe. Wenn ich gesagt habe, lass uns Microservices machen, dann war das vielfach auch ein Stück weit unbewusst, und ich würde einfach mal behaupten, dass das bei vielen auch der Fall ist, weil man sich vielleicht dieser ganzen Nachteile, die du gerade erwähnt hast, gar nicht so bewusst ist in dem Moment, wo man die Entscheidung trifft. Vielleicht nimmt man die auch hinterher nicht mehr so wahr, und es gibt ja jetzt auch nicht nur Nachteile. Ich würde sagen, das hält sich ungefähr die Waage, und letztendlich ist es eine Frage, wie wichtig mir diese Modularisierung ist und wie wichtig mir vor allen Dingen auch ist, dass ich die möglichst sauber und stark entkoppelt einhalte. Wenn mein Gefühl ist, eigentlich ist das für mich gar nicht so wichtig, dann sind Microservices vielleicht die falsche Entscheidung. Mein Gefühl nach wie vor, auch nachdem ich jetzt mit diesem Modulithen rumgespielt habe, ist aber, wenn mir die Modularisierung wirklich wichtig ist, dann will ich früher oder später diese einzelnen Module auch als getrennte Services haben, weil ich dann wirklich diese starke Entkopplung habe, aber vielleicht nicht unbedingt von Anfang an.

Sven Johann:

Ist Modularisierung denn immer wichtig?

Torsten Mandry:

Es hängt davon ab, auf welcher Ebene wir uns gerade bewegen. In diesem großen E-Commerce Projekt, Vertikalisierungsprojekt, da war die erste Ebene, wo Module geschnitten werden, quasi schon die Vertikalen. Das heißt, du hast dann Search Products, auf jeden Fall eine, wo du die Produktdarstellung und so weiter hattest – eine hieß, glaube ich, Checkout oder sowas in der Richtung. Auf der Ebene ist die Modularisierung auf jeden Fall wichtig. Wenn du ein Team von 100 Leuten hast, dann wird das wahrscheinlich mit einem Monolithen nicht vernünftig funktionieren. Sobald du getrennte Teams hast, willst du nicht mit mehreren Teams an einem Monolithen arbeiten, oder du musst dir extrem gut überlegen, wie du das so hinkriegst, dass diese Teams trotzdem unabhängig voneinander arbeiten können und sich da nicht ins Gehege kommen. Ich habe mir dann schon mindestens eine Ebene tiefer angeguckt, das heißt, wir waren in einer Vertikalen. Und hätten jetzt in dieser Vertikalen die Option gehabt zu sagen, wir sind ein Modul oder wir repräsentieren ein Modul oder implementieren genau ein Modul, und das können wir vielleicht noch mal strukturieren und noch mal ein bisschen tiefer modularisieren, aber da ist die Modularisierung dann vielleicht nicht mehr ganz so wichtig, wie die auf der aller obersten Ebene. Es ist immer so ein Abwägen, wie viel bringt mir das hier und wie viel kostet es mich?

Sven Johann:

Wenn es mehrere Teams sind, dann bleibt einem gar nichts anderes übrig. Ich meine, aber man muss dazu sagen, es ist ja auch nur so halb richtig, weil wenn Microsoft oder Apple ein Betriebssystem shippen oder wenn IntelliJ die IDE shippt, da kommt ja auch irgendwie so ein sehr großer Monolith raus. Im Continuous Delivery Buch von vor 15 Jahren, da haben die auch das Beispiel von HP, glaube ich, also auf jeden Fall irgendeine Druckersoftware, und da arbeiten sehr viele Teams dran, und die wird auch irgendwie als Monolith deployt, wobei ich auch gar nicht so genau weiß, ob das dann doch vielleicht einfach so ein Haufen, keine Ahnung, DLLs oder was auch immer rausplumst, dass man vielleicht keine Microservices hat, aber tatsächlich unabhängige deploybare Komponenten.

Torsten Mandry:

Vielleicht nicht unabhängig deploybar, aber zumindest irgendwie unabhängig releasebar, und ich kann die dann relativ flexibel jeweils den aktuellen Stand nehmen, zusammenfassen und das dann wieder deployen, was letztendlich auch so eine Art Modulith ist. Ich habe eine Einheit, die ich hinterher ausliefere, das ist das Betriebssystem oder die große IDE oder was auch immer. Das ist vielleicht der Vorteil in unserem Genre, dass wir die Option haben, das in unterschiedliche Applikationen und unterschiedliche Stücke Software oder Deployment Units zu unterteilen und die dann miteinander sprechen zu lassen. Das geht wahrscheinlich nicht überall. Da ist aber wahrscheinlich auch nennenswerter Aufwand reingeflossen, wenn das so gemacht wird, dass das möglich ist und dass das möglichst problemlos möglich ist.

Sven Johann:

Ja, genau, also da, ich zitiere wahrscheinlich falsch, aber ich bilde mir ein, dieser Typ von HP, der hat doch danach eine Reihe Bücher dazu geschrieben, der meinte, die haben ein Jahr nichts anderes gemacht, als existierende Software zu strukturieren. Damit sie irgendwie dazu in der Lage sind. Also kein Feature, kein gar nichts, nur an Strukturierung gearbeitet.

Du hast ja jetzt, okay, jetzt habe ich es so verstanden, bei großen Enterprise Applikationen, wir haben Domänenschnitt, da ist eine Domäne, ein Team. Da würden wir jetzt sagen, macht wahrscheinlich in fast allen Fällen Sinn, dass das eigene Services sind, aber innerhalb dieser Domäne wollen wir dann und unter welchen Umständen sollten wir es weiter aufsplitten? Ich glaube, da kommt es auch immer noch mal drauf an. Du hast jetzt gesagt: Ja, wir haben eine Änderung gehabt und die hat sich durch mehrere Services gezogen. Jetzt kann ich mir natürlich ziemlich gut vorstellen, dass natürlich das in einer Codebase einfacher zu machen wäre, aber wie war da dein Gedankengang? Also was wäre jetzt einfacher gewesen, wenn man einen Modulith gehabt hätte, jetzt in diesem einen, speziellen Fall?

Torsten Mandry:

Also zumindest von dem Gedanken her, wie gesagt, die diese sehr konkreten Fragen, das was wir jetzt hier gerade machen, wäre das nicht viel einfacher in dem Modulithen, die haben wir ja in dem konkreten Fall nie so nachverfolgt. Das was ich gemacht hatte und wo dann auch am Ende dieser Vortrag rausgepurzelt ist, ich habe mir von meinem aktuellen Projekt, wo wir auch wieder an so einer Stelle waren, habe ich mir die Domäne mal geschnappt und habe die mal versucht als Modulith so ein bisschen auf der grünen Wiese neu zu entwickeln, ne? Also natürlich jetzt nicht bis ins kleinste Detail, weil da haben wir halt mit einem auch teilweise sehr großen Team relativ lange dran gearbeitet, aber zumindest mal irgendwie soweit ins Detail, dass ich halt genau an solchen Modulschnittgrenzen und vor allen Dingen an den Schnittstellen, das waren halt, wenn ich mich richtig zurück erinnere, immer so die Stellen, wo genau solche Fragen aufkamen. Also wenn ich halt irgendwie Informationen oder Ereignisse oder was auch immer von einem Modul ans andere übergeben musste oder in einem anderen Modul irgendwas auslösen wollte oder so. Das waren immer so die Stellen, wo wir gehangen haben. Und ich habe mich halt hingesetzt und habe versucht mal rauszufinden, für mich so eine Idee zu kriegen, wie würde das denn aussehen, wenn ich das in dem Modulithen machen würde, wo ja vermeintlich erstmal alles viel enger zusammenhängt, ne? Und ich glaube, so ein Modulithen hatten wir gerade schon, dass das Thema, den kann ich auf unterschiedlichsten Ebenen schneiden. In meinem Fall habe ich das auch wieder in Java gemacht, weil unser aktuelles Projekt auch in der Sprache entwickelt ist. Und da habe ich halt auch unterschiedliche Ebenen, wie ich diese einzelnen Module aufteilen könnte und ich habe mich vermeintlich für die aller einfachste entschieden und habe gesagt, ich packe das alles in einer Applikation und trenne das halt einfach auf Package Ebenen und dann führt das dazu, dass ich letztendlich alles zusammen in einer Runtime habe und halt direkt auf Dinge zugreifen könnte, theoretisch, ja.

Sven Johann:

Kurze Frage: Meinst du ihr habt, das heißt so, dass sozusagen Package by Feature gemacht, also es gab Service 1 und das war praktisch im, ich sag mal im Package Service 1, was auch immer die Domäne war, Domain 1, ja?

Torsten Mandry:

Genau.

Sven Johann:

Und dann okay, ja, dann kann ich es mir vorstellen.

Torsten Mandry:

Und die Erwartung war so ein bisschen, dass dadurch gerade an den Schnittstellen alles viel einfacher werden müsste, weil theoretischer Zugriff viel direkter ist. Und das war eine der wesentlichen Erkenntnisse, die ich hatte, wenn ich Modularisierung ernst nehme und sage, okay, ich habe das jetzt zwar irgendwie nebeneinander, direkt nebeneinander in zwei Packages liegen und ich könnte jetzt irgendwie auf alles Mögliche in den jeweils anderen Package zugreifen, aber ich will das nicht. Ich will das sehr kontrolliert tun und möglichst stark voneinander entkoppelt tun, auch wenn ich dafür noch irgendwie Aufwand betreiben müsste, dann war meine Erkenntnis, dann ist es vielleicht gar nicht so viel einfacher, weil ich viele Dinge dann mehr oder weniger genauso tue und genauso aufsetze, wie ich das mit getrennten Services machen würde, nur ich meine Schnittstelle nutze halt dann nicht mehr HTTP als Übertragungsweg und als Protokoll, sondern halt ein etwas einfacheres Austauschformat und aber vieles hängt davon ab, wie entkoppelt ich das Ganze sehen möchte, ne? Und wo ich auch perspektivisch irgendwie hin will. Also, ob mir jetzt schon klar ist, dass ich immer bei diesem Modulithen bleiben will, oder ob mir vielleicht jetzt schon klar ist oder ich jetzt schon den Verdacht habe, dass ich irgendwann mal auf die Idee kommen könnte, da jetzt einzelne Teile von als einzelne Module letztendlich doch als separate Services rauszuziehen. Ja. Wo es ja gute Gründe geben kann, ne? Also bei uns in der aktuellen Domäne ist das beispielsweise, wir haben Services, die sind von den Anforderungen an Verfügbarkeit und Performance deutlich höher einzustufen als andere. Und die will ich dann vielleicht irgendwie gesondert skalieren, weiß ich nicht, mit mehreren Instanzen aufsetzen, weil um halt irgendwie Ausfallsicherheit zu haben und und also da will ich vielleicht Dinge tun, die ich nicht insgesamt für alles tun möchte, was ich entwickle, sondern die ich sehr gezielt nur für diese eine Komponente, für dieses eine Modul tun möchte. Und solche Dinge haben wir und deswegen will ich mich da vielleicht auch jetzt nicht zu stark auf dieses es soll möglichst einfach sein konzentrieren, sondern auch immer im Hinterkopf haben, es könnte sein, dass ich irgendwann auf die Idee komme, das halt wieder rauszuziehen.

Sven Johann:

Also, ich hatte nämlich gedacht, und vielleicht ist, habe ich richtig gedacht, aber hört sich jetzt erstmal nicht so an. Ich habe gedacht, du hast jetzt z.B. Package by Feature und natürlich ist es so, wenn ich jetzt irgendwie im Modulithen bin und ich würde jetzt sagen, ich nehme die Modularisierung ganz so ernst und dann sage ich, okay, ich bin definitiv schneller, weil ich muss, also ich habe weniger Overhead, ja, weil ich jetzt irgendwie an mehreren Stellen was anpassen muss. Ich muss jetzt die Schnittstellen in, ich sag mal so einfach in so einer Java Codebase, die sind halt schneller angepasst, wie wenn ich da an unterschiedlichen Deployment Units rumfummeln muss und so weiter. Ich könnte theoretisch, so habe ich es auch verstanden, manchmal kann ich auch ein paar Abkürzungen nehmen, wenn ich die Modularisierung nicht so ernst nehmen würde. Was ich gedacht habe, war, dass das natürlich auch einfacher ist, zu sagen, in dem Monolithn oder Modulithen, Ups, da habe ich mir vor drei Monaten gedacht, dass das der richtige Schnitt ist, aber leider falsch gedacht. Ich müsste jetzt mal hier so ein paar Sachen hin und her schieben, das kriege ich vielleicht von heute auf morgen hin, aber das ist so ein, ich sag mal, jeden Sprint ein bisschen, ich habe so einen Transformationspfad und ich will das irgendwie innerhalb von, weiß ich nicht, drei Monaten will ich praktisch einen neuen Domänenschnitt innerhalb von meinem Modulithen haben. Und hat das auch eine Rolle gespielt im Experiment?

Torsten Mandry:

Definitiv, weil also die dieses wie würde sich das als Monolith anfühlen, war in dem, was ich gemacht habe, tatsächlich auch nur ein Teil, das war so ein bisschen der treibende Teil, deswegen ich das angefangen habe. Aber eine andere Option oder eine andere Idee, die ich hatte, war auch mal einen etwas anderen Modulschnitt auszuprobieren. Also, der war für mich in dem Moment neu, also es war halt nicht exakt der, den wir im Projekt bekommen haben, sondern ich wollte was Neues ausprobieren und da habe ich genau diesen die dieses Vorgehen gehabt, ne? Ich hatte so eine Idee und habe das erstmal angefangen und dann habe ich aber festgestellt, ah, nee, so hundertprozentig passt es nicht, jetzt müsste es hier an der Stelle eigentlich anders aussehen, das Teil gehört eigentlich darüber und und das müsste eigentlich anders benannt sein, zwischen den Modulen und da hilft das ganz klar, wenn ich das alles in einem zusammenhängenden in dem Fall jetzt Java Projekt habe, kann aber wahrscheinlich auch jede andere Programmiersprache sein, wo ich eine IDE habe, die die mächtige Refactoring Werkzeuge hat, wo ich halt irgendwie so ein so ein Renaming oder so ein Move oder was weiß ich was machen kann und die mir dann direkt sagt, ja, kannst du machen, aber dann musst du die Methode jetzt public machen, weil ansonsten wird die da nicht mehr zugreifbar sein und so weiter. Das habe ich halt nicht, wenn ich da diese HTTP Grenze dazwischen habe, ne? Weil dann dann kann die IDE das nicht wissen. Und das hat mir auf jeden Fall genutzt. Und das war vielleicht auch so ein bisschen wo ich bisher halt auch immer relativ schnell dabei war, zu sagen, wir haben irgendwie getrennte Module jetzt identifiziert, dann lass uns da getrennte Services draus machen, würde ich mittlerweile jetzt wäre ich eher dabei zu sagen, lass uns die erstmal als Modulith mit irgendwie, weiß ich nicht, getrennten Packages oder vielleicht mit irgendwelchen anderen auf irgendwelchen anderen Ebenen trennen, aber lass uns die noch nicht so hart trennen, weil die Erfahrung sagt mir, da wird sich vielleicht jetzt gerade in der Anfangsphase noch das ein oder andere bewegen.

Sven Johann:

Da gibt’s natürlich auch Streit drüber, ne? Also da gibt’s auch wieder dieses kommt drauf an, verlinken wir auf jeden Fall in den Shownotes, weil die vor also der der Stefan Tilkov hat ja und der Martin Fowler, die haben ja sozusagen diese beiden Artikel geschrieben. Martin Fowler im Grunde genommen so argumentiert wie du, ja, also. Zuerst Monolith, dann schauen, wie sich die Modulgrenzen entwickeln. Ich glaube, das hat er nicht gesagt, aber es ist einfacher, Änderungen im Monolithen vorzunehmen.

Stefan Tilkov sagte Microservices First. Martin Fowler sagte danach, wir haben es so schwammig formuliert, dass am Schluss jeder sagen kann, er habe Recht gehabt. Der Artikel ist schon etwas älter, 2015 oder so, also fast 10 Jahre. Jetzt können wir auf relativ viel Erfahrung zurückgreifen. Ich war immer im Monolith First Lager und dachte, Microservices First kann niemals funktionieren, aufgrund historischer Erfahrungen. Jetzt würde ich sagen, es kommt darauf an. Bei INNOQ gab es ein paar Kunden, da war die Aufteilung unserer Domäne sonnenklar. Ich denke gerade an eine Integrationsplattform, da haben die Leute gesagt, das ist völlig klar, wie eine Servicedomäne aussieht. Ich erinnere mich nicht mehr an die exakten Namen, aber sie hatten Teile, die integriert werden mussten, die einfach fix sind, da ändert sich nichts, weil das z.B. Standardsoftware vorgibt. Wenn ich von Standardsoftware A zu B gehe, gibt es eine Transformation von A nach B, und A und B sind fix. Wir brauchen diese Transformatoren, da war Microservice First klar, alles andere wäre Quatsch gewesen. Ich bin auf deine Meinung gespannt, weil es sich gerade ein bisschen anders anhörte. Ich war der Meinung, es gibt diverse Domänen, wie E-Commerce, die sind schon so gut durchgestartet, dass man auf großer Ebene gar nicht daneben liegt. Wir haben es so gut verstanden, dass wir schon so viele Projekte gemacht haben, und die Tools haben, wie wir diese Domänen finden. Das passt, da würden wir, gilt jetzt nicht für alle. Ich hatte auch schon Projekte, wo wir Havarie erlitten haben, weil wir es nicht verstanden haben und zu früh reingesprungen sind. Vielleicht ist die Kunst zu verstehen, wo wir es wirklich verstanden haben und wo nicht.

Torsten Mandry:

Wenn ich auf das erste E-Commerce-Projekt zurückgehe, das war im Nachhinein betrachtet ein sehr klassisches E-Commerce. Es gibt einen Shop, man kann Produkte in den Warenkorb legen, auf Bestellen klicken, durch den Checkout gehen und bekommt die Ware geliefert. Was wir auf oberster Ebene hatten, also die Vertikalisierung der einzelnen Vertikalen, war der Standard, den man bei jedem Shop hat. Das hat auch im Großen und Ganzen gepasst. Vielleicht lag es daran, dass unsere Vertikale alles gemacht hat, nachdem der Kunde auf Bestellen geklickt hatte, also wir hatten relativ wenig Customer Facing Sachen, sondern waren eher im Backend angesiedelt. In der Vertikalen gibt es immer wieder Besonderheiten des jeweiligen Kunden, besonders wenn es ein Kunde ist, den es schon ewig gibt und der seine eingefahrenen Prozesse hat, die er im E-Commerce abbilden will. Dann merkt man an manchen Stellen, hier passt es nicht hundertprozentig, weil die Prozesse des Kunden entstanden sind, als er noch nicht über Vertikalisierung nachgedacht hat, sondern es so gemacht hat, wie es für ihn am natürlichsten war. Das ist sehr stark davon abhängig, was sie tun wollen und wie sie intern aufgestellt sind. Selbst in solchen Domänen, wo es auf den ersten Blick so aussieht, als wäre alles klar, und es bei 20 anderen Kunden geklappt hat, kommt man im Detail an Stellen, wo man sagt, da müsste es anders aufgeteilt sein, und da habe ich Abhängigkeiten, die ich bisher bei keinem anderen Kunden hatte. Deswegen würde ich das nicht so grundsätzlich sagen, dass man das immer schon im Vorfeld sehen oder abschätzen kann. Es gibt Wahrscheinlichkeiten. Man kommt in Projekte, wo man sagt, wahrscheinlich ist es wie bei 20 anderen Kunden, und dann weiß ich schon ungefähr, was ich tun muss. Und man kommt in Projekte, wo man sagt, keine Ahnung, wie das hier ist, die Domäne habe ich noch nie gesehen, da muss ich mich erstmal reinfuchsen. Ich lerne heute noch jeden Tag, hier ist alles komplett anders, weil es ein sehr spezielles E-Commerce-Segment ist, wo Kleinigkeiten alles ein bisschen umdrehen. Man kann in die Falle laufen.

Sven Johann:

Was ich mitnehme, bei Vertikalisierung, also großes System, viele Leute, da können wir uns an einen Schnitt wagen, aber wenn wir in diesen Schnitt reingehen, arbeiten wir mit Wahrscheinlichkeiten. Wie wahrscheinlich ist es, dass wir hier direkt in Microservices gehen können, weil wir die Erfahrung haben, wir haben das schon zehnmal gemacht und es wird jetzt wahrscheinlich genauso sein. Risiko ist immer dabei, dann würden wir Microservices, dann könnten wir Microservice First machen. Wenn es neu ist, würden wir Modulithh First machen. Wobei ich jetzt gerade, wo ich so drüber rede, innerhalb der Domäne, könnte ich sagen, wenn es ein Team ist, kann man eigentlich immer Modulith First machen.

Torsten Mandry:

Das wäre gerade mein Gefühl gewesen. Ich will nicht sagen, dass es nie sinnvoll ist, Services innerhalb eines Teams zu separieren. Aber wahrscheinlich ist es eine gute Idee, den Moment herauszuzögern. Gerade wenn ich eine Domäne neu aufsetze und weiß, dass ich in den nächsten Wochen und Monaten noch sehr viel über diese Domäne lernen werde, dann ist es vielleicht keine so gute Idee, den Modulschnitt direkt in Stein zu meißeln oder zumindest so zu manifestieren, dass ich Aufwand betreiben muss, um ihn anzupassen. Das wäre mein Gefühl. Es kann gute Gründe geben, wie z.B. Verfügbarkeit für bestimmte Module oder Performance. Ein anderer Kollege hatte das Beispiel gebracht, wenn ich an einem Modul bestimmte Lizenzen benötige, die instanzbezogen sind, dann will ich eher eine negative Skalierung haben. Ich möchte möglichst wenig Instanzen von diesem Ding haben. Wenn ich aber in derselben Domäne an einer anderen Stelle etwas habe, wo ich sehr viele Instanzen brauche, dann ist das vielleicht ein Grund, diese beiden Sachen zu trennen. Es gibt gute Gründe, die dafür sprechen, das dann doch wieder in Services zu trennen. Die Frage ist, wann muss ich das tun? Wie lange kann ich das rauszögern, wie bei Architektur Entscheidungen? Wie lange kann ich die Entscheidung noch rauszögern und davon profitieren, dass ich das jetzt noch nicht gemacht habe? Ab wann muss ich es wirklich tun und wie groß ist dann der Aufwand? Da sind wir wieder bei dem Punkt, wenn ich schon absehen kann, dass ich so einen Fall haben werde. Spätestens dann, wenn ich damit in Produktion gehe und das erste Mal mehrere Instanzen betreiben will, dann will ich das getrennt haben, aber das ist vielleicht noch sechs Monate hin, dann kann ich vielleicht vier Monate sagen, ich entwickle das jetzt als Modulith, aber ich achte darauf, dass ich diese Modularisierung ernst nehme und dass ich das auch in diesem Modulithen versuche so zu entkoppeln, dass dieser Schritt, ich ziehe es jetzt raus in einen separaten Service, mir möglichst wenig weh tut und möglichst wenig Aufwand verschafft. Vielleicht weiß ich das nicht, aber ich habe das Gefühl, das sein könnte irgendwann, dann ist es eher ein Invest in die Zukunft, wobei man da dann auch immer wieder abwägen muss, wie groß ist die Wahrscheinlichkeit, dass ich das tatsächlich irgendwann mal tun will und wie viel Aufwand muss ich dafür jetzt reinstecken, dass ich das dann später tun kann und wiegt sich das irgendwie auf. Das ist schwierig, das im Vorfeld abzuschätzen.

Sven Johann:

Ich bin auf der Suche nach Heuristiken. Es ist nie so eindeutig, aber dass wir Heuristiken haben, wo wir uns so ein bisschen dran hangeln können bei der Entscheidung. Ich nehme mit, der berühmte Last Responsible Moment, so lange hinauszögern, wie es nur geht. Es gibt diverse Qualitätsanforderungen, Security related, Performance related, Skalierung und so weiter, Kosten related, wo man dann noch mal sagen muss, okay, hier haben wir vielleicht noch mal ein anderes Auge drauf. Ansonsten ist eine gute Idee, erstmal rauszuzögern.

Torsten Mandry:

Das was ich gelernt habe, insbesondere in Diskussionen, nachdem ich diesen Vortrag gehalten habe, ich hatte den vorher schon mal intern in der Firma gehalten und da sind mehrere Kollegen auf mich zugekommen, die andere Erfahrungen haben, andere Sichtweisen haben, andere Aspekte haben, die für sie in ihren Projekten wichtig waren. Da waren viele Sachen dabei, die ich, bis ich dieses Experiment getrieben habe, noch nicht kannte, die aber alle da reinspielen. Wenn ich die Option sehe, dass ich irgendwann mal in der Lage sein möchte, das als Service rauszuziehen und wenn ich sage, ich will diese Modularisierung innerhalb meines Monolithen wirklich auch enforcen, dann war eine wichtige Erkenntnis für mich, dann muss ich im Monolithen explizit Dinge tun. Ich muss mir Sicherheitsnetze einbauen. Ich muss vielleicht mehr Aufwand betreiben, als ich das normalerweise innerhalb einer Applikation tun würde, um das sicherzustellen. Ein gutes Beispiel war, ich hatte die Module in nebeneinanderliegenden Packages und dann aber innerhalb der Module logischerweise auch weitere Unterpackages. Dann kommst du in Java nicht drumrum, dass du Sachen als public definieren musst, so dass sie prinzipiell überall sichtbar sind, obwohl du sie eigentlich nur innerhalb deines Moduls haben willst und obwohl die eigentlich von außen nicht sichtbar sein sollen. Dann stellt sich die Frage, wie kann ich verhindern, dass ich nicht aus Versehen auf diese Dinge zugreife, aus einem anderen Modul? Da musste ich mir Sicherheitsnetze bauen, eine Fragestellung, die du in Microservices nicht hast, wenn du getrennte Services hast, dann kommst du auf das API und auf nichts anderes. Ein Kollege hatte mich auf das Thema Transaktionsgrenzen gebracht, das hatte ich bisher noch gar nicht auf dem Schirm, weil ich so weit gar nicht gegangen war in meinem Experiment. In Microservices habe ich automatisch die Transaktionsgrenzen. Wenn ich die im Monolithen haben will, muss ich da, glaube ich, auch noch mal ekelhafte Dinge tun, um das sicherzustellen oder das Thema asynchrone Kommunikation. Wenn ich auf die Idee komme, irgendwann das rauszuziehen als separaten Service, dann sollen die möglichst asynchron miteinander kommunizieren. Vielleicht will ich das dann auch in meinem Monolithen schon und muss ich da wieder Aufwand betreiben. Du kriegst es nicht geschenkt. Du steckst viel Aufwand rein, den du bei Microservices nicht mehr reinstecken musst, dafür steckst du bei Microservices an anderen Stellen, Infrastruktur, Deployment und so weiter, Aufwand rein. Das ist vielleicht ein Punkt, der für Microservice First spricht. Wenn ich das Gefühl habe, ich will Microservices haben, warum soll ich dann viel Aufwand betreiben, um es nachzubauen, um es dann wieder wegzuwerfen, wenn ich es tatsächlich auseinanderziehe? Es ist ein Abwägen, leider gibt es wahrscheinlich nicht die eine Lösung.

Sven Johann:

Ich kann mich erinnern, vor langer Zeit, vor INNOQ, da ging es gerade los mit Microservices. Adrian Cockcroft hat die ersten Vorträge darüber gehalten, und dann waren wir alle scharf auf Microservices. Man denkt, oh, genau das! Wir hatten ein Kundenprojekt, und wir haben die Go to Konferenz organisiert und die Kunden eingeladen, um sie weichzukochen. Der Kunde, bei dem ich war, unser Director Digital, hat gesagt, er ist noch nicht überzeugt. Monolith, wir gehen Monolith. Meine Kollegen, die schon lange DDD gemacht haben, haben gesagt, wir bauen diese Applikation. Hier gibt es vermutlich Bounded Kontexte, die wir identifizieren, die wir vielleicht von Anfang an nicht richtig identifizieren, aber wir machen jetzt ein Modulith. Wir haben mehrere Bounded Kontexte innerhalb unseres Monolithen, und jeder dieser Bounded Kontexte hat ein eigenes Datenbankschema. Die Datenbanken sind getrennt. Du kannst die nicht verbinden, sie benutzen unterschiedliche Connections. Das war eine Sache. Wie kommunizieren diese unterschiedlichen Bounded Kontexte? Ich weiß nicht, ob das ein Ugly Teil war, ich bin gespannt auf deine Meinung. Wir hatten Spring in Memory Messaging, eine Messaging Lösung innerhalb deines Monolithen, also publish subscribe. Du imitierst ein Event, und das wird von anderen Stellen, also von dem anderen Bounded Kontext in Empfang genommen. Wir hatten ein eigenes CQRS Framework, Axon. Axon war monolithisch, aber Axon ist eventbasiert. Der Schreibvorgang kommt rein, du nudelst an deinem Aggregat rum, und das Ergebnis, was die Read Models wissen sollen, wird über in Memory Events publish subscribe verteilt. Axon ist ein bisschen cleverer, um das multithreaded zu machen. Einen ähnlichen Mechanismus gab es da auch. Du imitierst dein Event, und fertig ist die Laube, dann weißt du, der andere kriegt das, und er kann es auch doppelt in seiner Datenbank vorhalten. Ich bin dann zurück nach Deutschland gezogen. Ich weiß gar nicht, ob das jemals Microservices wurden, aber die Idee war, wenn jemand kommt und sagt, wir dürfen Microservices machen, dann splitten wir das auf. Jetzt würden wir sagen, wenn wir den Bedarf sehen, dass ein Service rauskommt, dann ist unser Gefühl, dass wir da sehr leicht rauskommen, weil wir schon getrennte Datenbanken haben. Das kommt vielleicht auch aus dem Axon Umfeld. Wir haben kein CQRS gemacht, aber diese Denke war schon da. Axon arbeitet viel mit Sagas. Wenn du keine große Transaktion hast, dann machst du eine Saga. Das war schon in der Denke drin und eingebaut. Ich habe das Gefühl, nach deinem Vortrag und dem von Oliver Gierke, dass man da wieder ein Revival machen könnte in dieser Denke.

Torsten Mandry:

Dass du innerhalb einer Applikation solche internen in Memory Events auslöst und darauf reagierst, ist ein Feature in Spring. Es gibt den Event Publisher, den wir in unseren Projekten nutzen, weil es dir noch mal eine Möglichkeit gibt, Dinge besser voneinander zu entkoppeln. Dass ihr getrennte Datenbank Connections verwaltet habt, ist schon aus dem Mainstream raus. Da musst du schon Aufwand betreiben und wissen, was du tust, und baust dir damit Komplexität auf. Du versuchst, innerhalb einer Applikation diese Grenzen hochzuziehen, die du mit einem Service automatisch mitbekommst. Das ist ein Pluspunkt für Microservices, dessen man sich bewusst sein sollte. Wir haben das damals nicht im Kopf gehabt, weil wir nicht gesehen haben, dass mit der Entscheidung, ich setze das jetzt als Microservices auf, Kosten entstehen. Das merkst du im Projekt, und es tut weh, aber es kommen auch Architektur Features mit, die man vielleicht nicht wahrnimmt. Du gewöhnst dich daran, und du denkst nicht mehr darüber nach, was das eigentlich ist, was du hier hast, was das bedeutet und was du für einen Aufwand betreiben müsstest, wenn du das nicht hättest. Das war eine Meta Erkenntnis, die in den letzten Monaten entstanden ist, als ich mich damit beschäftigt habe. Du bekommst sie nicht geschenkt, du zahlst dafür an anderer Stelle, aber wenn du das an anderer Stelle zahlst, kriegst du das quasi mit in dem Paket drin.

Sven Johann:

Wenn ich Modularisierung ernst nehmen will, dann wird es zwar einfacher, aber nicht so einfach, weil wir vielleicht unterschiedliche Datenbank Connections brauchen, wir müssen mit internen Events arbeiten. Die Möglichkeiten, wie ich diese Grenzen enforcen kann, weil Java bietet nur auf Klassenebene Modifier an, aber so anders wird es schwer. Wir können Module System machen. Du hast gesagt, dass man Arc Unit nutzen kann, als Enforcer.

Torsten Mandry:

Ich habe mich gegen Plattform Modules entschieden, weil ich mich damit nicht auskannte. Ich hatte sofort die Idee, Arc Unit zu nehmen, um Regeln zu definieren, was ich darf und was nicht. Ich bin über Spring Modulith gestolpert, ein Spring Projekt, das dieses Thema adressiert, was im Grunde genommen den Begriff eines Application Modules in die Spring Welt reinbringt mit Features, die ich brauche, wenn ich einen Modulithen mit Spring baue. Ich kann meine Module intuitiver definieren und definieren, welche Module auf welche andere Module zugreifen dürfen, und das generiert mir die entsprechenden Arc Unit Regeln, die das enforcen. Plus ein paar andere Sachen, Application Events, da gibt es einfachere Möglichkeiten, dass die persistiert werden, damit ich die nicht verliere, wenn die Applikation abstürzt. All solche Sachen, die mir helfen, eine Applikation mit getrennten Modulen auszuzeichnen, die schon ein Stück weit asynchron miteinander interagieren. Im Grunde genommen, so zu tun, als wenn diese getrennten Module getrennte Services wären.

Sven Johann:

Okay, ich fasse für mich zusammen. Wir können mit einem Modulithen starten, weil wir mit Änderungen rechnen. Ursprünglich hätten wir Abkürzungen nutzen können, aber das würde alles verrotten lassen. Wir müssen Maßnahmen treffen, um eine spätere Trennung zu erleichtern. Diese Maßnahmen machen unseren Modulithen komplexer, sei es durch interne Eventsysteme, unterschiedliche Schemata und Datenbanken, oder durch Enforcement über Arc Unit. Ich bin gespannt, weil du Java Module System erwähnt hast. Ich war früher OSGI-Fan. Ich fand OSGI war eine super Idee. Ich bin wahrscheinlich der Einzige, der OSGI gut findet, aber da war die Idee, wie wir die Modulgrenzen härter machen können, jenseits der Programmiersprache. Es gibt keine Querverweise in interne Public Methoden. OSGI ist irgendwann eingeschlafen. Java Module System habe ich mir nur pseudomäßig angesehen. Ich kenne die Beschwerden der OSGI-Leute. Daher bin ich gespannt und war immer ein Arc Unit Gegner, aber ich habe mich überzeugen lassen. Das tut es. Ich bin gespannt, ob wir irgendwann mehr Modulithen machen.

Torsten Mandry:

Ich habe mir die Java Plattform Modules bisher noch nicht angesehen, weil ich in Microservice-Projekten unterwegs war. Da war das Bedürfnis nicht da. Vielleicht willst du deinen Service intern modularisieren, aber die Bedeutung ist nicht so groß. Man ist mit pragmatischeren Mitteln zufrieden. Wenn eine Klasse public ist, die es nicht sein sollte, drückt man ein Auge zu. Ich bin nie auf die Idee gekommen, das weiter zu enforcen, außer vielleicht ein paar grundlegende Arc Unit Regeln. In dem Moment, wo du die Modularisierung enforcen willst, könnte das genau das sein, wofür Java Plattform Modules gedacht waren. Ich muss mal gucken, vielleicht versuche ich das mal mit Java Plattform Modules, um ein Gefühl dafür zu bekommen, wie es funktioniert und was es mir abnimmt.

Sven Johann:

Ich bin gespannt auf deine Meinung. Ich würde mich freuen, wenn es mehr Chancen gibt als bei OSGI. Ich habe deinen Blogpost gelobt. Du hast auch einen Blogpost, wie du peu à peu mit Arc Units Onion Architecture enforced hast, oder?

Torsten Mandry:

Das war nicht Arc Unit, sondern JQ Assistant, weil Arc Unit bei Regelverstößen direkt abbricht. Und bei JQ Assistant kannst du es so einstellen, dass es dann eine Warning gibt. Und gerade, wenn du so etwas Schritt für Schritt machen willst, dann hast du halt am Anfang ganz viele Warnings. Und dann guckst du halt so nach und nach, dass es dann weniger werden. Da gibt es irgendwo noch einen Blogpost dazu. Das haben wir in einem Projekt tatsächlich gemacht.

Sven Johann:

Jetzt kommt mir noch eine Frage auf. Wenn wir unseren Modulithen so aufbauen, wie wir es gesagt haben, und wir haben eine Änderung, dann würden uns die Refactoring Tools nur mittelmäßig helfen. Aber es wäre trotzdem noch einfacher, sich umzuentscheiden.

Torsten Mandry:

Es wäre auf jeden Fall einfacher zu entscheiden, als wenn sie halt gar nicht helfen könnten. Wenn du versuchst, die Modularisierung zu manifestieren, dann tue ich das in der Regel dadurch, dass ich auch wieder Schichten reinziehe. Ich definiere explizite Interfaces und Datenobjekte. Du machst letztendlich das, was du in Services mit HTTP machst, nur halt ohne HTTP. Das Refactoring ist etwas schwerer, aber du kannst es tun. Wenn du das Schnittstellenobjekt anfasst und refaktorierst du die Producer- und Consumer-Seite gleichzeitig. Du musst keine Parallelisierungsansätze fahren. Du musst dir dann über andere Dinge Gedanken machen.

Sven Johann:

Man könnte sagen, wir haben einen Deployment-Monolithen, aber das willst du nicht. Ich weiß nicht, ob ein Technologiewechsel bei Microservices einfacher ist. Wenn wir Spring und Timeleaf nutzen, könnte ich mich umentscheiden, was die Technologie angeht, also das Webframework austauschen, habe ich noch nicht drüber nachgedacht.

Torsten Mandry:

Du müsstest es für alle Module gleichzeitig machen. Wenn du in einem Monolithen mehrere Module hast, die eine Web UI anbieten, wirst du wahrscheinlich nicht zwei verschiedene Webframeworks nutzen. Du willst nicht Timeleaf in Version A und Timeleaf in Version B parallel betreiben. Du bist gezwungen, den Monolithen insgesamt von Version A auf Version B anzuheben.

Das muss nicht zwingend schlecht sein. Ja, genau. Aber wenn du sagst, ich habe Teile, die ich nicht in Java schreiben will, sondern in Go, Javascript oder Python, weil es besser zum Use Case passt, ist das ein Architekturtreiber. Das deutet darauf hin, dass du den Teil in Go schreibst und einen separaten Service aufsetzt. Dann hast du einen Monolithen, aber auch einen Go-Service, einen Javascript-Service und noch einen Service. Du hast ein gemischtes Setup aus Monolithen, bei dem du versuchst, die Modularisierung mit viel Aufwand hinzukriegen, aber du hast sowieso schon einzelne Services. Dann kann man sich fragen, ob es nicht besser ist, komplett auf einzelne Services zu gehen und den Aufwand zu reduzieren. Es ist ein Abwägen. Es gibt kein Patentrezept. Der wichtigste Punkt ist, dass du dich bewusst entscheidest und nicht aus dem Bauch heraus. Ich wollte schon immer Microservices machen, deswegen mache ich das jetzt, oder weil Microservices in den letzten fünf Projekten super waren. Du solltest wissen, was du dir mit beiden Optionen einkaufst, also was die Kosten oder Nachteile sind und weswegen du es machst, was die Vorteile sind, die du nutzen willst. Das ist wahrscheinlich das einzige Grundrezept, was man nennen könnte, was aber wahrscheinlich für alles zutrifft. Ja, und was dummerweise schwierig ist.

Sven Johann:

Ja, es gibt Heuristiken, keine Patentrezepte. Ich meine, um auf Timeleaf zurückzukommen, das war vor 10 Jahren ein Begeisterungstreiber, weil ich darunter gelitten habe, dass wir eine Anwendung mit vielleicht einer Million Zeilen Code haben. Die Älteren erinnern sich, Struts ist doof, ja? Aber dieses Ding loszuwerden, ist bei einer Million Zeilen Code unmöglich. Ich war mal in einem Projekt, wo die DAO-Geschichte vermorst war. Es ist klar, wie es richtig sein soll, aber es zieht sich durch den Monolithen und du kriegst das nicht mehr so ohne weiteres gefixt. Da musst du alles anfassen. Microservices sind super, weil du mit zehn Spring Services und Timeleaf startest. Wenn du sagst, das ist Mist, hast du nicht das riesen Problem, überall ran zu müssen.

Torsten Mandry:

Du hast ein paar Services, wo du wenig machst, außer die Dependency-Versionen hochzuziehen. Da ist es vielleicht nicht so schlimm, wenn der immer noch mit dem alten Framework läuft. Du hast die Möglichkeit, das mit einem Service auszuprobieren und dann festzustellen, ob es Mist ist. Das sind alles Vorteile von entkoppelten Dingen. Ich bin mir nicht sicher, ob du dieselben Probleme hast, wenn du Java Platform Modules verwendest. Vielleicht gibt’s noch Ebenen der Entkopplung, die mir nicht einfallen, wo das wieder einigermaßen geht.

Sven Johann:

OSGI da war das so, dass Bundle A Logger Bibliothek 1.2 nutzen konnte und Bundle B 1.3. Bei Frontend-Technologien weiß ich nicht, ob sich Dispatcher in den Weg kommen. Wenn ich einen Modulithen habe und zufrieden bin, wie in unserem jetzigen Projekt, wo wir zwei Bounded Contexts haben, könnten wir die trennen, aber wir sehen keinen Grund. Wenn wir eine andere Frontend-Technologie wollen, könnten wir die beiden in Services splitten.

Torsten Mandry:

Das wäre ein Grund. Ich weiß nicht, ob das ausreicht, weil das Aufsplitten in Services Aufwand ist. Am Ende willst du möglichst wenig unterschiedliche Technologien haben. Die Frage ist, was geschickter ist: den Aufwand zu betreiben, das zu trennen und getrennt zu halten, oder im schlimmsten Fall wieder zusammenzuführen, oder in den sauren Apfel zu beißen und alles auf ein neues Framework umzustellen.

Sven Johann:

Ich habe noch eine posttraumatische Störung von früher, wenn wir über Anwendungen in Fachdomänen reden. Mein Leid kommt von 500.000 bis 1 Million Monolithen. Wir reden über Services, die vielleicht 50.000 bis 100.000 Zeilen haben oder noch weniger.

Torsten Mandry:

Ich kann dir nicht sagen, wie viele Codezeilen das aktuelle Projekt hat. Wir reden über ein Team, das eine bestimmte Maximalgröße von acht bis zehn Leuten hat. Ich habe eine maximale Größenordnung, die ich optimal als Team handhaben kann. Es ist die Ausnahme, dass ich so große Sprünge in so einem großen Umfang mache, dass ich das nicht in einem Schritt schaffe. Wir haben getrennte Services und zwei getrennte Frameworks im Frontend. Wenn wir die zusammen hätten und in einem Framework, wäre es machbar, beide Frontends auf ein anderes Framework umzustellen, wenn wir gute Gründe hätten oder eine neue Major-Version anheben. Wenn wir alles in einem Modulithen hätten, würden wir das nicht in getrennte Services trennen, nur um es nacheinander zu machen und am Ende wieder zusammenzuführen. Der Mehraufwand würde das übersteigen. Solange wir in einer Teamgröße von maximal zehn Leuten sind, sollte das für alle Teams ähnlich sein. Oder ich habe den Grund, das eine Frontend mit der Technologie und das andere mit der zu haben, aber dann komme ich nicht auf die Idee, die zusammenzuführen.

Sven Johann:

Wobei ich sagen muss, dieser eine Fall. Wir waren glaube ich auch zu zehnt, aber die Software hatte schon ein paar Jahre auf dem Buckel. Und dann kommt da schon ein bisschen was zusammen.

Torsten Mandry:

Das stimmt. Ich hatte den Fall noch nie in meiner Karriere, Gott sei Dank.

Sven Johann:

Wir kamen ans Limit, wir haben zu spät gemerkt, dass wir am Limit waren. Wir hatten nur einen Bauen Kontext, aber dann haben wir überlegt, es ist ein Klammbutsch. Dann hätten wir versucht, nach und nach zu modularisieren und dann Services rauszutrennen. Dann hätten wir dieses unsägliche Frontend Framework wegwerfen können. Ich bedanke mich. Hast du noch etwas, was wir diskutieren müssen und noch nicht diskutiert haben?

Torsten Mandry:

Ich habe keine Punkte mehr.

Sven Johann:

Ich hatte noch ein paar Punkte, aber das hat sich erledigt, weil wir über einzelne Vertikalen geredet haben.

Torsten Mandry:

Ich glaube, wenn du mit mehreren Teams gemeinsam einen Modulithen entwickelst, geht das vielleicht, aber da kenne ich mich nicht aus. Das ist ein anderes Thema, ein anderes Fass, wo ganz andere Probleme aufkommen.

Sven Johann:

Ich würde gerne was dazu sagen, kann es aber nicht. Bei dem Kunden, wo ich jetzt bin, hatten wir mehrere Teams, Monorepo, Angular und dann mehrere Teams, eine super Anwendung. Da haben drei Teams zusammengearbeitet, Monorepo und dann praktisch diesen Deployment Monolithen. Aber jetzt sind die Teams zusammengeführt.

Torsten Mandry:*

Die Teams sind zusammengeführt, weil es ansonsten nicht funktioniert hat mit diesem Modulithen oder hatte das irgendwie andere fachliche Domänengründe?

Sven Johann:

Das hat andere Gründe gehabt. Die drei Teams waren eher dem Delivery Druck und viel zu tun geschuldet. Jetzt ist die Software da und es gibt immer noch Features, aber es hat sich ausentwickelt. Da brauchst du nicht mehr drei Teams, da reicht ein kleines Team, fünf, sechs Leute, die das weiterentwickeln.

Sven Johann:

Gut, dann vielen Dank an alle, die so lange durchgehalten haben. Danke Torsten, schönes Wochenende und bis zum nächsten Mal. Tschüss.

Senior Consultant

Sven Johann is Senior Consultant at INNOQ and has been involved in the modernization of medium and large Java applications for many years. He is an active participant in various workshops of the Software Engineering Institute (Managing Technical Debt) and the Leibnitz Zentrum für Informatik (Dagstuhl Seminar “Managing Technical Debt”). He is also Program Chair of GOTO Amsterdam and Show Host of Software Engineering Radio.

Senior Consultant

Torsten is a software developer and consultant with 20+ years of expertise in Java and web development. He loves light-weight architectures, domain-driven design, clean code, and automated testing.