https://www.innoq.com/en/feed.atomINNOQ2024-02-16T00:00:00+01:00INNOQhttps://res.cloudinary.com/innoq/image/asset/innoq-logo--petrolapricot-75bc6db3b9347925b7424b68c583dd20.svgff4d67https://www.innoq.com/en/talks/2024/07/dwx-2024-evolutionist/2024-02-16T00:00:00+01:002024-02-16T10:36:40+01:00Markus Harrermarkus.harrer@innoq.comhttps://www.innoq.com/en/staff/markus-harrer/Benjamin Wolfbenjamin.wolf@innoq.comhttps://www.innoq.com/en/staff/benjamin-wolf/Vortrag: "Die Rolle „Evolutionist“: Softwarearchitekturarbeit im Bestand" by Markus Harrer, Benjamin Wolf — 2024-07-03 Developer Week ’24 (Nürnberg)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Ein großer Teil der Softwareentwicklung besteht aus Wartungsarbeit. In Ausbildung und Studium haben wir oft jedoch nur die Neuentwicklung kennengelernt. Überforderung droht, Frust baut sich auf und die Freude an der Softwareentwicklung geht verloren. Das muss nicht sein!</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Ein großer Teil der Softwareentwicklung besteht aus Wartungsarbeit. In Ausbildung und Studium haben wir oft jedoch nur die Neuentwicklung kennengelernt. Überforderung droht, Frust baut sich auf und die Freude an der Softwareentwicklung geht verloren. Das muss nicht sein!</p>
<p>Wir stellen die Rolle “Evolutionist” vor, welche sich auf die qualitativ angemessene Weiterentwicklung bestehender Systeme fokussiert. Wir blicken auf das notwendige Skill- und Mindset sowie erste Praktiken, um mit großen und langlebenden Softwaresystemen zurecht zu kommen.<figure><img alt="Titelbild des Vortrags" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/g1nsuad744f2bh6h1g62rycfxnzy 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/g1nsuad744f2bh6h1g62rycfxnzy 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/g1nsuad744f2bh6h1g62rycfxnzy 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/g1nsuad744f2bh6h1g62rycfxnzy 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/g1nsuad744f2bh6h1g62rycfxnzy 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/g1nsuad744f2bh6h1g62rycfxnzy 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/g1nsuad744f2bh6h1g62rycfxnzy 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/g1nsuad744f2bh6h1g62rycfxnzy 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/g1nsuad744f2bh6h1g62rycfxnzy 2800w" sizes="(min-width: 1400px) 1024px, 90vw"></figure></p>
</body></html>
https://www.innoq.com/en/talks/2024/06/data-contracts-eine-sozio-technische-beziehung/2024-03-14T00:00:00+01:002024-03-14T10:07:48+01:00Jochen Christjochen.christ@innoq.comhttps://www.innoq.com/en/staff/jochen-christ/Vortrag: "Data Contracts: Eine sozio-technische Beziehung" by Jochen Christ — 2024-06-19 CloudLand (Brühl)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>In modernen verteilten Datenarchitekturen, wie z. B. Data Mesh, werden Daten zunehmend zwischen verschiedenen Teams ausgetauscht. Wir brauchen eine Möglichkeit, uns auf die Qualität und Stabilität der von uns verwendeten Daten zu verlassen.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>In modernen verteilten Datenarchitekturen, wie z. B. Data Mesh, werden Daten zunehmend zwischen verschiedenen Teams ausgetauscht. Wir brauchen eine Möglichkeit, uns auf die Qualität und Stabilität der von uns verwendeten Daten zu verlassen.</p>
<p>Data Contracts sind so ähnlich wie OpenAPI- oder AsyncAPI-Spezifikationen, aber die Datenwelt funktioniert etwas anders. Ein Data Contract definiert das Schema der bereitgestellten Daten und deren Qualitätsattribute in einem YAML-Format. Datenverträge können auch Beispieldaten und eine semantische Beschreibung enthalten. Data Contracts legen zudem die Nutzungsbedingungen für die Verwendung von Daten fest.</p>
<p>Data Contracts sind in erster Linie auch ein Kommunikationsinstrument, um ein gemeinsames Verständnis darüber auszudrücken, wie Daten strukturiert und interpretiert werden sollten. Sie machen implizite semantische und qualitative Erwartungen explizit. Später in der Entwicklung und Produktion dienen sie auch als Grundlage für die Codegenerierung, das Testen, die Schemavalidierung, die Qualitätskontrolle, die Überwachung, die Zugriffskontrolle und die Richtlinien für die Verwaltung der Datenverarbeitung.</p>
<p>In diesem Vortrag möchte ich die Data Contract Specification <a href="http://www.datacontract.com">datacontract.com</a> und das Data Contract CLI zur Validierung von Data Contracts in CI/CD-Pipelines vorstellen.</p>
</body></html>
https://www.innoq.com/en/talks/2024/05/craftconf-2024-software-analytics-with-data-science-on-software-data/2024-01-12T00:00:00+01:002024-01-12T15:45:01+01:00Markus Harrermarkus.harrer@innoq.comhttps://www.innoq.com/en/staff/markus-harrer/Talk: "Software Analytics with Data Science on Software Data" by Markus Harrer — 2024-05-30 Craft Conference 2024 (Budapest)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Data Science has demonstrated its value in extracting insights from business data, raising the question: Why not apply these principles to our software systems’ data? In this talk, I’ll introduce you to the world of Software Analytics. We’ll explore how to extract valuable insights from software data by using tools and techniques from data science to get rid of big, systemic problems in our software systems.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Data Science has demonstrated its value in extracting insights from business data, raising the question: Why not apply these principles to our software systems’ data? In this talk, I’ll introduce you to the world of Software Analytics. We’ll explore how to extract valuable insights from software data by using tools and techniques from data science to get rid of big, systemic problems in our software systems.</p>
<p>You’ll learn how to leverage scientific thinking, manage the analysis process, and apply literate statistical programming to analyze software systems in an understandable way. Or to put it in the words of software developers: We automate the analysis of large software systems using open-source tools like Jupyter Notebook, Python, pandas, jQAssistant, and Neo4j. I’ll also demonstrate through live-coding how we can gain new insights from data sources like Git repositories, performance measurements, or source code.</p>
<p>Join me in this session to acquire your starter kit for uncovering deeply hidden issues and change the way of improving systems with data-driven software analysis!</p>
</body></html>
https://www.innoq.com/en/talks/2024/04/hoeher-schneller-weiter-neue-qualitaeten-braucht-das-land-isaqb/2024-03-18T00:00:00+01:002024-03-18T09:37:43+01:00Dr. Gernot Starkegernot.starke@innoq.comhttps://www.innoq.com/en/staff/gernot-starke/Vortrag: "Höher, schneller, weiter: Neue Qualitäten braucht das Land" by Dr. Gernot Starke — 2024-04-16 iSAQB Software Architektur Meetup München (München)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Software soll hochperformant, robust, elastisch, skalierbar und sicher sein, oder was immer Eure Stakeholder so unter Qualität verstehen.
Da genau beginnt das Problem: Stakeholder können oft nicht erklären, was sie an Qualität genau brauchen.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Software soll hochperformant, robust, elastisch, skalierbar und sicher sein, oder was immer Eure Stakeholder so unter Qualität verstehen.
Da genau beginnt das Problem: Stakeholder können oft nicht erklären, was sie an Qualität genau brauchen.</p>
<p>Bisherige Ansätze (z.B. ISO-Standards) helfen dem normalen Entwicklungsprojekt wie ein Grashalm gegen den Hunger: Theoretisch schon, praktisch kaum.</p>
<p>Ich zeige im Vortrag einen pragmatischen und einfachen Weg, der zielsicher zu spezifischen, konkreten und überprüfbaren Qualitätsanforderungen führt.</p>
<p>Ganz nebenbei finden Entwicklungsteams und anderen Stakeholder damit zu einer gemeinsamen Sprache rund um Qualität - und letztlich zu besseren Systemen.</p>
</body></html>
https://www.innoq.com/en/talks/2024/04/remote-mob-programming-die-besondere-art-des-teamworks-dev-day-2024/2024-03-07T00:00:00+01:002024-03-07T13:28:24+01:00Joshua Töpferjoshua.toepfer@innoq.comhttps://www.innoq.com/en/staff/joshua-toepfer/Vortrag: "Remote Mob Programming - Die besondere Art des Teamworks" by Joshua Töpfer — 2024-04-16 Dev Day 2024 (Dresden)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Das ganze Team sitzt in einem Online-Meeting und entwickelt gemeinsam. Einer tippt den Code, die anderen diskutieren. Klingt ungewöhnlich? Das ist Remote Mob Programming, eine spannende Arbeitsweise für verteilte Teams. Seit über 3 Jahren arbeitet Joshua Töpfer Vollzeit in einem Remote Mob und möchte nicht mehr anders arbeiten. Was es damit auf sich hat, was die Vor- und Nachteile dieser Methodik sind und wie ihr herausfinden könnt, ob diese Methodik etwas für euer Team ist, erfahrt ihr in diesem Vortrag.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Das ganze Team sitzt in einem Online-Meeting und entwickelt gemeinsam. Einer tippt den Code, die anderen diskutieren. Klingt ungewöhnlich? Das ist Remote Mob Programming, eine spannende Arbeitsweise für verteilte Teams. Seit über 3 Jahren arbeitet Joshua Töpfer Vollzeit in einem Remote Mob und möchte nicht mehr anders arbeiten. Was es damit auf sich hat, was die Vor- und Nachteile dieser Methodik sind und wie ihr herausfinden könnt, ob diese Methodik etwas für euer Team ist, erfahrt ihr in diesem Vortrag.</p>
</body></html>
https://www.innoq.com/en/talks/2024/04/data-mesh-was-ist-ein-datenprodukt-javaland-2024/2023-12-14T00:00:00+01:002023-12-14T11:05:08+01:00Jochen Christjochen.christ@innoq.comhttps://www.innoq.com/en/staff/jochen-christ/Vortrag: "Data Mesh: Was ist ein Datenprodukt?" by Jochen Christ — 2024-04-10 JavaLand 2024 (Nürburg)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Moderne Datenarchitekturen verwenden das Konzept eines Datenprodukts, um die Bereitstellung und Nutzung von Daten besser zu organisieren.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Moderne Datenarchitekturen verwenden das Konzept eines Datenprodukts, um die Bereitstellung und Nutzung von Daten besser zu organisieren.</p>
<p>Ein Datenprodukt bildet eine logische Einheit um fachliche Domänendaten und umfasst alle Komponenten, um diese Daten zu speichern, aufzubereiten, zu aggregieren, zu erklären und für die Nutzer so einfach wie möglich zugänglich zu machen. Dabei werden die Schnittstellen nach außen über Datenkontrakte spezifiziert, interne Implementierungsdetails gemäß dem Prinzip “Information Hiding” aber verborgen. Die Verantwortung für die Entwicklung und den Betrieb eines Datenprodukts ist genau einem Team zugeordnet. Mit dieser Art der Modularisierung wird die Datenarchitektur übersichtlicher und besser skalierbar.</p>
<p>Jochen Christ, Autor von datamesh-architecture.com, zeigt, wie ein Datenprodukt in der AWS-Cloud implementiert werden kann, welche Funktionen eine gute Datenplattform zur Verfügung stellen sollte, und wie Datenprodukte die Data Governance vereinfachen.</p>
<h4><a id="lernziele" href="https://www.innoq.com/en/talks/2024/04/data-mesh-was-ist-ein-datenprodukt-javaland-2024/#lernziele">Lernziele</a></h4>
<ul>
<li>Was ist ein Datenprodukt</li>
<li>Komponenten eines Datenprodukts</li>
<li>Governance eines Datenprodukts</li>
</ul>
<h4><a id="vorkenntnisse" href="https://www.innoq.com/en/talks/2024/04/data-mesh-was-ist-ein-datenprodukt-javaland-2024/#vorkenntnisse">Vorkenntnisse</a></h4>
<ul>
<li>SQL-Kenntnisse sind hilfreich, aber nicht notwendig</li>
<li>AWS-Cloud-Kenntnisse sind hilfreich, aber nicht notwendig</li>
<li>Terraform-Kenntnisse sind hilfreich, aber nicht notwendig</li>
</ul>
</body></html>
https://www.innoq.com/en/talks/2024/04/data-mesh-manager/2024-03-14T00:00:00+01:002024-03-14T09:56:47+01:00Dr. Simon Harrersimon.harrer@innoq.comhttps://www.innoq.com/en/staff/dr-simon-harrer/Vortrag: "Data Mesh Manager" by Dr. Simon Harrer — 2024-04-10 INNOQ Technology Lunch 04/24 (Online)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Der Data Mesh Manager ist ein Produkt, das aus Data Mesh Beratungsprojekten bei INNOQ entstanden ist. Der Data Mesh Manager ermöglicht es, Data Mesh Initiativen auf die Überholspur zusetzen. Denn in dem Produkt ist der ganze Erfahrungsschatz unserer Beraterinnen und Berater hineingeflossen - und fließt dort auch weiterhin hinein. In diesem Technology Lunch möchten wir die Entstehungsgeschichte des SaaS-Produkts einmal kurz skizzieren, und natürlich ein wenig das Produkt aus verschiedenen Blickwinkeln zeigen. </p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Der Data Mesh Manager ist ein Produkt, das aus Data Mesh Beratungsprojekten bei INNOQ entstanden ist. Der Data Mesh Manager ermöglicht es, Data Mesh Initiativen auf die Überholspur zusetzen. Denn in dem Produkt ist der ganze Erfahrungsschatz unserer Beraterinnen und Berater hineingeflossen - und fließt dort auch weiterhin hinein. In diesem Technology Lunch möchten wir die Entstehungsgeschichte des SaaS-Produkts einmal kurz skizzieren, und natürlich ein wenig das Produkt aus verschiedenen Blickwinkeln zeigen. </p>
</body></html>
https://www.innoq.com/en/talks/2024/04/das-browser-website-sicherheitsmodell/2024-01-26T00:00:00+01:002024-02-06T14:27:09+01:00Christoph Iserlohnchristoph.iserlohn@innoq.comhttps://www.innoq.com/en/staff/ci/Vortrag: "Das Browser-Website-Sicherheitsmodell" by Christoph Iserlohn — 2024-04-10 JavaLand 2024 (Nürburg)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Der Browser hat sich in der letzten Dekade zur dominierenden Applikationsplattform entwickelt. Leider besteht das unterliegende Applikationsmodell daraus, Code aus potenziell nicht vertrauenswürdigen Quellen auszuführen. Damit daraus keine Gefahr von einer Website für eine andere Website erwächst - und für den Browser selber - haben die Browserhersteller in Laufe der Zeit eine ganze Reihe von Sicherheitsmaßnahmen entwickelt: Content-Security-/Cross-Origin Resource-/Cross-Origin-Embedder-/Same-Origin-Policy, Cross-Origin Resource Sharing, diverse HTTP-Header, Cookie-Attribute u. a. Hierbei den Überblick zu behalten ist schwer. Dieser Vortrag hilft dabei</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Der Browser hat sich in der letzten Dekade zur dominierenden Applikationsplattform entwickelt. Leider besteht das unterliegende Applikationsmodell daraus, Code aus potenziell nicht vertrauenswürdigen Quellen auszuführen. Damit daraus keine Gefahr von einer Website für eine andere Website erwächst - und für den Browser selber - haben die Browserhersteller in Laufe der Zeit eine ganze Reihe von Sicherheitsmaßnahmen entwickelt: Content-Security-/Cross-Origin Resource-/Cross-Origin-Embedder-/Same-Origin-Policy, Cross-Origin Resource Sharing, diverse HTTP-Header, Cookie-Attribute u. a. Hierbei den Überblick zu behalten ist schwer. Dieser Vortrag hilft dabei</p>
<h4><a id="lernziele" href="https://www.innoq.com/en/talks/2024/04/das-browser-website-sicherheitsmodell/#lernziele">Lernziele</a></h4>
<p>Die Teilnehmer:innen sollen das grundlegende Sicherheitskonzept, das Browser für Webseiten implementieren, kennenlernen, verstehen, welche Maßnahmen vor welchen Bedrohungen schützen, wie diese zusammenspielen, aus welchen Kontexten heraus diese entstanden sind, welche aktuell angewendet werden sollten, welche veraltet sind, und wie Sicherheitslücken wie Spectre-/Meltdown das Browser-Sicherheitsmodell verändert haben.</p>
<h4><a id="vorkenntnisse" href="https://www.innoq.com/en/talks/2024/04/das-browser-website-sicherheitsmodell/#vorkenntnisse">Vorkenntnisse</a></h4>
<p>Grundlegende Kenntnisse in der Webentwicklung</p>
</body></html>
https://www.innoq.com/en/talks/2024/04/du-machst-das-falsch-und-das-ist-gut-so/2023-12-14T00:00:00+01:002023-12-14T12:16:19+01:00Benjamin Wolfbenjamin.wolf@innoq.comhttps://www.innoq.com/en/staff/benjamin-wolf/Melanie Schäfermelanie.schaefer@innoq.comhttps://www.innoq.com/en/staff/melanie-schaefer/Vortrag: "Du machst das falsch - und das ist gut so!" by Benjamin Wolf, Melanie Schäfer — 2024-04-10 JavaLand 2024 (Nürburg)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Fehler zu machen ist unvermeidlich und essentiell für Euer berufliches und persönliches Wachstum.
Die Fähigkeit, Fehler als Lernchancen zu begreifen, fördert nicht nur die Qualitätsverbesserung Eurer Software und Eurer Systeme, sondern schafft auch ein Umfeld, in dem Innovation gedeihen kann.
Wir betrachten in diesem Vortrag, wie Ihr aus Fehlern lernt, sie kommuniziert und konstruktiv damit umgeht. Wir zeigen euch an einigen Beispielen, welche psychologischen Modelle hinter einer Fehlerkultur stecken und wie wir sie in unsere tägliche Arbeit transferieren können.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Fehler zu machen ist unvermeidlich und essentiell für Euer berufliches und persönliches Wachstum.
Die Fähigkeit, Fehler als Lernchancen zu begreifen, fördert nicht nur die Qualitätsverbesserung Eurer Software und Eurer Systeme, sondern schafft auch ein Umfeld, in dem Innovation gedeihen kann.
Wir betrachten in diesem Vortrag, wie Ihr aus Fehlern lernt, sie kommuniziert und konstruktiv damit umgeht. Wir zeigen euch an einigen Beispielen, welche psychologischen Modelle hinter einer Fehlerkultur stecken und wie wir sie in unsere tägliche Arbeit transferieren können.</p>
<h4><a id="lernziele" href="https://www.innoq.com/en/talks/2024/04/du-machst-das-falsch-und-das-ist-gut-so/#lernziele">Lernziele</a></h4>
<p>Die Teilnehmer:innen sollen verstehen,</p>
<ul>
<li>welchen positiven Effekt das Machen von Fehlern auf das Lernen und die persönliche Entwicklung hat.</li>
<li>welchen Unterschied verschiedene Kommunikationsstile beim Empfänger ausmachen.</li>
<li>wie wichtig es ist, Fehler im Team offen und konstruktiv anzusprechen.</li>
</ul>
<h4><a id="vorkenntnisse" href="https://www.innoq.com/en/talks/2024/04/du-machst-das-falsch-und-das-ist-gut-so/#vorkenntnisse">Vorkenntnisse</a></h4>
<p>Du hast schon einmal Fehler gemacht.</p>
</body></html>
https://www.innoq.com/en/talks/2024/04/mit-buildpacks-effiziente-container-images-erzeugen/2023-12-14T00:00:00+01:002023-12-14T11:03:45+01:00Michael Vitzmichael.vitz@innoq.comhttps://www.innoq.com/en/staff/michael-vitz/Vortrag: "Mit Buildpacks effiziente Container-Images erzeugen" by Michael Vitz — 2024-04-10 JavaLand 2024 (Nürburg)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Beim Wort Buildpacks denken viele zuerst an Heroku. Doch Buildpacks können mittlerweile auch unabhängig von einer konkreten Plattform verwendet werden, um effiziente Container-Images zu erzeugen. In diesem Talk schauen wir uns deswegen im Detail an, wie Buildpacks funktionieren. Hierzu wird neben Theorie auch ein eigenes Buildpack, live, erstellt. Dabei wird auch klar, welche Vorteile sich durch den Einsatz ergeben. Letztlich schauen wir uns dann auch noch an, wie Buildpacks in Java basierten Projekten konkret eingesetzt werden können.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Beim Wort Buildpacks denken viele zuerst an Heroku. Doch Buildpacks können mittlerweile auch unabhängig von einer konkreten Plattform verwendet werden, um effiziente Container-Images zu erzeugen. In diesem Talk schauen wir uns deswegen im Detail an, wie Buildpacks funktionieren. Hierzu wird neben Theorie auch ein eigenes Buildpack, live, erstellt. Dabei wird auch klar, welche Vorteile sich durch den Einsatz ergeben. Letztlich schauen wir uns dann auch noch an, wie Buildpacks in Java basierten Projekten konkret eingesetzt werden können.</p>
<ul>
<li>
<strong>Lernziele:</strong> Die Teilnehmenden lernen mit Buildpacks eine Möglichkeit zur Erzeugung von effizienten Container-Images kennen. Dabei wird die Theorie hinter diesen vermittelt und durch die Erzeugung eines eigenen Buildpacks gefestigt. Außerdem wird zum Schluss auch noch gezeigt, wie Buildpacks in der Praxis in konkreten Java basierten Projekten eingesetzt werden kann.</li>
<li>
<strong>Vorkenntnisse:</strong> Ein grundlegendes Verständnis von Containern und Container-Images sollte vorhanden sein.</li>
</ul>
</body></html>
https://www.innoq.com/en/talks/2024/04/web-assembly-die-neue-jvm/2023-12-14T00:00:00+01:002024-02-07T09:11:09+01:00Christoph Iserlohnchristoph.iserlohn@innoq.comhttps://www.innoq.com/en/staff/ci/Vortrag: "WebAssembly - die neue JVM?" by Christoph Iserlohn — 2024-04-09 JavaLand 2024 (Nürburg)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>WebAssembly, oft abgekürzt als WASM, ist ein binäres Ausführungsformat für Webanwendungen. Es handelt sich um eine plattformübergreifende Technologie, die in modernen Webbrowsern nativ unterstützt wird. In letzter Zeit findet WebAssembly als Laufzeitumgebung auch außerhalb des Browsers immer mehr Verbreitung, z. B. als Alternative zu Containern. Sowohl strukturell als auch von den Zielen ähnelt WebAssembly dabei Java und der JVM.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>WebAssembly, oft abgekürzt als WASM, ist ein binäres Ausführungsformat für Webanwendungen. Es handelt sich um eine plattformübergreifende Technologie, die in modernen Webbrowsern nativ unterstützt wird. In letzter Zeit findet WebAssembly als Laufzeitumgebung auch außerhalb des Browsers immer mehr Verbreitung, z. B. als Alternative zu Containern. Sowohl strukturell als auch von den Zielen ähnelt WebAssembly dabei Java und der JVM.</p>
<p>In diesem Vortrag erfahren Sie mehr über die aufregende Welt von WebAssembly und können herauszufinden, wie Java-Entwickler:innen von dieser innovativen Technologie profitieren können.</p>
<ul>
<li>Lernziele: Nach dem Vortag sollten die Teilnehmer:innen einen Überblick über WebAssembly haben und die grundlegenden Vor- und Nachteile sowie die Unterschiede und Gemeinsamkeiten zu Java und der JVM kennen.</li>
<li>Vorkenntnisse: Programmierkenntnisse in einer (oder mehreren) Mainstream-Programmiersprachen.</li>
</ul>
</body></html>
https://www.innoq.com/en/talks/2024/04/diaet-fuer-eure-architekturdokumentation-unser-ernaehrungsplan-javaland-2024/2023-12-14T00:00:00+01:002023-12-18T09:24:01+01:00Benjamin Wolfbenjamin.wolf@innoq.comhttps://www.innoq.com/en/staff/benjamin-wolf/Vortrag: "Diät für eure Architekturdokumentation – Unser Ernährungsplan" by Benjamin Wolf — 2024-04-09 JavaLand 2024 (Nürburg)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>In den meisten Projekten treffen wir auf folgende Arten von Architekturdokumentation: Entweder gibt es gar keine Dokumentation, oder es existiert veraltete Dokumentation in gigantischem Umfang. Unabhängig davon ist der Effekt jedoch der gleiche – es wird nichts Neues mehr dokumentiert. Begründet wird dies meist mit “Das liest doch niemand”, “Dafür haben wir keine Zeit” oder “Das findet ja niemand mehr”. Kommt Euch bekannt vor? Dann ist dieser Vortrag für Euch genau das Richtige.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>In den meisten Projekten treffen wir auf folgende Arten von Architekturdokumentation: Entweder gibt es gar keine Dokumentation, oder es existiert veraltete Dokumentation in gigantischem Umfang. Unabhängig davon ist der Effekt jedoch der gleiche – es wird nichts Neues mehr dokumentiert. Begründet wird dies meist mit “Das liest doch niemand”, “Dafür haben wir keine Zeit” oder “Das findet ja niemand mehr”. Kommt Euch bekannt vor? Dann ist dieser Vortrag für Euch genau das Richtige.</p>
<p>Benjamin Wolf stellt Euch den Architecture Communication Canvas (ACC) vor, mit welchem Ihr in kurzer Zeit die wichtigsten Aspekte Eurer Architektur dokumentieren und kommunizieren könnt. Der Canvas ist kompatibel zum arc42-Template, jedoch wesentlich kompakter. Mit Beispielen aus der Praxis sowie praktischen Tipps wird gezeigt, dass Dokumentation zugleich sparsam erstellt werden und dabei nützlich für Stakeholder sein kann – und das Ganze auch noch Spaß macht!</p>
<h4><a id="lernziele" href="https://www.innoq.com/en/talks/2024/04/diaet-fuer-eure-architekturdokumentation-unser-ernaehrungsplan-javaland-2024/#lernziele">Lernziele</a></h4>
<ul>
<li>Die Teilnehmenden sollen verstehen, warum Architekturdokumentation wichtig ist</li>
<li>Die Teilnehmenden werden ein Werkzeug kennenlernen, um in kurzer Zeit ein hilfreiches Architekturdokument erstellen zu können</li>
<li>Die Teilnehmenden werden sehen, dass Architekturdokumentation auch Spaß machen kann</li>
</ul>
<h4><a id="vorkenntnisse" href="https://www.innoq.com/en/talks/2024/04/diaet-fuer-eure-architekturdokumentation-unser-ernaehrungsplan-javaland-2024/#vorkenntnisse">Vorkenntnisse</a></h4>
<p>Grundkenntnisse über Architekturdokumentation sind hilfreich, aber nicht erforderlich. </p>
</body></html>
https://www.innoq.com/en/talks/2024/03/bob-2024-software-analytics-with-data-science-on-software-data/2024-01-12T00:00:00+01:002024-01-12T15:40:42+01:00Markus Harrermarkus.harrer@innoq.comhttps://www.innoq.com/en/staff/markus-harrer/Talk: "Software Analytics with Data Science on Software Data" by Markus Harrer — 2024-03-15 BOB2024 Konferenz (Berlin)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Data Science has demonstrated its value in extracting insights from business data, raising the question: Why not apply these principles to our software systems’ data? In this talk, I’ll introduce you to the world of Software Analytics. We’ll explore how to extract valuable insights from software data by using tools and techniques from data science to get rid of big, systemic problems in our software systems.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Data Science has demonstrated its value in extracting insights from business data, raising the question: Why not apply these principles to our software systems’ data? In this talk, I’ll introduce you to the world of Software Analytics. We’ll explore how to extract valuable insights from software data by using tools and techniques from data science to get rid of big, systemic problems in our software systems.</p>
<p>You’ll learn how to leverage scientific thinking, manage the analysis process, and apply literate statistical programming to analyze software systems in an understandable way. Or to put it in the words of software developers: We automate the analysis of large software systems using open-source tools like Jupyter Notebook, Python, pandas, jQAssistant, and Neo4j. I’ll also demonstrate through live-coding how we can gain new insights from data sources like Git repositories, performance measurements, or source code.</p>
<p>Join me in this session to acquire your starter kit for uncovering deeply hidden issues and change the way of improving systems with data-driven software analysis!</p>
</body></html>
https://www.innoq.com/en/podcast/005-legacy-modernisierung-5/2024-03-14T00:00:00+01:002024-03-14T18:10:44+01:00Johannes Seitzjohannes.seitz@innoq.comhttps://www.innoq.com/en/staff/johannes-seitz/Podcast #5: Legacy-Modernisierung: Zwischen Innovation und Sicherheit. Zu Gast: Steffen Bergmann, Leiter Softwareentwicklung und -architektur, Meierhofer AG<span class="translation_missing" title="translation missing: en.feed_contents.items.podcast_episode.cto_need_to_know.title, episode_number: 5, episode_title: Legacy-Modernisierung: Zwischen Innovation und Sicherheit, episode_subtitle: Zu Gast: Steffen Bergmann, Leiter Softwareentwicklung und -architektur, Meierhofer AG">Title</span><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Wie können IT-Systeme in Kliniken und Gesundheitseinrichtungen modernisiert werden, um Prozesseffizienz und Patientenversorgung zu verbessern? Johannes Seitz und Steffen Bergmann (Leiter Softwareentwicklung und -architektur, Meierhofer AG) sprechen in dieser Folge über die Modernisierung von Bestandssystemen im Gesundheitswesen. Dabei geht es um die Balance zwischen technischer Innovation und der Sicherstellung von Zuverlässigkeit und Sicherheit. Wie geht Meierhofer mit diesen Herausforderungen um und welche Rolle spielen Kundenorientierung und strategische Planung?</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Wie können IT-Systeme in Kliniken und Gesundheitseinrichtungen modernisiert werden, um Prozesseffizienz und Patientenversorgung zu verbessern? Johannes Seitz und Steffen Bergmann (Leiter Softwareentwicklung und -architektur, Meierhofer AG) sprechen in dieser Folge über die Modernisierung von Bestandssystemen im Gesundheitswesen. Dabei geht es um die Balance zwischen technischer Innovation und der Sicherstellung von Zuverlässigkeit und Sicherheit. Wie geht Meierhofer mit diesen Herausforderungen um und welche Rolle spielen Kundenorientierung und strategische Planung?</p></body></html>
https://www.innoq.com/en/podcast/146-architekturqualitaet/2024-03-13T00:00:00+01:002024-03-13T14:37:14+01:00Sven Johannsven.johann@innoq.comhttps://www.innoq.com/en/staff/sven-johann/Dr. Gernot Starkegernot.starke@innoq.comhttps://www.innoq.com/en/staff/gernot-starke/Podcast #146: Architekturqualität. Von Stakeholder-Bedürfnissen zu konkreten LösungenPodcast #146: Architekturqualität. Von Stakeholder-Bedürfnissen zu konkreten Lösungen<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Am Beginn dieser Folge zu Architekturqualität steht eine zentrale Frage: Was bedeutet eigentlich Qualität? Im Gespräch erörtern Sven Johann und Gernot Starke, wie durch direkte Dialoge mit Stakeholdern Anforderungen nicht nur identifiziert, sondern auch effektiv für die Entwicklungsarbeit nutzbar gemacht werden können. Mit einer Vielzahl an praxisnahen Beispielen illustrieren sie, wie entscheidend es ist, Qualitätsanforderungen flexibel an den jeweiligen Projektkontext anzupassen.
</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Am Beginn dieser Folge zu Architekturqualität steht eine zentrale Frage: Was bedeutet eigentlich Qualität? Im Gespräch erörtern Sven Johann und Gernot Starke, wie durch direkte Dialoge mit Stakeholdern Anforderungen nicht nur identifiziert, sondern auch effektiv für die Entwicklungsarbeit nutzbar gemacht werden können. Mit einer Vielzahl an praxisnahen Beispielen illustrieren sie, wie entscheidend es ist, Qualitätsanforderungen flexibel an den jeweiligen Projektkontext anzupassen.
</p></body></html>
https://www.innoq.com/en/talks/2024/03/durch-bessere-requirements-zu-besseren-architekturen-2024/2024-01-05T00:00:00+01:002024-01-05T10:44:08+01:00Dr. Gernot Starkegernot.starke@innoq.comhttps://www.innoq.com/en/staff/gernot-starke/Daniel Lauxtermanndaniel.lauxtermann@innoq.comhttps://www.innoq.com/en/staff/daniel-lauxtermann/Vortrag: "Durch bessere Requirements zu besseren Architekturen" by Dr. Gernot Starke, Daniel Lauxtermann — 2024-03-13 Software Architecture Summit 2024 (München)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>In Bezug auf Anforderungen werden Entwicklungsteams manchmal von ihren Product-Ownern, Business-Analysten oder Fachbereichen schlichtweg „im Stich gelassen“.
Für solche Fälle hilft Ihnen unser Workshop dabei, die Anforderungen an Ihre Systeme effektiv zu verbessern.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>In Bezug auf Anforderungen werden Entwicklungsteams manchmal von ihren Product-Ownern, Business-Analysten oder Fachbereichen schlichtweg „im Stich gelassen“.
Für solche Fälle hilft Ihnen unser Workshop dabei, die Anforderungen an Ihre Systeme effektiv zu verbessern.</p>
<p>Wir fokussieren dabei auf architekturrelevanten Themen, beispielsweise:</p>
<ul>
<li>Scoping und Klärung externer Schnittstellen,</li>
<li>verschiedenen Granularitäten funktionaler Anforderungen und</li>
<li>Qualitätsanforderungen</li>
</ul>
<p>Damit helfen wir Ihnen, das Fundament für erfolgreiche Systementwicklung zu verbessern, basierend auf den konkreten Anforderungen Ihrer Stakeholder.</p>
<p>Wir wechseln im Workshop konzeptionelle Grundlagen mit praktischen Hands-On Teilen ab.</p>
</body></html>
https://www.innoq.com/en/talks/2024/03/bessere-architekturentscheidungen-tiger-kommt-weglaufen/2023-11-20T00:00:00+01:002024-03-13T09:33:29+01:00Dr. Gernot Starkegernot.starke@innoq.comhttps://www.innoq.com/en/staff/gernot-starke/Vortrag: "Bessere Architekturentscheidungen: Tiger kommt – weglaufen?" by Dr. Gernot Starke — 2024-03-13 Software Architecture Summit 2024 (München)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>In dieser Keynote schaut Gernot Starke auf einige dieser komischen Phänomene des Gehirns und betrachten Zusammenhänge mit typischen Situationen in der Softwareentwicklung. Die Erforschung mancher dieser Gehirneigenschaften hat einigen Leuten Nobelpreise eingebracht – und bieten uns in der Softwareentwicklung gute Ansätze, unsere eigene Arbeitsweise und unsere Architekturentscheidungen systematisch zu verbessern.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>In dieser Keynote schaut Gernot Starke auf einige dieser komischen Phänomene des Gehirns und betrachten Zusammenhänge mit typischen Situationen in der Softwareentwicklung. Die Erforschung mancher dieser Gehirneigenschaften hat einigen Leuten Nobelpreise eingebracht – und bieten uns in der Softwareentwicklung gute Ansätze, unsere eigene Arbeitsweise und unsere Architekturentscheidungen systematisch zu verbessern.</p>
</body></html>
https://www.innoq.com/en/talks/2024/03/architekturarbeit-im-mob/2023-11-20T00:00:00+01:002023-12-01T14:14:06+01:00Joshua Töpferjoshua.toepfer@innoq.comhttps://www.innoq.com/en/staff/joshua-toepfer/Vortrag: "Architekturarbeit im Mob" by Joshua Töpfer, Nikolas Hermann — 2024-03-13 Software Architecture Summit 2024 (München)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Die Arbeit von Softwarearchitekten findet in klassisch aufgestellten Teams oft abseits der Entwicklungsarbeit statt - sowohl räumlich als auch zeitlich getrennt. Dass das auch anders geht, zeigen Niko und Joshua in ihrem Workshop. Sie arbeiten seit mehr als 3 Jahren in einem Remote Mob, in dem das ganze Team bei Bedarf die Rolle des Architekten einnimmt. So wird die Arbeit an Domänen- und Lösungsarchitektur genauso vom ganzen Team gemeinsam durchgeführt, wie die Entwicklung und der Betrieb der Lösungen. Wir möchten euch vorstellen, wie ein Mob funktioniert und welche Veränderungen die synchrone Zusammenarbeit bei Architektur-Methoden mit sich bringt und gemeinsam mit euch die Architekturarbeit im Mob einmal ausprobieren.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Die Arbeit von Softwarearchitekten findet in klassisch aufgestellten Teams oft abseits der Entwicklungsarbeit statt - sowohl räumlich als auch zeitlich getrennt. Dass das auch anders geht, zeigen Niko und Joshua in ihrem Workshop. Sie arbeiten seit mehr als 3 Jahren in einem Remote Mob, in dem das ganze Team bei Bedarf die Rolle des Architekten einnimmt. So wird die Arbeit an Domänen- und Lösungsarchitektur genauso vom ganzen Team gemeinsam durchgeführt, wie die Entwicklung und der Betrieb der Lösungen. Wir möchten euch vorstellen, wie ein Mob funktioniert und welche Veränderungen die synchrone Zusammenarbeit bei Architektur-Methoden mit sich bringt und gemeinsam mit euch die Architekturarbeit im Mob einmal ausprobieren.</p>
<p>Der Workshop ist für Einsteiger und Fortgeschrittene geeignet, die verwendeten Methoden werden vorab erklärt.</p>
</body></html>
https://www.innoq.com/en/talks/2024/03/business-fuer-software-architekten-2024/2023-11-20T00:00:00+01:002023-11-20T09:10:04+01:00Martina Freersmartina.freers@innoq.comhttps://www.innoq.com/en/staff/martina-freers/Jörg Müllerjoerg.mueller@innoq.comhttps://www.innoq.com/en/staff/joerg-mueller/Vortrag: "Business für Software Architekt:innen" by Martina Freers, Jörg Müller — 2024-03-12 Software Architecture Summit 2024 (München)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Natürlich lieben wir alle Technologie und natürlich ist es wichtig, dass man als Architekt:in den genutzten Tech-Stack versteht. Aber ist es nicht ein bisschen langweilig, sich nur in der Technologie zu tummeln?</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Natürlich lieben wir alle Technologie und natürlich ist es wichtig, dass man als Architekt:in den genutzten Tech-Stack versteht. Aber ist es nicht ein bisschen langweilig, sich nur in der Technologie zu tummeln?</p>
<p>In diesem Workshop möchten wir Sie dazu motivieren, die Komfortzone zu verlassen und einen Schritt zurückzutreten. Er soll dazu motivieren, tief in die Fachlichkeit einzutauchen, um das Business richtig zu verstehen und zu verinnerlichen. Bedeutet der aktuell inflationär beanspruchte Begriff “Digitalisierung” nicht ein Zusammenwachsen von Business und Tech? Wir denken, dass ein gemeinsames Verständnis dieser beiden Aspekte unerlässlich dabei ist, wenn man bessere Design-Entscheidungen treffen will.</p>
<p>Der Workshop wird sich allerdings nicht nur um das Thema Motivation drehen, sondern Ihnen primär zahlreiche praktisch umsetzbare Hilfsmittel aufzeigen, wie Sie Business-Aspekte reibungslos in Ihre Architektur-Arbeit integrieren und wie Sie dies in Ihrer Kommunikationsarbeit als Architekt:in bei unterschiedlichen Stakeholdern nutzen können.</p>
</body></html>
https://www.innoq.com/en/talks/2024/03/deep-dive-software-analytics/2024-01-05T00:00:00+01:002024-03-07T20:01:19+01:00Markus Harrermarkus.harrer@innoq.comhttps://www.innoq.com/en/staff/markus-harrer/Vortrag: "Deep-Dive Software Analytics" by Markus Harrer — 2024-03-12 Software Architecture Summit 2024 (München)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Software Analytics analysiert Softwaredaten von Systemen und deren Entwicklung, um systemische Probleme in Softwaresystemen zu entdecken und Verbesserungsarbeiten zu priorisieren. In diesem Deep-Dive-Workshop werfen wir einen Blick hinter die Kulissen von zwei weitverbreiteten Analysemethoden. Zum einen die Hotspot-Analyse, welche zur Darstellung der Verbesserungsdringlichkeit und Code-Komplexität für die gesamte Codebasis dient. Zum zweiten die Code-Abhängigkeits-Analyse, welche hilft, vorhandene Strukturen im Code wiederzuentdecken und neue Modularisierungsoptionen zu verproben.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Software Analytics analysiert Softwaredaten von Systemen und deren Entwicklung, um systemische Probleme in Softwaresystemen zu entdecken und Verbesserungsarbeiten zu priorisieren. In diesem Deep-Dive-Workshop werfen wir einen Blick hinter die Kulissen von zwei weitverbreiteten Analysemethoden. Zum einen die Hotspot-Analyse, welche zur Darstellung der Verbesserungsdringlichkeit und Code-Komplexität für die gesamte Codebasis dient. Zum zweiten die Code-Abhängigkeits-Analyse, welche hilft, vorhandene Strukturen im Code wiederzuentdecken und neue Modularisierungsoptionen zu verproben.</p>
<p>Der Großteil des Workshops besteht aus Hands-Ons mit Standardwerkzeuge aus den Bereichen Data Science und Graph Analytics wie Python, pandas, jQAssistant und Neo4j. Wir decken auch mögliche Fallstricke auf und erkunden weitere Potenziale dieser faszinierenden Analysetechniken. Alle, die schon immer einmal wissen wollten, was hinter den bunten Visualisierungen von Analyse-Tools steckt, finden hier wertvolle Einblicke – auch für ihre ganz eigenen Analyseideen.</p>
<p>Hinweis: Für die interaktiven Teile dieses Workshops wird ein Notebook benötigt.</p>
</body></html>
https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/2024-03-12T00:00:00+01:002024-03-12T12:01:04+01:00Rate Limiting with Spring Boot, Bucket4j, and RedisMartin Weckmartin.weck@innoq.comhttps://www.innoq.com/en/staff/martin-weck/<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Let’s implement rate-limiting protection for multiple Spring Boot server instances using bucket4j and redis to have the solution on application level.</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>In this blog post you learn a way how to write a rate limit protection for your <a href="https://start.spring.io/">Spring Boot server</a> that runs in multiple instances using <a href="https://github.com/bucket4j/bucket4j">bucket4j</a> and <a href="https://redis.io/">Redis</a>. Rate limiting means an upper threshold exists for how many calls per timeframe against all server instances are allowed per client. If the rate by which a client calls the servers is too high, the calls are rejected.</p>
<p>There exist various scenarios for rate limiting:</p>
<ul>
<li>If you have a single instance of your
server, <a href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/">this blog post</a> provides a simple
solution.</li>
<li>If you have control over your infrastructure (it is not controlled by another team that does not react to your
requests), rate limiting might be solved on the infrastructure level,
e.g. <a href="https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting">rate limiting using an HA-Proxy</a>.</li>
<li>If you have multiple instances of your server and prefer to implement rate limiting on application level this
blog post might be interesting.</li>
</ul>
<p>For the remainder of this text, we assume that we intend to solve the issue of rate limiting on application level and that we have multiple server instances.</p>
<h3><a id="theidea" href="https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/#theidea">The idea</a></h3>
<p>We use a <a href="https://en.wikipedia.org/wiki/Leaky_bucket">leaky bucket</a>. That means we have a bucket per calling client that we refill regularly by adding a fixed number of tokens to the remaining ones. For each request against any server instance, we remove a token. If no tokens are left, all servers return an <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#429">HTTP 429</a> until tokens become available again. This idea has already been implemented by <a href="https://github.com/bucket4j/bucket4j">bucket4j</a>. Many people use this library. However, remember that you add a dependency if you plan to use it. Therefore, please note that it is the work of only two contributors who have changed the major version several times. However, the good news is that
it provides integrations with several distributed storage providers. The distributed storage is needed to share the information between the server instances, and how many requests have already been made by one client. <a href="https://github.com/bucket4j/bucket4j">bucket4j</a> allows you to use a JDBC remote storage like:</p>
<ul>
<li><a href="https://bucket4j.com/8.9.0/toc.html#mysql-integration">MySQL</a></li>
<li><a href="https://bucket4j.com/8.9.0/toc.html#postgresql-integration">PostgreSQL</a></li>
<li><a href="https://bucket4j.com/8.9.0/toc.html#oracle-integration">Oracle</a></li>
<li><a href="https://bucket4j.com/8.9.0/toc.html#microsoftsqlserver-integration">Microsoft SQL Server</a></li>
<li><a href="https://bucket4j.com/8.9.0/toc.html#mariadb-integration">Maria DB</a></li>
</ul>
<p>In this blog post, we use <a href="https://redis.io/">Redis</a> instead for which the following clients are available</p>
<ul>
<li><a href="https://bucket4j.com/8.9.0/toc.html#example-of-bucket-instantiation-via-redissonbasedproxymanager">Redisson</a></li>
<li><a href="https://bucket4j.com/8.9.0/toc.html#example-of-bucket-instantiation-via-jedisbasedproxymanager">Jedis</a></li>
<li><a href="https://bucket4j.com/8.9.0/toc.html#example-of-bucket-instantiation-via-lettucebasedproxymanager">Lettuce</a></li>
</ul>
<p>We use <a href="https://lettuce.io/">Lettuce</a>. So we have a Redis server, several instances of the same <a href="https://start.spring.io/">Spring Boot server</a> with a REST endpoint that needs rate limit protection. For each client, calling our Spring Boot application, we create a bucket using a bucket configuration that refills the bucket with 200 tokens per minute and a Proxy Manager that creates a proxy through which the bucket will be shared between the server instances via the Redis database. A <a href="https://www.baeldung.com/spring-boot-add-filter">Spring Boot Filter</a> intercepts any calls to any endpoint of our Spring Boot Server and checks that the rate limit is kept. Only then the request is forwarded to the Controller, otherwise an <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#429">HTTP 429</a> is returned. If you need to check the rate limit only on certain endpoints, you can use an aspect and an annotation instead of a filter. Check out <a href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/">this blog post</a> that shows the details of that implementation.</p>
<p>you can use instead of a filter an aspect and an annotation as described in <a href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/">this blog post</a>.</p>
<h3><a id="startingpoint:aplaincontroller" href="https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/#startingpoint:aplaincontroller">Starting Point: A plain Controller</a></h3>
<p>Let’s assume we have a <a href="https://start.spring.io/">Spring Boot Server</a> with the following endpoint:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.PostMapping</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.RequestBody</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.RequestMapping</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.RestController</span><span class="o">;</span>
<span class="nd">@RestController</span>
<span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/api"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyRestController</span>
<span class="nd">@Autowired</span>
<span class="nc">MyService</span> <span class="n">myService</span><span class="o">;</span>
<span class="nd">@PostMapping</span>
<span class="kd">public</span> <span class="nc">MyResponse</span> <span class="nf">processRequest</span><span class="o">(</span><span class="nd">@RequestBody</span> <span class="kd">final</span> <span class="nc">MyRequest</span> <span class="n">request</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">myService</span><span class="o">.</span><span class="na">process</span><span class="o">(</span><span class="n">request</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<p>Let’s assume, <code>MyResponse</code>, and <code>MyRequest</code> are simple <a href="https://de.wikipedia.org/wiki/JavaBeans">JavaBeans</a>. Spring Boot will use the <a href="https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html">ObjectMapper</a> of Jackson to translate the incoming JSON to an instance of <code>MyRequest</code>. After the call <code>MyResponse</code> will be translated to JSON again. The content of <code>myService.process()</code> is not relevant, only that it returns a <code>MyResponse</code>.</p>
<h3><a id="bucket4j" href="https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/#bucket4j">bucket4j</a></h3>
<p>Add the following dependencies:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="nt"><dependency></span>
<span class="nt"><groupId></span>com.bucket4j<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>bucket4j-core<span class="nt"></artifactId></span>
<span class="nt"><version></span>8.9.0<span class="nt"></version></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>com.bucket4j<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>bucket4j-redis<span class="nt"></artifactId></span>
<span class="nt"><version></span>8.9.0<span class="nt"></version></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>io.lettuce<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>lettuce-core<span class="nt"></artifactId></span>
<span class="nt"><version></span>6.3.1.RELEASE<span class="nt"></version></span>
<span class="nt"></dependency></span></code></pre></div></figure>
<p>Execute Redis, locally:</p>
<figure><div class="highlight" title=""><pre class=""><code>docker pull redis:latest
docker run <span class="nt">-d</span> <span class="nt">-p</span> 6379:6379 <span class="nt">--name</span> rate-limit redis</code></pre></div></figure>
<p>Configure bucket4j to</p>
<ul>
<li>connect to Redis</li>
<li>provide a <code>ProxyManager</code> that connects bucket4j with the Redis client and can store JSON-Strings instead of objects as <code>byte[]</code>.</li>
<li>provide a <code>BucketConfiguration</code> with a chosen rate limit per time interval as Spring bean</li>
</ul>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.Bandwidth</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.BucketConfiguration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.distributed.ExpirationAfterWriteStrategy</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.distributed.proxy.ProxyManager</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.redis.lettuce.cas.LettuceBasedProxyManager</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.lettuce.core.RedisClient</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.lettuce.core.RedisURI</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.lettuce.core.api.StatefulRedisConnection</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.lettuce.core.codec.ByteArrayCodec</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.lettuce.core.codec.RedisCodec</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.lettuce.core.codec.StringCodec</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Bean</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Configuration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.Duration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.function.Supplier</span><span class="o">;</span>
<span class="nd">@Configuration</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RedisConfig</span> <span class="o">{</span>
<span class="kd">private</span> <span class="nc">RedisClient</span> <span class="nf">redisClient</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nc">RedisClient</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="nc">RedisURI</span><span class="o">.</span><span class="na">builder</span><span class="o">()</span>
<span class="o">.</span><span class="na">withHost</span><span class="o">(</span><span class="s">"localhost"</span><span class="o">)</span>
<span class="o">.</span><span class="na">withPort</span><span class="o">(</span><span class="mi">6379</span><span class="o">)</span>
<span class="o">.</span><span class="na">withSsl</span><span class="o">(</span><span class="kc">false</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">());</span>
<span class="o">}</span>
<span class="nd">@Bean</span>
<span class="kd">public</span> <span class="nc">ProxyManager</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="nf">lettuceBasedProxyManager</span><span class="o">()</span> <span class="o">{</span>
<span class="nc">RedisClient</span> <span class="n">redisClient</span> <span class="o">=</span> <span class="n">redisClient</span><span class="o">();</span>
<span class="nc">StatefulRedisConnection</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span> <span class="kt">byte</span><span class="o">[]></span> <span class="n">redisConnection</span> <span class="o">=</span> <span class="n">redisClient</span>
<span class="o">.</span><span class="na">connect</span><span class="o">(</span><span class="nc">RedisCodec</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="nc">StringCodec</span><span class="o">.</span><span class="na">UTF8</span><span class="o">,</span> <span class="nc">ByteArrayCodec</span><span class="o">.</span><span class="na">INSTANCE</span><span class="o">));</span>
<span class="k">return</span> <span class="nc">LettuceBasedProxyManager</span><span class="o">.</span><span class="na">builderFor</span><span class="o">(</span><span class="n">redisConnection</span><span class="o">)</span>
<span class="o">.</span><span class="na">withExpirationStrategy</span><span class="o">(</span>
<span class="nc">ExpirationAfterWriteStrategy</span><span class="o">.</span><span class="na">basedOnTimeForRefillingBucketUpToMax</span><span class="o">(</span><span class="nc">Duration</span><span class="o">.</span><span class="na">ofMinutes</span><span class="o">(</span><span class="mi">1L</span><span class="o">)))</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Bean</span>
<span class="kd">public</span> <span class="nc">Supplier</span><span class="o"><</span><span class="nc">BucketConfiguration</span><span class="o">></span> <span class="nf">bucketConfiguration</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">()</span> <span class="o">-></span> <span class="nc">BucketConfiguration</span><span class="o">.</span><span class="na">builder</span><span class="o">()</span>
<span class="o">.</span><span class="na">addLimit</span><span class="o">(</span><span class="nc">Bandwidth</span><span class="o">.</span><span class="na">simple</span><span class="o">(</span><span class="mi">200L</span><span class="o">,</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofMinutes</span><span class="o">(</span><span class="mi">1L</span><span class="o">)))</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<h3><a id="thefilter" href="https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/#thefilter">The filter</a></h3>
<p>Write a filter that every call to any endpoint of this Spring Boot application has to pass, before the actual endpoint is called. We use the client’s remote address as the bucket’s key. The <code>build()</code> method of the <code>bucket4j</code> library only creates new buckets if they do not exist already for the given key.</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.Bucket</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.BucketConfiguration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.ConsumptionProbe</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.distributed.proxy.ProxyManager</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">jakarta.servlet.Filter</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">jakarta.servlet.FilterChain</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">jakarta.servlet.ServletException</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">jakarta.servlet.ServletRequest</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">jakarta.servlet.ServletResponse</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">jakarta.servlet.http.HttpServletRequest</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">jakarta.servlet.http.HttpServletResponse</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.slf4j.Logger</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.slf4j.LoggerFactory</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.core.annotation.Order</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.IOException</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.concurrent.TimeUnit</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.function.Supplier</span><span class="o">;</span>
<span class="nd">@Component</span>
<span class="nd">@Order</span><span class="o">(</span><span class="mi">1</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RateLimitFilter</span> <span class="kd">implements</span> <span class="nc">Filter</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">Logger</span> <span class="no">LOG</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span><span class="n">getClass</span><span class="o">());</span>
<span class="nd">@Autowired</span>
<span class="nc">Supplier</span><span class="o"><</span><span class="nc">BucketConfiguration</span><span class="o">></span> <span class="n">bucketConfiguration</span><span class="o">;</span>
<span class="nd">@Autowired</span>
<span class="nc">ProxyManager</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">proxyManager</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">doFilter</span><span class="o">(</span><span class="nc">ServletRequest</span> <span class="n">servletRequest</span><span class="o">,</span> <span class="nc">ServletResponse</span> <span class="n">servletResponse</span><span class="o">,</span> <span class="nc">FilterChain</span> <span class="n">filterChain</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">ServletException</span> <span class="o">{</span>
<span class="nc">HttpServletRequest</span> <span class="n">httpRequest</span> <span class="o">=</span> <span class="o">(</span><span class="nc">HttpServletRequest</span><span class="o">)</span> <span class="n">servletRequest</span><span class="o">;</span>
<span class="nc">String</span> <span class="n">key</span> <span class="o">=</span> <span class="n">httpRequest</span><span class="o">.</span><span class="na">getRemoteAddr</span><span class="o">();</span>
<span class="nc">Bucket</span> <span class="n">bucket</span> <span class="o">=</span> <span class="n">proxyManager</span><span class="o">.</span><span class="na">builder</span><span class="o">().</span><span class="na">build</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="n">bucketConfiguration</span><span class="o">);</span>
<span class="nc">ConsumptionProbe</span> <span class="n">probe</span> <span class="o">=</span> <span class="n">bucket</span><span class="o">.</span><span class="na">tryConsumeAndReturnRemaining</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="no">LOG</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">">>>>>>>>remainingTokens: {}"</span><span class="o">,</span> <span class="n">probe</span><span class="o">.</span><span class="na">getRemainingTokens</span><span class="o">());</span>
<span class="k">if</span> <span class="o">(</span><span class="n">probe</span><span class="o">.</span><span class="na">isConsumed</span><span class="o">())</span> <span class="o">{</span>
<span class="n">filterChain</span><span class="o">.</span><span class="na">doFilter</span><span class="o">(</span><span class="n">servletRequest</span><span class="o">,</span> <span class="n">servletResponse</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="nc">HttpServletResponse</span> <span class="n">httpResponse</span> <span class="o">=</span> <span class="o">(</span><span class="nc">HttpServletResponse</span><span class="o">)</span> <span class="n">servletResponse</span><span class="o">;</span>
<span class="n">httpResponse</span><span class="o">.</span><span class="na">setContentType</span><span class="o">(</span><span class="s">"text/plain"</span><span class="o">);</span>
<span class="n">httpResponse</span><span class="o">.</span><span class="na">setHeader</span><span class="o">(</span><span class="s">"X-Rate-Limit-Retry-After-Seconds"</span><span class="o">,</span> <span class="s">""</span> <span class="o">+</span> <span class="nc">TimeUnit</span><span class="o">.</span><span class="na">NANOSECONDS</span><span class="o">.</span><span class="na">toSeconds</span><span class="o">(</span><span class="n">probe</span><span class="o">.</span><span class="na">getNanosToWaitForRefill</span><span class="o">()));</span>
<span class="n">httpResponse</span><span class="o">.</span><span class="na">setStatus</span><span class="o">(</span><span class="mi">429</span><span class="o">);</span>
<span class="n">httpResponse</span><span class="o">.</span><span class="na">getWriter</span><span class="o">().</span><span class="na">append</span><span class="o">(</span><span class="s">"Too many requests"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<h3><a id="execution" href="https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/#execution">Execution</a></h3>
<p>To run a local test with multiple instances, create an <code>application.properties</code> with</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="py">server.port</span><span class="p">=</span><span class="s">8080</span></code></pre></div></figure>
<p>and an <code>application-dev.properties</code> with</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="py">server.port</span><span class="p">=</span><span class="s">8081</span></code></pre></div></figure>
<p>If you execute the Spring Boot application, once with the JVM argument <code>-Dspring.profiles.active=dev</code> and once without the JVM argument, you will have two instances of your server running, where one uses port 8080 and another port 8081. Then you can use <code>curl</code> in your command line with port 8080 resp. 8081.</p>
<figure><div class="highlight" title=""><pre class=""><code>curl <span class="nt">--location</span> <span class="nt">--request</span> POST <span class="s1">'http://localhost:8080/api'</span> <span class="se">\</span>
<span class="nt">--header</span> <span class="s1">'Content-Type: application/json'</span> <span class="se">\</span>
<span class="nt">--data-raw</span> <span class="s1">'{
"question": "How do you do?"
}'</span></code></pre></div></figure>
<p>If you call the <code>curl</code> command too often, you will get an HTTP 429. Even if you switch the port to 8081, you will still get an HTTP 429 until tokens become available again as both server instances use the same bucket shared via Redis. So, after the duration specified in the bucket configuration has passed, you should get again an HTTP 200.</p>
<p>An interesting fact about filters, like the one used in this solution, is that they are executed sequentially. Hence, they cannot be executed asynchronously. If you use reactive programming or you prefer an asynchronous solution it might be worth checking out the <a href="https://bucket4j.com/8.9.0/toc.html#example-limiting-the-rate-of-access-to-the-asynchronous-servlet">bucket4j documentation on that</a>. The here used <code>LettuceBasedProxyManager</code> supports an asynchronous approach, too. If you use a solution based on <a href="https://github.com/jsr107/jsr107spec">JCache</a>, then this must be a synchronous solution. The next paragraph presents a way how to use a JCache based solution with bucket4j.</p>
<h3><a id="alternative:blazingcache" href="https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/#alternative:blazingcache">Alternative: Blazing Cache</a></h3>
<p><a href="https://bucket4j.com/8.9.0/toc.html#distributed-facilities">bucket4j</a> supports any <a href="https://www.jcp.org/en/jsr/detail?id=107">JCache (JSR 107)</a> solution. bucket4j provides a <a href="https://bucket4j.com/8.9.0/toc.html#verification-of-compatibility-with-a-particular-jcache-provider-is-your-responsibility">CompatibilityTest</a> that allows to verify that a chosen JCache provider is compatible with bucket4j. <a href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/#commento">As suggested by our reader Jacopo</a>, let’s discover how to adapt the above solution that it uses the compatible <a href="https://github.com/diennea/blazingcache">Blazing Cache</a> instead of redis.</p>
<ul>
<li>Remove <code>bucket4j-redis</code>, <code>lettuce-core</code>, keep <code>bucket4j-core</code> and add the following dependencies:</li>
</ul>
<figure><div class="highlight" title=""><pre class=""><code><span class="nt"><dependency></span>
<span class="nt"><groupId></span>com.bucket4j<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>bucket4j-jcache<span class="nt"></artifactId></span>
<span class="nt"><version></span>8.9.0<span class="nt"></version></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>javax.cache<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>cache-api<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.1.1<span class="nt"></version></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.blazingcache<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>blazingcache-jcache<span class="nt"></artifactId></span>
<span class="nt"><version></span>3.3.0<span class="nt"></version></span>
<span class="nt"></dependency></span></code></pre></div></figure>
<ul>
<li>Replace the prior <code>RedisConfig</code> by the following <code>JCacheConfig</code>:</li>
</ul>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.Bandwidth</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.BucketConfiguration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.Refill</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.distributed.proxy.ProxyManager</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">io.github.bucket4j.grid.jcache.JCacheProxyManager</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Bean</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.context.annotation.Configuration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.cache.Cache</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.cache.CacheManager</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.cache.Caching</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.cache.configuration.MutableConfiguration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.cache.spi.CachingProvider</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.Duration</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Properties</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.function.Supplier</span><span class="o">;</span>
<span class="nd">@Configuration</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">JCacheConfig</span> <span class="o">{</span>
<span class="nd">@Bean</span>
<span class="kd">public</span> <span class="nc">Cache</span> <span class="nf">cache</span><span class="o">()</span> <span class="o">{</span>
<span class="nc">CachingProvider</span> <span class="n">provider</span> <span class="o">=</span> <span class="nc">Caching</span><span class="o">.</span><span class="na">getCachingProvider</span><span class="o">();</span>
<span class="nc">Properties</span> <span class="n">properties</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Properties</span><span class="o">();</span>
<span class="n">properties</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"blazingcache.mode"</span><span class="o">,</span><span class="s">"static"</span><span class="o">);</span>
<span class="n">properties</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"blazingcache.jmx"</span><span class="o">,</span><span class="s">"false"</span><span class="o">);</span> <span class="c1">//default value</span>
<span class="n">properties</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"blazingcache.server.host"</span><span class="o">,</span><span class="s">"localhost"</span><span class="o">);</span> <span class="c1">// default value</span>
<span class="n">properties</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"blazingcache.server.port"</span><span class="o">,</span><span class="s">"1025"</span><span class="o">);</span> <span class="c1">// default value</span>
<span class="n">properties</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"blazingcache.server.ssl"</span><span class="o">,</span><span class="s">"false"</span><span class="o">);</span> <span class="c1">// default value</span>
<span class="nc">CacheManager</span> <span class="n">cacheManager</span> <span class="o">=</span> <span class="n">provider</span><span class="o">.</span><span class="na">getCacheManager</span><span class="o">(</span><span class="n">provider</span><span class="o">.</span><span class="na">getDefaultURI</span><span class="o">(),</span> <span class="n">provider</span><span class="o">.</span><span class="na">getDefaultClassLoader</span><span class="o">(),</span> <span class="n">properties</span><span class="o">);</span>
<span class="nc">MutableConfiguration</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">></span> <span class="n">cacheConfiguration</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">MutableConfiguration</span><span class="o"><>();</span>
<span class="kd">final</span> <span class="nc">Cache</span> <span class="n">alreadyExistingCache</span> <span class="o">=</span> <span class="n">cacheManager</span><span class="o">.</span><span class="na">getCache</span><span class="o">(</span><span class="s">"rate-limit-bucket"</span><span class="o">);</span>
<span class="k">return</span> <span class="n">alreadyExistingCache</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="n">alreadyExistingCache</span> <span class="o">:</span> <span class="n">cacheManager</span><span class="o">.</span><span class="na">createCache</span><span class="o">(</span><span class="s">"rate-limit-bucket"</span><span class="o">,</span> <span class="n">cacheConfiguration</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Bean</span>
<span class="kd">public</span> <span class="nc">ProxyManager</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="nf">jCacheProxyManager</span><span class="o">(</span><span class="nc">Cache</span> <span class="n">cache</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nc">JCacheProxyManager</span><span class="o"><>(</span><span class="n">cache</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Bean</span>
<span class="kd">public</span> <span class="nc">Supplier</span><span class="o"><</span><span class="nc">BucketConfiguration</span><span class="o">></span> <span class="nf">bucketConfiguration</span><span class="o">()</span> <span class="o">{</span>
<span class="nc">Refill</span> <span class="n">refill</span> <span class="o">=</span> <span class="nc">Refill</span><span class="o">.</span><span class="na">intervally</span><span class="o">(</span><span class="mi">200</span><span class="o">,</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">ofMinutes</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span>
<span class="k">return</span> <span class="o">()</span> <span class="o">-></span> <span class="nc">BucketConfiguration</span><span class="o">.</span><span class="na">builder</span><span class="o">()</span>
<span class="o">.</span><span class="na">addLimit</span><span class="o">(</span><span class="nc">Bandwidth</span><span class="o">.</span><span class="na">classic</span><span class="o">(</span><span class="mi">200L</span><span class="o">,</span> <span class="n">refill</span><span class="o">))</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<ul>
<li>Keep all other code untouched, i.e. <code>RateLimitFilter</code>, <code>MyRestController</code>
</li>
<li>Execute a <code>Blazing Cache Server</code>
</li>
<li>Execute one server instance using port 8080 and another using port 8081 like before</li>
<li>Run the <code>curl</code> requests like before</li>
</ul>
<p>At the time of writing the step to execute a <code>Blazing Cache Server</code> has been a little tricky. The documentation states <a href="https://blazingcache.readme.io/docs/getting-started#distributed-setup">to download the BazingCache package</a>, although this could not be found. However, one can download the <a href="https://github.com/diennea/blazingcache">sources</a>, run <code>mvn install -Dmaven.test.skip=true</code> on the subdirectory <code>blazingcache-service</code>, and then obtain the zip file referred to in the <a href="https://blazingcache.readme.io/docs/getting-started#distributed-setup">documentation</a>, i.e. <code>blazingcache-3.3.0.zip</code>, from the <code>target</code> directory of the build.</p>
<h3><a id="conclusion" href="https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/#conclusion">Conclusion</a></h3>
<p>This blog post presented a solution for implementing rate limiting for multiple instances of a Spring Boot server. Depending on the given project requirements and constraints readers may choose between different valid approaches. This blog post used bucket4j. It presented a way, to how bucket4j can be combined with Redis or Blazing Cache as a JCache provider. Based on that it should be easy for readers to adapt the solution presented here if another distributed storage provider is preferred. Interesting other alternatives might be also to consider <a href="https://resilience4j.readme.io/docs/ratelimiter">Resilience4j</a> or the even simpler solution presented in <a href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/">this other blog post</a>.</p>
</body></html>
https://www.innoq.com/en/talks/2024/03/develop-ci-cd-pipelines-locally-with-dagger-io-decompiled-2024/2024-01-18T00:00:00+01:002024-01-18T16:30:57+01:00Fabian Kretzerfabian.kretzer@innoq.comhttps://www.innoq.com/en/staff/fabian-kretzer/Talk: "Develop CI/CD-pipelines locally with dagger.io" by Fabian Kretzer — 2024-03-08 DecompileD 2024 (Dresden)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>… and escape YAML-hell along the way</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>… and escape YAML-hell along the way</p>
<p>Build and CI/CD systems are powerful tools which complement each other well.</p>
<p>Systems like Gradle or maven take us a long way from code to running software artifacts. But if we get into a highly collaborative setting the need for a central CI/CD system will emerge soon. Scalable and reproduceable build environments for a team are just too helpful to go without them.</p>
<p>Sadly testing and developing complex pipelines often turns out to be tedious, because we have to wait for the central system to pick up and process our jobs. Wouldn’t it be fabulous if we could develop and run our pipeline steps locally without modifying them? Exactly this is where dagger.io is coming into play.</p>
<p>Through the smart combination of using containers to isolate build steps and real programming languages for expressive, declarative and type safe configuration of our pipeline dagger.io creates a flexible build system which - thanks to containers - can also be extended in different programming languages.</p>
<p>In the talk we will look into the concepts and components of dagger.io by example and learn about its inherent advantages in comparison to other build and CI/CD systems. We will learn why we definitely want to use a real programming language instead of YAML in the future and define our first own build steps.</p>
</body></html>
https://www.innoq.com/en/talks/2024/03/data-mesh-2019-2023-und-2024/2024-02-09T00:00:00+01:002024-02-09T09:21:25+01:00Dr. Simon Harrersimon.harrer@innoq.comhttps://www.innoq.com/en/staff/dr-simon-harrer/Vortrag: "Data Mesh: 2019, 2023 und 2024" by Dr. Simon Harrer — 2024-03-08 BATbern52 - Data Mesh - Modernes Datenmanagement (Bern)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Data Mesh ist ein soziotechnischer Ansatz für die Erstellung einer dezentralen Datenarchitektur. Zhamak Dehghani hat den Begriff Data Mesh und die Theorie dahinter mit ihren Blog-Beiträgen in 2019 bereits geprägt und dann mit ihrem Buch noch etwas ausführlicher beschrieben. Diese Theorie möchte ich kurz vorstellen, um für den Abend ein Begriffsfundament zu giessen. Seit 2019 ist jedoch viel passiert. Viele haben das Konzept gelesen und einige haben ihre Data Mesh Reise bereits begonnen. Ich möchte auf 2023 zurückblicken und den aktuellen Stand der Praxis beschreiben. Enden möchte ich mit einem Ausblick über die Top 5 Entwicklungen in Data Mesh, die dich total überraschen werden. </p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Data Mesh ist ein soziotechnischer Ansatz für die Erstellung einer dezentralen Datenarchitektur. Zhamak Dehghani hat den Begriff Data Mesh und die Theorie dahinter mit ihren Blog-Beiträgen in 2019 bereits geprägt und dann mit ihrem Buch noch etwas ausführlicher beschrieben. Diese Theorie möchte ich kurz vorstellen, um für den Abend ein Begriffsfundament zu giessen. Seit 2019 ist jedoch viel passiert. Viele haben das Konzept gelesen und einige haben ihre Data Mesh Reise bereits begonnen. Ich möchte auf 2023 zurückblicken und den aktuellen Stand der Praxis beschreiben. Enden möchte ich mit einem Ausblick über die Top 5 Entwicklungen in Data Mesh, die dich total überraschen werden. </p>
</body></html>
https://www.innoq.com/en/talks/2024/03/data-contract-cli/2024-01-17T00:00:00+01:002024-01-17T08:39:40+01:00Stefan Negelestefan.negele@innoq.comhttps://www.innoq.com/en/staff/stefan-negele/Vortrag: "Data Contract CLI" by Stefan Negele — 2024-03-06 INNOQ Technology Lunch 03/24 (Online)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Data Contracts können für die reibungslose Kommunikation zwischen verschiedenen analytischen und operativen Softwarekomponenten entscheidend sein. Mit der Data Contract Specification arbeitet INNOQ an einem generischen Standard, ähnlich der OpenAPI und AsyncAPI.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Data Contracts können für die reibungslose Kommunikation zwischen verschiedenen analytischen und operativen Softwarekomponenten entscheidend sein. Mit der Data Contract Specification arbeitet INNOQ an einem generischen Standard, ähnlich der OpenAPI und AsyncAPI.</p>
<p>Das Data Contract CLI soll die Arbeit mit Data Contracts deutlich vereinfachen, indem es Funktionen zur Erstellung, Bearbeitung und vor allem zum Testen von Data Contracts bereitstellt.</p>
<p>In diesem Technology Lunch werden diese Funktionen anhand von praktischen Anwendungsfällen vorgestellt. Dabei kann man viel über das Tool, aber auch über das Arbeiten mit Data Contracts lernen.</p>
</body></html>
https://www.innoq.com/en/talks/2024/03/security-in-legacy-systemen/2024-01-19T00:00:00+01:002024-01-19T10:55:29+01:00Felix Schumacherfelix.schumacher@innoq.comhttps://www.innoq.com/en/staff/felix-schumacher/Vortrag: "Security in Legacy-Systemen" by Felix Schumacher — 2024-03-06 // heise devSec( ) (Hannover)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Oft nimmt Pflege den größten Teil des Lebenszyklus einer Software ein. In größeren Organisationen muss man nicht selten die Wartung einer Software übernehmen, die man nicht selbst geschrieben hat. Besonders im Kontext sogenannter Legacy-Systeme werden Entwickler:innen oft auf das Problem treffen, dass bei der ursprünglichen Implementierung das Thema Security keine oder lediglich eine untergeordnete Rolle spielte. Wenn die Software eventuell sogar unter anderen Betriebsbedingungen laufen muss, z. B. wegen einer Migration in die Cloud, muss man sich tiefergehend mit dem Security-Aspekt beschäftigen.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Oft nimmt Pflege den größten Teil des Lebenszyklus einer Software ein. In größeren Organisationen muss man nicht selten die Wartung einer Software übernehmen, die man nicht selbst geschrieben hat. Besonders im Kontext sogenannter Legacy-Systeme werden Entwickler:innen oft auf das Problem treffen, dass bei der ursprünglichen Implementierung das Thema Security keine oder lediglich eine untergeordnete Rolle spielte. Wenn die Software eventuell sogar unter anderen Betriebsbedingungen laufen muss, z. B. wegen einer Migration in die Cloud, muss man sich tiefergehend mit dem Security-Aspekt beschäftigen.</p>
<p>In diesem Vortrag sollen die üblichen Stolpersteine betrachtet werden, die im Hinblick auf Security bei der Übernahme von Legacy-Systemen beachtet werden sollten, sowie Maßnahmen, um langfristige Wartung und Betrieb sicher zu gewährleisten.</p>
<h4><a id="vorkenntnisse" href="https://www.innoq.com/en/talks/2024/03/security-in-legacy-systemen/#vorkenntnisse">Vorkenntnisse</a></h4>
<p>Grundlegende Kenntnisse in der Entwicklung und Betrieb von Services sowie Grundkenntnisse über mögliche Angriffsvektoren (z. B. die OWASP Top 10).</p>
<h4><a id="lernziele" href="https://www.innoq.com/en/talks/2024/03/security-in-legacy-systemen/#lernziele">Lernziele</a></h4>
<p>Nach dem Vortrag sollten die Teilnehmer:innen wissen, wie man Sicherheitsrisiken bei dem Betrieb von Legacy-Software identifizieren und mitigieren kann und welche praktischen Maßnahmen ergriffen werden können, um die Security von Legacy-Systemen kurz- und langfristig zu verbessern.</p>
</body></html>
https://www.innoq.com/en/talks/2024/03/software-supply-chain-security-mehr-als-nur-tooling-und-package-management/2024-01-19T00:00:00+01:002024-01-19T10:54:13+01:00Christoph Iserlohnchristoph.iserlohn@innoq.comhttps://www.innoq.com/en/staff/ci/Vortrag: "Software-Supply-Chain-Security - Mehr als nur Tooling und Package-Management" by Christoph Iserlohn — 2024-03-06 // heise devSec( ) (Hannover)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Software-Supply-Chain-Security ist ein Thema, das immer mehr an Bedeutung gewinnt. Leider wird es häufig auf 3rd-Party-Software, Package-Management und Tooling zum Aufspüren von bekannten Sicherheitslücken verkürzt.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Software-Supply-Chain-Security ist ein Thema, das immer mehr an Bedeutung gewinnt. Leider wird es häufig auf 3rd-Party-Software, Package-Management und Tooling zum Aufspüren von bekannten Sicherheitslücken verkürzt.</p>
<p>Zweifellos sind diese wichtigen Bestandteile der Software-Supply-Chain-Security, dennoch verbergen sich noch eine Menge weitere, ebenso wichtige Aspekte hinter einer sicheren Software-Supply-Chain: von kulturellen, psychologischen und soziotechnischen Facetten über vertrauenswürdige Quellen, Builds und Tools sowie Standards und Maßnahmen.</p>
<p>Lernen Sie in diesem Vortrag das volle Spektrum der Software-Supply-Chain-Security kennen.</p>
<h4><a id="lernziele" href="https://www.innoq.com/en/talks/2024/03/software-supply-chain-security-mehr-als-nur-tooling-und-package-management/#lernziele">Lernziele</a></h4>
<p>Nach dem Vortrag sollten die Teilnehmer:innen eine Vorstellung davon haben, was alles zur Software-Supply-Chain-Security gehört, und wie sie diese in ihren Projekten berücksichtigen können.</p>
</body></html>
https://www.innoq.com/en/talks/2024/03/ki-systeme-planen-use-cases-prototypen-und-kostenschaetzungen-schnell-und-zielsicher-erstellen/2024-02-02T00:00:00+01:002024-02-02T17:09:50+01:00Dr. Larysa Visengeriyevalarysa.visengeriyeva@innoq.comhttps://www.innoq.com/en/staff/larysa-visengeriyeva/Vortrag: "KI-Systeme planen: Use Cases, Prototypen und Kostenschätzungen schnell und zielsicher erstellen" by Dr. Larysa Visengeriyeva — 2024-03-05 Summit Architekturmanagement (Leipzig)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Eine der bewährten Praktiken, die wir von großartigen Ingenieur:innen kennen, ist die grobe Kostenschätzung und Ressourcenplanung auf einem Notizzettel. Im Bereich des maschinellen Lernens würden wir alle von einer solchen Fähigkeit zur “groben Kostenschätzung” profitieren, um ein ML-System zu entwerfen. Wir müssen so kostengünstig wie möglich bestätigen, dass unser zukünftiges ML-Projekt sinnvoll ist und ein Geschäftsproblem lösen wird, und dass die Kosten und Ressourcen realisierbar sein werden.
In diesem Vortrag stellt Larysa Visengeryeva ein gemeinsames Design-Toolkit für ML-Projekte vor, das die Identifizierung von ML-Anwendungsfällen und die grobe Prototypisierung unterstützt, indem drei Canvas verwendet werden: Machine Learning Canvas, Data Landscape Canvas und MLOps Stack Canvas.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Eine der bewährten Praktiken, die wir von großartigen Ingenieur:innen kennen, ist die grobe Kostenschätzung und Ressourcenplanung auf einem Notizzettel. Im Bereich des maschinellen Lernens würden wir alle von einer solchen Fähigkeit zur “groben Kostenschätzung” profitieren, um ein ML-System zu entwerfen. Wir müssen so kostengünstig wie möglich bestätigen, dass unser zukünftiges ML-Projekt sinnvoll ist und ein Geschäftsproblem lösen wird, und dass die Kosten und Ressourcen realisierbar sein werden.
In diesem Vortrag stellt Larysa Visengeryeva ein gemeinsames Design-Toolkit für ML-Projekte vor, das die Identifizierung von ML-Anwendungsfällen und die grobe Prototypisierung unterstützt, indem drei Canvas verwendet werden: Machine Learning Canvas, Data Landscape Canvas und MLOps Stack Canvas.</p>
</body></html>
https://www.innoq.com/de/blog/2024/02/ollama-llm-spring-ai-rag/2024-02-29T00:00:00+01:002024-02-29T10:43:34+01:00Lokale LLMs mit Ollama und Spring AI nutzenMichael Vitzmichael.vitz@innoq.comhttps://www.innoq.com/en/staff/michael-vitz/<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Egal, ob wir wollen oder nicht, um AI und speziell Large Language Models (LLM)
kommen wir aktuell nicht herum. Mich schrecken solche Hypes zwar aus Reflex eher
ab. Allerdings sieht es so aus, als würde von diesem Hype mehr bleiben als vom
letzten, der Blockchain. Deshalb wollen wir uns in diesem Post einmal anschauen,
wie man ein LLM lokal aufsetzen kann und dieses mittels Spring AI in eine Spring
Boot-Anwendung einbinden kann.</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Spätestens mit <a href="https://chat.openai.com">ChatGPT</a> haben es LLMs geschafft, in aller Munde zu sein. Und
auch wenn LLMs nur ein Aspekt von AI sind, ist es aktuell der Teil, der wohl am
meisten im Fokus steht. Dabei stechen vorrangig die LLMs der großen Firmen, wie
ChatGPT von OpenAI, <a href="https://gemini.google.com/">Googles Gemini</a> und <a href="https://www.microsoft.com/de-de/microsoft-copilot">Microsoft Copilot</a>
heraus. Diese Firmen haben, aus der Historie heraus, die Unmenge an Daten,
um diese Modelle anzulernen und auch das notwendige Kleingeld, um die dafür
benötigte Hardware und Betriebskosten zu stemmen.</p>
<p>Gerade in Deutschland, bzw. der EU, ist diese Nutzung von Daten aus juristischer
Perspektive, insbesondere hinsichtlich Datenschutz und Urheberrecht noch unklar
und schwierig. Vor allem da die Anbindung eines LLM ohne spezifische interne
Daten in den wenigsten Anwendungsfällen einen wirklichen Mehrwert liefert.
Dementsprechend bleiben nur zwei Möglichkeiten. Entweder, wir lernen, mit unseren
Daten, ein eigenes LLM an oder wir nutzen ein bestehendes LLM und geben unsere
Daten als Kontext mit. Das eigene Anlernen benötigt, neben einer Menge Wissen
und Arbeitskraft, dann auch die passende Hardware. Der zweite Weg ist deswegen
aktuell der Übliche. Diesen werden wir daher in diesem Post einmal Schritt für
Schritt abgehen.</p>
<h3><a id="lokalesllmmitollama" href="https://www.innoq.com/de/blog/2024/02/ollama-llm-spring-ai-rag/#lokalesllmmitollama">Lokales LLM mit Ollama</a></h3>
<p>Vereinfacht gesagt, handelt es sich bei einem LLM um ein auf Sprache
spezialisiertes Modell. Das heißt, ein solches Modell kann entweder Anfragen in
natürlicher Sprache verstehen oder Antworten in natürlicher Sprache produzieren
oder beides. Dazu gibt es auch noch Modelle, die für einen spezifischen
Anwendungsfall, wie das Schreiben oder Diskutieren über Code, optimiert wurden.</p>
<p>Mittlerweile gibt es auch viele Modelle zur freien und lokalen Nutzung. Diese
laufen auch auf nicht spezialisierter Hardware. Natürlich muss man hier, im
Vergleich zu den großen kommerziellen Cloud-Modellen, Abstriche machen. In der
Regel ist die Performanz, vor allem auf normaler Consumer-Hardware, schlechter
und auch die Modelle selbst sind qualitativ nicht komplett vergleichbar. Um sich
mit dem Thema vertraut zu machen und auszuprobieren, wie man diese in eine
Anwendung integrieren kann, reichen diese aber mehr als aus. Der Anlaufpunkt für
diese Modelle, und noch mehr, ist <a href="https://huggingface.co/">Hugging Face</a> - eine
Open-Source-Community, die sich auf die Entwicklung von Künstlicher
Intelligenz (KI) und Natural Language Processing (NLP) spezialisiert hat.</p>
<p>Um nun so ein LLM lokal zu nutzen, müssen wir uns als Erstes für eine
Ablaufumgebung entscheiden. Hier gibt es zahlreiche Möglichkeiten, wie
<a href="https://lmstudio.ai/">LM Studio</a>, <a href="https://ollama.com/">Ollama</a> oder
<a href="https://github.com/oobabooga/text-generation-webui">Text Generation Web UI</a>. Für diesen Post nutzen wir
Ollama. Ollama ist eine solide Lösung, die sich vor allem auf die Grundlagen, das
Verwalten von LLMs, spezialisiert.</p>
<p>Nachdem wir Ollama installiert und gestartet haben können wir über die
Kommandozeile Modelle herunterladen und diese starten. In Listing 1 ist zu sehen
wie wir einen Prompt mit dem Modell <em>Mistral</em> starten und eine Frage an das LLM
formulieren.</p>
<figure><div class="highlight" title="Listing 1: Ollama Prompt für Mistral Modell"><pre class=""><code>~ $ ollama run mistral
>>> What is INNOQ?
INNOQ is a German consulting company that specializes in software engineering, digital transformation, and innovation.
The company was founded in 1995 and has its headquarters in Munich. INNOQ offers services in areas such as software
architecture, agile development, test automation, DevOps, and data engineering. Their clients come from various
industries, including finance, healthcare, telecommunications, media, and manufacturing. INNOQ's mission is to help
businesses innovate and stay competitive by providing them with the latest technologies and best practices in software
development.
>>> Send a message (/? for help)</code></pre></div>
<figcaption>Listing 1: Ollama Prompt für Mistral Modell</figcaption></figure>
<p>Bei der ersten Verwendung eines Modells muss dieses vorher jedoch noch
heruntergeladen werden, was durchaus dauern kann. So ist das oben verwendete
Mistral etwa 4,1 GB groß.</p>
<p>Wir müssen jedoch nicht den interaktiven Prompt nutzen, denn eigentlich ist
Ollama ein Serverprozess, der, standardmäßig, auf Port 11434 gestartet wird.
Diesen können wir also auch über ein Tool das HTTP-Anfragen absetzen kann, wie
curl, ansprechen (siehe Listing 2).</p>
<figure><div class="highlight" title="Listing 2: Abfrage der Ollama HTTP API"><pre class=""><code>~ $ curl http://localhost:11434/api/generate -d '{
"model": "mistral",
"prompt": "What is INNOQ?"
}'
{"model":"mistral","created_at":"...","response":" IN","done":false}
{"model":"mistral","created_at":"...","response":"NO","done":false}
{"model":"mistral","created_at":"...","response":"Q","done":false}
{"model":"mistral","created_at":"...","response":" is","done":false}
{"model":"mistral","created_at":"...","response":" a","done":false}
{"model":"mistral","created_at":"...","response":" German","done":false}
{"model":"mistral","created_at":"...","response":" consulting","done":false}
{"model":"mistral","created_at":"...","response":" company","done":false}
{"model":"mistral","created_at":"...","response":" special","done":false}
{"model":"mistral","created_at":"...","response":"izing","done":false}
{"model":"mistral","created_at":"...","response":" in","done":false}
{"model":"mistral","created_at":"...","response":" software","done":false}
{"model":"mistral","created_at":"...","response":" engineering","done":false}
…</code></pre></div>
<figcaption>Listing 2: Abfrage der Ollama HTTP API</figcaption></figure>
<h3><a id="springai" href="https://www.innoq.com/de/blog/2024/02/ollama-llm-spring-ai-rag/#springai">Spring AI</a></h3>
<p>Um nun dasselbe Model aus einer <a href="https://spring.io/projects/spring-boot">Spring Boot</a>-Anwendung heraus
anzusprechen könnten wir natürlich den dort vorhandenen RestClient nutzen,
dieselbe HTTP-Anfrage wie oben erzeugen und absetzen. Glücklicherweise gibt es
aber bereits erste Schritte, um diese Abfragen und Konzepte zu kapseln, nämlich
<a href="https://spring.io/projects/spring-ai">Spring AI</a>.</p>
<p>Um die dort vorhandene Unterstützung für Ollama einzubinden, nutzen wir die
<code>spring-ai-bom</code> und können anschließend <code>spring-ai-ollama-spring-boot-starter</code>
als Abhängigkeit hinzufügen (siehe Listing 3).</p>
<figure><div class="highlight" title="Listing 3: Spring AI Abhängigkeiten"><pre class=""><code>…
<span class="nt"><dependencyManagement></span>
<span class="nt"><dependencies></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.springframework.ai<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spring-ai-bom<span class="nt"></artifactId></span>
<span class="nt"><version></span>0.8.0<span class="nt"></version></span>
<span class="nt"><type></span>pom<span class="nt"></type></span>
<span class="nt"><scope></span>import<span class="nt"></scope></span>
<span class="nt"></dependency></span>
<span class="nt"></dependencies></span>
<span class="nt"></dependencyManagement></span>
<span class="nt"><dependencies></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.springframework.ai<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spring-ai-ollama-spring-boot-starter<span class="nt"></artifactId></span>
<span class="nt"></dependency></span>
<span class="nt"></dependencies></span>
…</code></pre></div>
<figcaption>Listing 3: Spring AI Abhängigkeiten</figcaption></figure>
<p>Für dieselbe Abfrage wie bisher können wir jetzt einen <code>Prompt</code> mit einer
<code>UserMessage</code> erzeugen und diesen über einen <code>ChatClient</code> an das LLM senden und
die Antwort auswerten (siehe Listing 4).</p>
<figure><div class="highlight" title="Listing 4: Spring AI ChatClient Nutzung"><pre class=""><code><span class="nd">@SpringBootApplication</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Application</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">SpringApplication</span><span class="o">.</span><span class="na">run</span><span class="o">(</span><span class="nc">Application</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Bean</span>
<span class="nc">CommandLineRunner</span> <span class="nf">chat</span><span class="o">(</span><span class="nc">ChatClient</span> <span class="n">client</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">args</span> <span class="o">-></span> <span class="o">{</span>
<span class="kt">var</span> <span class="n">query</span> <span class="o">=</span> <span class="s">"What is INNOQ?"</span><span class="o">;</span>
<span class="kt">var</span> <span class="n">prompt</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Prompt</span><span class="o">(</span>
<span class="k">new</span> <span class="nf">UserMessage</span><span class="o">(</span><span class="n">query</span><span class="o">));</span>
<span class="kt">var</span> <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="na">call</span><span class="o">(</span><span class="n">prompt</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span>
<span class="n">response</span><span class="o">.</span><span class="na">getResult</span><span class="o">().</span><span class="na">getOutput</span><span class="o">().</span><span class="na">getContent</span><span class="o">());</span>
<span class="o">};</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div>
<figcaption>Listing 4: Spring AI ChatClient Nutzung</figcaption></figure>
<p>Um das gewünschte Modell auszuwählen, müssen wir die zusätzlich die Option
<code>spring.ai.ollama.chat.options.model</code>, hier über die <em>application.properites</em>
(siehe Listing 5) setzen.</p>
<figure><div class="highlight" title="Listing 5: application.properties der Anwendung"><pre class=""><code>spring.ai.ollama.chat.options.model=mistral
spring.main.banner-mode=off
spring.main.web-application-type=none</code></pre></div>
<figcaption>Listing 5: application.properties der Anwendung</figcaption></figure>
<p>Zusätzlich verhindern wir noch das versucht wird ein HTTP-Server zu starten, da
wir diesen für das Beispiel nicht benötigen. Starten wir jetzt die Anwendung
erscheint nach einiger Zeit die Antwort des LLMs auf der Standardausgabe (siehe
Listing 6).</p>
<figure><div class="highlight" title="Listing 6: Ausgaben der Anwendung"><pre class=""><code>…: Starting Application using Java 21.0.2 with PID 55277
…
…: Started Application <span class="k">in </span>1.07 seconds <span class="o">(</span>process running <span class="k">for </span>1.551<span class="o">)</span>
INNOQ is a technology and consulting company headquartered <span class="k">in </span>Germany, with a focus on software engineering, …
…</code></pre></div>
<figcaption>Listing 6: Ausgaben der Anwendung</figcaption></figure>
<h3><a id="kontextundrag" href="https://www.innoq.com/de/blog/2024/02/ollama-llm-spring-ai-rag/#kontextundrag">Kontext und RAG</a></h3>
<p>Ein LLM kann zur Beantwortung der Prompts lediglich die beim Training
verwendeten Daten zur Beantwortung nutzen. Deswegen wird etwa die
Frage „What date is today?“ mit „I’m an artificial intelligence and don’t have
the ability to experience time or know the current date without being connected
to a database or external source. However, I can help you check the date if you
tell me which specific date you have in mind, or I can tell you the current date
if you provide me with your location so I can access an online calendar or
database.“ beantwortet.</p>
<p>Wir können dem Prompt aber neben der eigentlichen Frage auch zusätzlichen
Kontext mitgeben. Diesen kann das LLM dann mit nutzen. Um dies in der Anwendung
zu machen, können wir dem <code>Prompt</code> eine Liste von Nachrichten mitgeben und neben
der Frage als <code>UserMessage</code> auch eine <code>SystemMessage</code> erzeugen (siehe Listing
7).</p>
<figure><div class="highlight" title="Listing 7: Übergabe von Kontext als SystemMessage"><pre class=""><code><span class="err">…</span>
<span class="kt">var</span> <span class="n">query</span> <span class="o">=</span> <span class="s">"What date is today?"</span><span class="o">;</span>
<span class="kt">var</span> <span class="n">prompt</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Prompt</span><span class="o">(</span><span class="nc">List</span><span class="o">.</span><span class="na">of</span><span class="o">(</span>
<span class="k">new</span> <span class="nf">SystemMessage</span><span class="o">(</span><span class="s">"Today is 2024-02-27."</span><span class="o">),</span>
<span class="k">new</span> <span class="nf">UserMessage</span><span class="o">(</span><span class="n">query</span><span class="o">)));</span>
<span class="kt">var</span> <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="na">call</span><span class="o">(</span><span class="n">prompt</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span>
<span class="n">response</span><span class="o">.</span><span class="na">getResult</span><span class="o">().</span><span class="na">getOutput</span><span class="o">().</span><span class="na">getContent</span><span class="o">());</span>
<span class="err">…</span></code></pre></div>
<figcaption>Listing 7: Übergabe von Kontext als SystemMessage</figcaption></figure>
<p>Mit diesem Kontext bekommen wir nun „Today, February 27, 2024, falls in the year 2024.
It is the 58th day of the year in the Gregorian calendar, with only 306
days remaining until the end of the year on December 31. This date represents a
Wednesday according to the standard 7-day weekly cycle.“ als Antwort.</p>
<p>Um ein LLM also mit eigenen Daten zu nutzen, müssen wir die für die Beantwortung
relevanten Informationen mit in unseren Prompt packen. Dieses hat jedoch in der
Regel ein Limit an Tokens, vereinfacht gesagt Wörtern, die es akzeptiert. Das
liegt daran, dass bei einer größeren Menge an Tokens die Beantwortung mehr
Energie benötigt und demnach länger dauert und teurer wird. Deswegen werden auf
LLMs basierende Anwendungen aktuell in der Regel in Kombination mit einer
Vektor-basierten Datenbank und dem Ansatz der Retrieval Augmented Generation
(RAG) entwickelt.</p>
<p>Vereinfacht gesagt werden hierbei sämtliche Informationen, die wir potenziell
als Kontext benötigen mithilfe eines Embedding Modells in Vektoren umgewandelt.
Diese, mehrdimensionalen Vektoren werden, inklusive den Daten, anschließend in
eine auf Vektoren spezialisierte Datenbank gespeichert. Vor jeder Anfrage an das
LLM werden nun aus dieser Datenbank nur noch die Informationen geladen, die für
die Anfrage relevant sind. Um zu erkennen, was relevant ist, wird auch die
Frage, oder Teile von dieser, in einen Vektor umgewandelt und anschließend sind
nur noch die Daten relevant, die in der Nähe dieses Vektors liegen.</p>
<p>Dementsprechend bringt Spring AI auch Unterstützung für das Berechnen dieser
Embeddings und die Verbindung zu den gängigen Vektordatenbanken mit. Um
etwa Fragen zu den deutschen Standorten von INNOQ zu beantworten
können wir diese in einer JSON-Datei ablegen (siehe Listing 8).</p>
<figure><div class="highlight" title="Listing 8: JSON-Datei mit Informationen"><pre class=""><code><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"zip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"40789"</span><span class="p">,</span><span class="w">
</span><span class="nl">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Monheim am Rhein"</span><span class="p">,</span><span class="w">
</span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Krischerstr. 100"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"zip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10999"</span><span class="p">,</span><span class="w">
</span><span class="nl">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Berlin"</span><span class="p">,</span><span class="w">
</span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ohlauer Str. 43"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"zip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"20537"</span><span class="p">,</span><span class="w">
</span><span class="nl">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Hamburg"</span><span class="p">,</span><span class="w">
</span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Wendenstraße 130"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"zip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"50672"</span><span class="p">,</span><span class="w">
</span><span class="nl">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Köln"</span><span class="p">,</span><span class="w">
</span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Spichernstraße 44"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"zip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"63067"</span><span class="p">,</span><span class="w">
</span><span class="nl">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Offenbach"</span><span class="p">,</span><span class="w">
</span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ludwigstr. 180 E"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"zip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"80331"</span><span class="p">,</span><span class="w">
</span><span class="nl">"city"</span><span class="p">:</span><span class="w"> </span><span class="s2">"München"</span><span class="p">,</span><span class="w">
</span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kreuzstr. 16"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span></code></pre></div>
<figcaption>Listing 8: JSON-Datei mit Informationen</figcaption></figure>
<p>Diese laden wir jetzt vor unserer Anfrage in einen <code>VectorStore</code>. Hierfür nutzen
wir in diesem Falle eine einfache arbeitsspeicherbasierte Implementierung (siehe
Listing 9).</p>
<figure><div class="highlight" title="Listing 9: Erzeugen und speichern der Embeddings im VectorStore"><pre class=""><code><span class="err">…</span>
<span class="nd">@Bean</span>
<span class="nc">VectorStore</span> <span class="nf">vectorStore</span><span class="o">(</span><span class="nc">EmbeddingClient</span> <span class="n">client</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">SimpleVectorStore</span><span class="o">(</span><span class="n">client</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Bean</span>
<span class="nd">@Order</span><span class="o">(-</span><span class="mi">1000</span><span class="o">)</span>
<span class="nc">CommandLineRunner</span> <span class="nf">load</span><span class="o">(</span><span class="n">i</span>
<span class="nc">VectorStore</span> <span class="n">store</span><span class="o">,</span>
<span class="nd">@Value</span><span class="o">(</span><span class="s">"classpath:/offices.json"</span><span class="o">)</span> <span class="nc">Resource</span> <span class="n">resource</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">args</span> <span class="o">-></span> <span class="o">{</span>
<span class="kt">var</span> <span class="n">documents</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">JsonReader</span><span class="o">(</span><span class="n">resource</span><span class="o">,</span>
<span class="s">"zip"</span><span class="o">,</span> <span class="s">"city"</span><span class="o">,</span> <span class="s">"address"</span><span class="o">)</span>
<span class="o">.</span><span class="na">get</span><span class="o">();</span>
<span class="n">store</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">documents</span><span class="o">);</span>
<span class="o">};</span>
<span class="o">}</span>
<span class="err">…</span></code></pre></div>
<figcaption>Listing 9: Erzeugen und speichern der Embeddings im VectorStore</figcaption></figure>
<p>Neben dem verwendeten <code>JsonReader</code> werden auch noch weitere
<a href="https://docs.spring.io/spring-ai/reference/api/etl-pipeline.html#_etl_interfaces_and_implementations">Implementierungen zur Extraktion von Daten</a> aus anderen Formaten
wie PDF mitgeliefert. Und natürlich können wir hier auch eigene schreiben, indem
wir das <code>DocumentReader</code>-Interface implementieren.</p>
<p>Zuletzt können wir den befüllten <code>VectorStore</code> jetzt auch bei der Abfrage des LLM
nutzen und die Dokumente, die zur Anfrage passen, als Kontext mit übergeben
(siehe Listing 10).</p>
<figure><div class="highlight" title="Listing 10: Nutzung der Embeddings für RAG"><pre class=""><code><span class="err">…</span>
<span class="nd">@Bean</span>
<span class="nc">CommandLineRunner</span> <span class="nf">chat</span><span class="o">(</span><span class="nc">VectorStore</span> <span class="n">store</span><span class="o">,</span> <span class="nc">ChatClient</span> <span class="n">client</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">args</span> <span class="o">-></span> <span class="o">{</span>
<span class="kt">var</span> <span class="n">city</span> <span class="o">=</span> <span class="s">"München"</span><span class="o">;</span>
<span class="kt">var</span> <span class="n">query</span> <span class="o">=</span> <span class="s">"Where is the %s INNOQ office located?"</span>
<span class="o">.</span><span class="na">formatted</span><span class="o">(</span><span class="n">city</span><span class="o">);</span>
<span class="kt">var</span> <span class="n">documents</span> <span class="o">=</span> <span class="n">store</span><span class="o">.</span><span class="na">similaritySearch</span><span class="o">(</span><span class="s">"city: "</span> <span class="o">+</span> <span class="n">city</span><span class="o">).</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="nl">Document:</span><span class="o">:</span><span class="n">getContent</span><span class="o">)</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="n">joining</span><span class="o">(</span><span class="s">"\n"</span><span class="o">));</span>
<span class="kt">var</span> <span class="n">systemMessage</span> <span class="o">=</span> <span class="sh">"""
You are a virtual assistant.
You are answering questions regarding the INNOQ offices provided within the DOCUMENTS paragraph.
You are only allowed to use information from the DOCUMENTS paragraph and no other information.
If you are not sure or don't know honestly state that you don't know.
DOCUMENTS:
{documents}
"""</span><span class="o">;</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">documents</span><span class="o">);</span>
<span class="kt">var</span> <span class="n">prompt</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Prompt</span><span class="o">(</span><span class="nc">List</span><span class="o">.</span><span class="na">of</span><span class="o">(</span>
<span class="k">new</span> <span class="nf">SystemPromptTemplate</span><span class="o">(</span><span class="n">systemMessage</span><span class="o">)</span>
<span class="o">.</span><span class="na">createMessage</span><span class="o">(</span><span class="nc">Map</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"documents"</span><span class="o">,</span> <span class="n">documents</span><span class="o">)),</span>
<span class="k">new</span> <span class="nf">UserMessage</span><span class="o">(</span><span class="n">query</span><span class="o">)));</span>
<span class="kt">var</span> <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="na">call</span><span class="o">(</span><span class="n">prompt</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span>
<span class="n">response</span><span class="o">.</span><span class="na">getResult</span><span class="o">().</span><span class="na">getOutput</span><span class="o">().</span><span class="na">getContent</span><span class="o">());</span>
<span class="o">};</span>
<span class="o">}</span>
<span class="err">…</span></code></pre></div>
<figcaption>Listing 10: Nutzung der Embeddings für RAG</figcaption></figure>
<p>Als Ergebnis antwortet unser LLM nun mit „Based on the information provided in
the DOCUMENTS paragraph, the München INNOQ office is located at address
Kreuzstr. 16 in city München and zip code 80331.“. Außerdem ist durch das
vorherige <code>System.out.println</code> auf der Standardausgabe auch zu sehen, dass nicht
alle, sondern nur die Informationen zu vier Standorten mit in den Kontext
gegeben wurden.</p>
<h3><a id="fazit" href="https://www.innoq.com/de/blog/2024/02/ollama-llm-spring-ai-rag/#fazit">Fazit</a></h3>
<p>In diesem Post haben wir uns gemeinsam angeschaut, wie wir unter Verwendung von
Ollama Large Language Models lokal nutzen können und wie sich diese mithilfe von
Spring AI in eine Spring Boot basierte Anwendung integrieren lassen.</p>
<p>Spring AI ist ein noch sehr frisches Projekt und hat erst vor wenigen Tagen mit
Version 0.8.0 ein <a href="https://spring.io/blog/2024/02/23/spring-ai-0-8-0-released">erstes Milestone Release veröffentlicht</a>. Da
die Entwicklung hier auch auf die sehr dynamische Situation der LLMs reagieren
muss, können sich die hier gezeigten Listings natürlich in Zukunft noch stark
verändern. Ich gehe jedoch nicht davon aus, dass das generelle Konzept noch
einmal komplett umgeworfen wird.</p>
<p>Neben der Anbindung von Ollama bietet uns Spring AI auch andere Möglichkeiten
wie Chat GPT oder das Azure Open AI an. Und neben der Unterstützung von Chat basierten LLMs gibt es auch eine
<a href="https://docs.spring.io/spring-ai/reference/api/imageclient.html">Abstraktion für die Generierung von Bildern</a>.</p>
<p>Dieser Post hat sich dabei darauf beschränkt, die grundlegenden Konzepte kurz und
vereinfacht zu erklären und die technische Integration in Spring Boot anhand
eines kleinen Beispiels zu zeigen. Um in einer realen Anwendung gute Ergebnisse
zu erzielen, müssten wir hauptsächlich in die Embeddings und den Prompt noch
deutlich mehr Energie investieren. Diese beiden Stellen sind beim Einsatz von
Retrieval Augmented Generation, neben der Wahl des richtigen Modells als Basis,
existenziell für die Qualität und somit für den gesamten Anwendungsfall.</p>
<p><em>Ein besonderer Dank geht an die Kolleg:innen, die fleißig Headerbilder generiert
oder den Artikel Korrektur gelesen haben.</em></p>
</body></html>
https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/2024-02-29T00:00:00+01:002024-03-12T13:56:11+01:00Rate Limiting with Spring BootMartin Weckmartin.weck@innoq.comhttps://www.innoq.com/en/staff/martin-weck/<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Let’s implement rate-limiting protection for your Spring Boot server without the need for any additional dependencies beyond those included in the Spring Boot Starter package.</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Rate limiting is an architectural tactic for a server to limit access to an API. It helps to:</p>
<ul>
<li>protect against server overload due to clients that call the server in a short time frame too often</li>
<li>increase the fairness of how clients use server resources</li>
<li>allow pricing schemes for different amounts of requests</li>
</ul>
<p>Spring Boot 3.0 does not provide rate limiting out of the box. In most cases, for you as an application developer, it might not be necessary to take care of rate-limiting as this might be addressed through your infrastructure, e.g. if you use a reverse proxy (like <a href="https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting">HA-Proxy</a>). The infrastructure might provide a global rate limit, while rate limiting at the application level provides just a local rate limit. So, if you have n instances and each has its own rate limiting then your actual limit might be n times higher. You may check <a href="https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/">this blog post</a> if you have multiple instances of your server.</p>
<p>However, adding rate limit protection to your application allows fine control per HTTP endpoint under the control of developers if the infrastructure is maintained by someone else. If you are looking for a small solution without additional dependencies for a single instance application, the solution suggested by this blog post might be an option.</p>
<h3><a id="theidea" href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/#theidea">The idea</a></h3>
<p>We define an annotation that you can add to any HTTP endpoint that should have a rate limit protection. We define an <a href="https://en.wikipedia.org/wiki/AspectJ">aspect</a> for methods with that annotation that counts HTTP requests per sender IP address. If the rate limit is exceeded we throw an exception. In the exception handling we return an <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#429">HTTP Status code of 429</a>. The rate limit configuration will be possible using properties in the Spring configuration file.</p>
<h3><a id="startingpoint:aplaincontroller" href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/#startingpoint:aplaincontroller">Starting Point: A plain Controller</a></h3>
<p>Let’s assume we have a Spring Boot Server, you have added the <a href="https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop">spring-boot-starter-aop 2.x</a> or higher dependency and you have an endpoint similar like this one:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.PostMapping</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.RequestBody</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.RequestMapping</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.RestController</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.innoq.test.WithRateLimitProtection</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.validation.Valid</span><span class="o">;</span>
<span class="nd">@RestController</span>
<span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/api"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyRestController</span>
<span class="nd">@PostMapping</span>
<span class="kd">public</span> <span class="nc">MyResponse</span> <span class="nf">processRequest</span><span class="o">(</span><span class="nd">@Valid</span> <span class="nd">@RequestBody</span> <span class="kd">final</span> <span class="nc">MyRequest</span> <span class="n">request</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">myService</span><span class="o">.</span><span class="na">process</span><span class="o">(</span><span class="n">request</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<p>Let’s assume, <code>MyResponse</code>, and <code>MyRequest</code> are simple <a href="https://en.wikipedia.org/wiki/JavaBeans">JavaBeans</a>. Spring Boot will use the <a href="https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html">ObjectMapper</a> of Jackson to translate the incoming JSON to an instance of <code>MyRequest</code>. After the call <code>MyResponse</code> will be translated to JSON again. You can omit the <code>@Valid</code> annotation if you are not using <a href="https://www.baeldung.com/spring-boot-bean-validation">Spring Boot Bean Validation</a>.</p>
<h3><a id="detectingtheratelimit" href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/#detectingtheratelimit">Detecting the rate limit</a></h3>
<p>We start by writing an annotation <code>@WithRateLimitProtection</code> that allows to mark HTTP endpoints like the above <code>processRequest</code> one that should have a rate limit protection. We define the annotation like this:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.lang.annotation.ElementType</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.lang.annotation.Retention</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.lang.annotation.RetentionPolicy</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.lang.annotation.Target</span><span class="o">;</span>
<span class="nd">@Retention</span><span class="o">(</span><span class="nc">RetentionPolicy</span><span class="o">.</span><span class="na">RUNTIME</span><span class="o">)</span>
<span class="nd">@Target</span><span class="o">(</span><span class="nc">ElementType</span><span class="o">.</span><span class="na">METHOD</span><span class="o">)</span>
<span class="kd">public</span> <span class="nd">@interface</span> <span class="nc">WithRateLimitProtection</span> <span class="o">{</span>
<span class="o">}</span></code></pre></div></figure>
<p>Now, we can add this annotation to the controller that should have a rate limit protection:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="nd">@RestController</span>
<span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/api"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyRestController</span>
<span class="nd">@PostMapping</span>
<span class="nd">@WithRateLimitProtection</span>
<span class="kd">public</span> <span class="nc">MyResponse</span> <span class="nf">processRequest</span><span class="o">(</span><span class="nd">@Valid</span> <span class="nd">@RequestBody</span> <span class="kd">final</span> <span class="nc">MyRequest</span> <span class="n">request</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">myService</span><span class="o">.</span><span class="na">process</span><span class="o">(</span><span class="n">request</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<p>If the rate limit is exceeded at the endpoint, a <code>RateLimitException</code> should be thrown that we define like this:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test.exceptions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.http.HttpStatus</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.ResponseStatus</span><span class="o">;</span>
<span class="nd">@ResponseStatus</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="nc">HttpStatus</span><span class="o">.</span><span class="na">TOO_MANY_REQUESTS</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RateLimitException</span> <span class="kd">extends</span> <span class="nc">RuntimeException</span> <span class="o">{</span>
<span class="kd">public</span> <span class="nf">RateLimitException</span><span class="o">(</span><span class="kd">final</span> <span class="nc">String</span> <span class="n">message</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">super</span><span class="o">(</span><span class="n">message</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">ApiErrorMessage</span> <span class="nf">toApiErrorMessage</span><span class="o">(</span><span class="kd">final</span> <span class="nc">String</span> <span class="n">path</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">ApiErrorMessage</span><span class="o">(</span><span class="nc">HttpStatus</span><span class="o">.</span><span class="na">TOO_MANY_REQUESTS</span><span class="o">.</span><span class="na">value</span><span class="o">(),</span> <span class="nc">HttpStatus</span><span class="o">.</span><span class="na">TOO_MANY_REQUESTS</span><span class="o">.</span><span class="na">name</span><span class="o">(),</span> <span class="k">this</span><span class="o">.</span><span class="na">getMessage</span><span class="o">(),</span> <span class="n">path</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<p>where <code>ApiErrorMessage</code> will be translated to a JSON body in the response, such that our JSON API answers with JSON also in case of error and not with the Spring default, i.e. an HTML page:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.fasterxml.jackson.annotation.JsonInclude</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.Clock</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.LocalDateTime</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.UUID</span><span class="o">;</span>
<span class="nd">@JsonInclude</span><span class="o">(</span><span class="nc">JsonInclude</span><span class="o">.</span><span class="na">Include</span><span class="o">.</span><span class="na">NON_EMPTY</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">ApiErrorMessage</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="no">UUID</span> <span class="n">id</span> <span class="o">=</span> <span class="no">UUID</span><span class="o">.</span><span class="na">randomUUID</span><span class="o">();</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">status</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">error</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">message</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">LocalDateTime</span> <span class="n">timestamp</span> <span class="o">=</span> <span class="nc">LocalDateTime</span><span class="o">.</span><span class="na">now</span><span class="o">(</span><span class="nc">Clock</span><span class="o">.</span><span class="na">systemUTC</span><span class="o">());</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">String</span> <span class="n">path</span><span class="o">;</span>
<span class="kd">public</span> <span class="no">UUID</span> <span class="nf">getId</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">getStatus</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">status</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getError</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">error</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getMessage</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">message</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">LocalDateTime</span> <span class="nf">getTimestamp</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">timestamp</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getPath</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">path</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<p>Let’s define an aspect that implements the rate limiting using <a href="https://docs.spring.io/spring-framework/reference/core/aop.html">Spring AOP</a>. The aspect is called before the marked endpoint method is called:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.innoq.test.exceptions.RateLimitException</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.aspectj.lang.annotation.Aspect</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.aspectj.lang.annotation.Before</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.beans.factory.annotation.Value</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.stereotype.Component</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.context.request.RequestContextHolder</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.context.request.ServletRequestAttributes</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.ArrayList</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.List</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.concurrent.ConcurrentHashMap</span><span class="o">;</span>
<span class="nd">@Aspect</span>
<span class="nd">@Component</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RateLimitAspect</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">ERROR_MESSAGE</span> <span class="o">=</span> <span class="s">"To many request at endpoint %s from IP %s! Please try again after %d milliseconds!"</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">ConcurrentHashMap</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span> <span class="nc">List</span><span class="o"><</span><span class="nc">Long</span><span class="o">>></span> <span class="n">requestCounts</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ConcurrentHashMap</span><span class="o"><>();</span>
<span class="nd">@Value</span><span class="o">(</span><span class="s">"${APP_RATE_LIMIT:#{200}}"</span><span class="o">)</span>
<span class="kd">private</span> <span class="kt">int</span> <span class="n">rateLimit</span><span class="o">;</span>
<span class="nd">@Value</span><span class="o">(</span><span class="s">"${APP_RATE_DURATIONINMS:#{60000}}"</span><span class="o">)</span>
<span class="kd">private</span> <span class="kt">long</span> <span class="n">rateDuration</span><span class="o">;</span>
<span class="cm">/**
* Executed by each call of a method annotated with {@link WithRateLimitProtection} which should be an HTTP endpoint.
* Counts calls per remote address. Calls older than {@link #rateDuration} milliseconds will be forgotten. If there have
* been more than {@link #rateLimit} calls within {@link #rateDuration} milliseconds from a remote address, a {@link RateLimitException}
* will be thrown.
* @throws RateLimitException iff rate limit for a given remote address has been exceeded
*/</span>
<span class="nd">@Before</span><span class="o">(</span><span class="s">"@annotation(com.innoq.test.WithRateLimitProtection)"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">rateLimit</span><span class="o">()</span> <span class="o">{</span>
<span class="kd">final</span> <span class="nc">ServletRequestAttributes</span> <span class="n">requestAttributes</span> <span class="o">=</span> <span class="o">(</span><span class="nc">ServletRequestAttributes</span><span class="o">)</span> <span class="nc">RequestContextHolder</span><span class="o">.</span><span class="na">currentRequestAttributes</span><span class="o">();</span>
<span class="kd">final</span> <span class="nc">String</span> <span class="n">key</span> <span class="o">=</span> <span class="n">requestAttributes</span><span class="o">.</span><span class="na">getRequest</span><span class="o">().</span><span class="na">getRemoteAddr</span><span class="o">();</span>
<span class="kd">final</span> <span class="kt">long</span> <span class="n">currentTime</span> <span class="o">=</span> <span class="nc">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span>
<span class="n">requestCounts</span><span class="o">.</span><span class="na">putIfAbsent</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o"><>());</span>
<span class="n">requestCounts</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">key</span><span class="o">).</span><span class="na">add</span><span class="o">(</span><span class="n">currentTime</span><span class="o">);</span>
<span class="n">cleanUpRequestCounts</span><span class="o">(</span><span class="n">currentTime</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">requestCounts</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">key</span><span class="o">).</span><span class="na">size</span><span class="o">()</span> <span class="o">></span> <span class="n">rateLimit</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">RateLimitException</span><span class="o">(</span><span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="no">ERROR_MESSAGE</span><span class="o">,</span> <span class="n">requestAttributes</span><span class="o">.</span><span class="na">getRequest</span><span class="o">().</span><span class="na">getRequestURI</span><span class="o">(),</span> <span class="n">key</span><span class="o">,</span> <span class="n">rateDuration</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">cleanUpRequestCounts</span><span class="o">(</span><span class="kd">final</span> <span class="kt">long</span> <span class="n">currentTime</span><span class="o">)</span> <span class="o">{</span>
<span class="n">requestCounts</span><span class="o">.</span><span class="na">values</span><span class="o">().</span><span class="na">forEach</span><span class="o">(</span><span class="n">l</span> <span class="o">-></span> <span class="o">{</span>
<span class="n">l</span><span class="o">.</span><span class="na">removeIf</span><span class="o">(</span><span class="n">t</span> <span class="o">-></span> <span class="n">timeIsTooOld</span><span class="o">(</span><span class="n">currentTime</span><span class="o">,</span> <span class="n">t</span><span class="o">));</span>
<span class="o">});</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">timeIsTooOld</span><span class="o">(</span><span class="kd">final</span> <span class="kt">long</span> <span class="n">currentTime</span><span class="o">,</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">timeToCheck</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">currentTime</span> <span class="o">-</span> <span class="n">timeToCheck</span> <span class="o">></span> <span class="n">rateDuration</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<p>The <code>@Before</code> annotation, aka advice, informs <a href="https://docs.spring.io/spring-framework/reference/core/aop/choosing.html">AspectJ</a> to call this method before the <code>WithRateLimitProtection</code> annotated method is executed. We use Spring’s <code>RequestContextHolder</code> to fetch information on the request that called the annotated endpoint. We are especially interested in the remote address and we obtain the current time in milliseconds from the system. Per remote address, we maintain a list of current times that we store in a <code>ConcurrentHashMap</code> in RAM. In my use case, there was only a limited group of calling remote addresses, and time stamps that are older than <code>rateDuration</code> are deleted through <code>cleanUpRequestCounts</code> in each call so that we are not running out of memory here. If <code>requestCounts</code> contains for the current remote address more entries than <code>rateLimit</code> we throw a <code>RateLimitException</code> with information who has violated the rate limit. To configure the rate limit, you add in your <code>application.properties</code> or <code>application.yml</code>:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="py">app.rate.limit</span><span class="p">=</span><span class="s">200</span>
<span class="py">app.rate.durationinms</span><span class="p">=</span><span class="s">60000</span></code></pre></div></figure>
<p>So we allow up to 200 calls per minute from the same remote address. As usual with Spring Boot, you can override the values using also system environment variable, e.g. <code>export APP_RATE_LIMIT=100</code>. Moreover, we have hard-coded a default in the Java code if those properties have not been specified.</p>
<h3><a id="handlingratelimitexceptions" href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/#handlingratelimitexceptions">Handling Rate Limit Exceptions</a></h3>
<p>So far, having written the code above, a <code>RateLimitException</code> will be thrown if there are too many requests. We need to add exception handling in Spring that translates the thrown <code>RateLimitException</code> to an HTTP 429 response with some information on the error in its response body. For that, we write an exception handler:</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="kn">package</span> <span class="nn">com.innoq.test.exceptions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.slf4j.Logger</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.slf4j.LoggerFactory</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.core.Ordered</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.core.annotation.Order</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.http.HttpStatus</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.http.ResponseEntity</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.ControllerAdvice</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.ExceptionHandler</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.servlet.http.HttpServletRequest</span><span class="o">;</span>
<span class="nd">@ControllerAdvice</span>
<span class="nd">@Order</span><span class="o">(</span><span class="nc">Ordered</span><span class="o">.</span><span class="na">HIGHEST_PRECEDENCE</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RateLimitExceptionHandler</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Logger</span> <span class="no">LOG</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span><span class="nc">RateLimitExceptionHandler</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="nd">@ExceptionHandler</span><span class="o">(</span><span class="nc">RateLimitException</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">ResponseEntity</span><span class="o"><</span><span class="nc">ApiErrorMessage</span><span class="o">></span> <span class="nf">handleInvalidFieldsInValidJson</span><span class="o">(</span><span class="kd">final</span> <span class="nc">RateLimitException</span> <span class="n">rateLimitException</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">HttpServletRequest</span> <span class="n">request</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">final</span> <span class="nc">ApiErrorMessage</span> <span class="n">apiErrorMessage</span> <span class="o">=</span> <span class="n">rateLimitException</span><span class="o">.</span><span class="na">toApiErrorMessage</span><span class="o">(</span><span class="n">request</span><span class="o">.</span><span class="na">getRequestURI</span><span class="o">());</span>
<span class="n">logIncomingCallException</span><span class="o">(</span><span class="n">rateLimitException</span><span class="o">,</span> <span class="n">apiErrorMessage</span><span class="o">);</span>
<span class="k">return</span> <span class="k">new</span> <span class="nc">ResponseEntity</span><span class="o"><>(</span><span class="n">apiErrorMessage</span><span class="o">,</span> <span class="nc">HttpStatus</span><span class="o">.</span><span class="na">TOO_MANY_REQUESTS</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">logIncomingCallException</span><span class="o">(</span><span class="kd">final</span> <span class="nc">RateLimitException</span> <span class="n">rateLimitException</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">ApiErrorMessage</span> <span class="n">apiErrorMessage</span><span class="o">)</span> <span class="o">{</span>
<span class="no">LOG</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"%s: %s"</span><span class="o">,</span> <span class="n">apiErrorMessage</span><span class="o">.</span><span class="na">getId</span><span class="o">(),</span> <span class="n">rateLimitException</span><span class="o">.</span><span class="na">getMessage</span><span class="o">()),</span> <span class="n">rateLimitException</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div></figure>
<h3><a id="conclusion" href="https://www.innoq.com/en/blog/2024/02/rate-limiting-with-spring-boot/#conclusion">Conclusion</a></h3>
<p>This blog post has shown how you limit requests per remote address for a Spring Boot server using just <a href="https://docs.spring.io/spring-framework/reference/core/aop.html">Spring AOP</a>. If your requirements do not allow you to use the small simple solution above and you can add dependencies, then you may check out <a href="https://resilience4j.readme.io/docs/ratelimiter">Resilience4J</a> as an alternative. Alternatively, if you have multiple instances of your server, it might be interesting also to read <a href="https://www.innoq.com/en/blog/2024/03/distributed-rate-limiting-with-spring-boot-and-redis/">this blog post</a>, which describes a solution with bucket4j for distributed rate limiting.</p>
</body></html>
https://www.innoq.com/en/talks/2024/02/remote-mob-programming-at-home-but-not-alone/2024-02-13T00:00:00+01:002024-02-27T14:46:15+01:00Joshua Töpferjoshua.toepfer@innoq.comhttps://www.innoq.com/en/staff/joshua-toepfer/Talk: "Remote Mob Programming - At home but not alone " by Joshua Töpfer — 2024-02-27 Web Engineering Düsseldorf (Düsseldorf)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>The whole team attends an online meeting and develops together. One person types the code, the others discuss it. Sounds unusual? That’s Remote Mob Programming, an exciting way of working for distributed teams. Joshua Töpfer has been working full-time in a remote mob for over 3 years and wouldn’t want to work any other way. In this talk, you’ll learn what it’s all about, what the pros and cons of this methodology are, and how you can find out if this methodology is something for your team.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>The whole team attends an online meeting and develops together. One person types the code, the others discuss it. Sounds unusual? That’s Remote Mob Programming, an exciting way of working for distributed teams. Joshua Töpfer has been working full-time in a remote mob for over 3 years and wouldn’t want to work any other way. In this talk, you’ll learn what it’s all about, what the pros and cons of this methodology are, and how you can find out if this methodology is something for your team.</p>
</body></html>
https://www.innoq.com/en/blog/2024/02/compacted-state-feed/2024-02-27T00:00:00+01:002024-02-27T11:28:19+01:00Compacted State FeedsJochen Christjochen.christ@innoq.comhttps://www.innoq.com/en/staff/jochen-christ/<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Event notification vs. Event-carried State Transfer vs. Delta loads. How to design good event architectures for mutable data?</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>In our projects, we very often get the question, how to design messages for exchanging data with other teams over asynchronous technologies, such as Kafka.</p>
<p>When looking at data, it basically comes down to these archetypes:</p>
<ul>
<li>
<strong>Domain Events</strong>. The fact that something business-important has happened at a specific time, e.g., a customer placed an order in the online shop or a shipment has been delivered. Events are immutable. They have been happened in the past and the information does not change. Well, except for technical reasons, such as code bugs, latency issues, and similar.</li>
<li>
<strong>State</strong>. State refers to the value of business entities that change over time. E.g., prices are updated, a customer updated their credit card number, the weather forecast has changed. When data of a business entity changes, it gets a new state.</li>
</ul>
<p>The technical representation of <em>domain events</em> in an asynchronous messaging system is pretty straightforward (put the ID, the event type, and the timestamp in the payload, make it bitemporal, make it idempotent). And for business modeling, we have collaborative techniques, such as event storming.</p>
<p>So, in this article, I want to focus on best practices in how to build a feed with mutable entities.</p>
<h2 id="designingcompactedstatefeeds">Designing Compacted State Feeds</h2>
<p>When managed data is published at a defined place for consumers to subscribe, we call it a <em>feed</em>.</p>
<p>My recommended design for exchanging mutable data with state looks like this:</p>
<ul>
<li><strong>Define a managed model of your entity,</strong></li>
<li><strong>with a defined business key.</strong></li>
<li><strong>Whenever the state changes,</strong></li>
<li><strong>publish a full snapshot of the current state </strong></li>
<li><strong>on a topic with unlimited retention</strong></li>
<li><strong>and (optionally) compact on the business key.</strong></li>
<li><strong>Initially, make a full load with all business keys.</strong></li>
<li><strong>Define tombstone messages for deleted entities referring to the business key.</strong></li>
</ul>
<p>It is also a good idea, to add a reference to the domain event that triggered the state change.</p>
<p>It is important, that the full current state is included into the message, also known as <a href="https://martinfowler.com/articles/201701-event-driven.html">Event-Carried State Transfer</a>. So, no context or historic messages are needed to build the current state for a consumer.
If the full snapshot of the current state does not fit into the payload of the message, it is OK to add a link to retrieve the payload via a GET request.</p>
<p>This architecture requires a component that supports persistent streams (unlimited retention), such as <a href="https://kafka.apache.org">Apache Kafka</a>, <a href="https://www.http-feeds.org/">HTTP-Feeds</a>, <a href="https://docs.nats.io/nats-concepts/jetstream">NATS JetStream</a>, or <a href="https://rabbitmq-website.pages.dev/docs/streams">RabbitMQ Streams</a>. </p>
<p>Compaction, also known as deduplication, is a method to delete older entries for one business key. It ensures, that the state of every business entity remains on the topic at least once. This keeps the topic small and reduces the time and inconsistency for consumers that need to read the topic from the beginning.</p>
<p>When an entity gets deleted, we need to notify our consumers to delete it as well. We need the business key and the fact, that it has been deleted. We call this a <em>tombstone message</em>.</p>
<h2 id="example">Example</h2>
<p>In this example, the inventory is published. Whenever the quantity of an article changes, a new message is added to the feed. The business key of an article is the <em>SKU</em>, which is defined as <em>subject</em> in the header.</p>
<figure><div class="highlight" title=""><pre class=""><code><span class="p">[{</span><span class="w">
</span><span class="nl">"specversion"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"org.http-feeds.example.inventory"</span><span class="p">,</span><span class="w">
</span><span class="nl">"source"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://example.http-feeds.org/inventory"</span><span class="p">,</span><span class="w">
</span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"1c6b8c6e-d8d0-4a91-b51c-1f56bd04c758"</span><span class="p">,</span><span class="w">
</span><span class="nl">"time"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-01-01T00:00:01Z"</span><span class="p">,</span><span class="w">
</span><span class="nl">"subject"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"9521234567899"</span><span class="p">,</span><span class="w">
</span><span class="nl">"event"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"org.http-feeds.example.events.webshop.order"</span><span class="p">,</span><span class="w">
</span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"sku"</span><span class="p">:</span><span class="w"> </span><span class="s2">"9521234567899"</span><span class="p">,</span><span class="w">
</span><span class="nl">"updated"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-01-01T00:00:01Z"</span><span class="p">,</span><span class="w">
</span><span class="nl">"quantity"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},{</span><span class="w">
</span><span class="nl">"specversion"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"org.http-feeds.example.inventory"</span><span class="p">,</span><span class="w">
</span><span class="nl">"source"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://example.http-feeds.org/inventory"</span><span class="p">,</span><span class="w">
</span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"292042fb-ab04-4653-af90-19a24032bffe"</span><span class="p">,</span><span class="w">
</span><span class="nl">"time"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-12-01T00:00:15Z"</span><span class="p">,</span><span class="w">
</span><span class="nl">"subject"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"9521234512349"</span><span class="p">,</span><span class="w">
</span><span class="nl">"event"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"org.http-feeds.example.events.logistics.nofind"</span><span class="p">,</span><span class="w">
</span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"sku"</span><span class="p">:</span><span class="w"> </span><span class="s2">"9521234512349"</span><span class="p">,</span><span class="w">
</span><span class="nl">"updated"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-01-01T00:00:12Z"</span><span class="p">,</span><span class="w">
</span><span class="nl">"quantity"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},{</span><span class="w">
</span><span class="nl">"specversion"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"org.http-feeds.example.inventory"</span><span class="p">,</span><span class="w">
</span><span class="nl">"source"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://example.http-feeds.org/inventory"</span><span class="p">,</span><span class="w">
</span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"fa3e2a22-398c-4d02-ad08-9415e43178e6"</span><span class="p">,</span><span class="w">
</span><span class="nl">"time"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-01-01T00:00:22Z"</span><span class="p">,</span><span class="w">
</span><span class="nl">"subject"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"9521234567899"</span><span class="p">,</span><span class="w">
</span><span class="nl">"event"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"org.http-feeds.example.events.branch.sale"</span><span class="p">,</span><span class="w">
</span><span class="nl">"data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"sku"</span><span class="p">:</span><span class="w"> </span><span class="s2">"9521234567899"</span><span class="p">,</span><span class="w">
</span><span class="nl">"updated"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-01-01T00:00:21Z"</span><span class="p">,</span><span class="w">
</span><span class="nl">"quantity"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"specversion"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"org.http-feeds.example.inventory"</span><span class="p">,</span><span class="w">
</span><span class="nl">"source"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://example.http-feeds.org/inventory"</span><span class="p">,</span><span class="w">
</span><span class="nl">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"06b13630-e4c3-4d85-a669-ce66fc4daa75"</span><span class="p">,</span><span class="w">
</span><span class="nl">"time"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-12-31T00:00:01Z"</span><span class="p">,</span><span class="w">
</span><span class="nl">"subject"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"9521234567899"</span><span class="p">,</span><span class="w">
</span><span class="nl">"event"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"org.http-feeds.example.events.articles.unlisted"</span><span class="p">,</span><span class="w">
</span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DELETE"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span></code></pre></div></figure>
<p>This example uses <a href="https://cloudevents.io/">CloudEvents</a> event format.</p>
<p>The first message will be deleted after the next compaction run, as a newer message with the same subject <code>9521234567899</code> was added to the feed.</p>
<p>The last message is a tombstone message that signals a delete event, so the previous message will also be deleted after compaction.</p>
<h2 id="alternativesandtheirdrawbacks">Alternatives and their Drawbacks</h2>
<p>We have seen some patterns quite often, which all come with drawbacks:</p>
<ul>
<li>Publish Diffs</li>
<li>Event sourcing</li>
<li>Daily snapshots + delta loads</li>
<li>Change data capture (CDC) on database tables</li>
</ul>
<p>Publishing only the properties of an entity which have changed, the <em>diffs</em>, is usually a suboptimal idea, as they require distributing business logic to the consumers for building the current state. And, they require that you need to keep your history forever.</p>
<p>Sometimes, we see people to call this pattern <em>Event Sourcing</em>. Event sourcing is an internal persistence strategy that should not be used for cross-system data sharing. Also, using Domain Events for Event Sourcing is generally not a good idea (see <a href="https://www.innoq.com/en/blog/2019/01/domain-events-versus-event-sourcing/">Domain Events vs. Event Sourcing</a>)</p>
<p>Daily snapshots and real-time delta feeds seem natural and work. However, they are unnecessarily complex, as you need to pause delta loading when you process a snapshot. You might be out of sync at that period. With compacted state feeds, the separation of snapshots and delta loads is simply not needed.</p>
<p>CDC uses on actual databases. Not great, as we often want to abstract the logical model from the technical model to have the flexibility for changes. However, for legacy applications, this might be a feasible option.</p>
<h2 id="benefits">Benefits</h2>
<p>Feeds are the connecting asynchronous interfaces in complex software systems and the foundation of a scaled event architectures.</p>
<p>An overview of the benefits of compacted state feeds :</p>
<ul>
<li>
<strong>Real-time updates.</strong> Updates are shared in near real-time over asynchronous systems with all consumers.</li>
<li>
<strong>Complete data sets.</strong> All business entities, identified by their business keys, are available in a topic.</li>
<li>
<strong>Replayable.</strong> New consumers can consume the feed from the beginning and start building their read model.</li>
<li>
<strong>Consistent.</strong> With compaction, outdated data gets removed from the feed. Replaying does not need to handle old data or schemas.</li>
<li>
<strong>Evolvable.</strong> When the schema changes, it is sufficient to publish an update for every entity in the feed. No need to mitigate schema changes forever.</li>
<li>
<strong>GDPR-compliant.</strong> The payload of deleted entities really gets deleted in the feed after a compaction run. Downstream consumers have a mechanism to trigger deletions as well.</li>
</ul>
<p>Compacted state feeds combine the benefits of full snapshots and real-time delta processing for mutable business entities while keeping the complexity moderate. It can be implemented based on available technologies, such as Kafka or HTTP endpoints.</p>
</body></html>
https://www.innoq.com/de/articles/2024/02/es-lebe-die-buerokratie-soziotechnische-welten-teil-10/2024-02-22T00:00:00+01:002024-02-23T09:10:39+01:00Es lebe die Bürokratie! Gerrit Beinegerrit.beine@innoq.comhttps://www.innoq.com/en/staff/?person=gbeine<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Die Digitalisierung hilft, Bürokratie zu reduzieren, glaubt man. Warum das so einfach nicht ist, erklärt diese Kolumne.</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<ul>
<li><em>Teil 1: <a href="https://www.innoq.com/de/articles/2022/03/die-angemessenheit-von-komplexitaet">Die Angemessenheit von Komplexität</a></em></li>
<li><em>Teil 2: <a href="https://www.innoq.com/de/articles/2022/03/der-foerster-und-die-softwarearchitektur">Der Foerster und die Softwarearchitektur</a></em></li>
<li><em>Teil 3: <a href="https://www.innoq.com/de/articles/2022/03/mythos-teamautonomie">Mythos Teamautonomie</a></em></li>
<li><em>Teil 4: <a href="https://www.innoq.com/de/articles/2022/06/autonomie-und-entscheidungen">Autonomie und Entscheidungen</a></em></li>
<li><em>Teil 5: <a href="https://www.innoq.com/de/articles/2022/06/ich-du-conways-law/">Ich, Du und Conway’s Law</a></em></li>
<li><em>Teil 6: <a href="https://www.innoq.com/de/articles/2022/08/conway-hat-immer-recht/">Conway hat immer Recht</a></em></li>
<li><em>Teil 7: <a href="https://www.innoq.com/de/articles/2023/06/illegale-softwarearchitekturen/">Illegale Softwarearchitekturen</a></em></li>
<li><em>Teil 8: <a href="https://www.innoq.com/de/articles/2023/07/paradoxical-safety/">Paradoxical Safety</a></em></li>
<li><em>Teil 9: <a href="https://www.innoq.com/de/articles/2023/10/new-work-taylorismus/">New Work: Taylorismus 2.0</a></em></li>
<li><em>Teil 10: Es lebe die Bürokratie! (dieser Artikel)</em></li>
</ul>
<p>Seit einiger Zeit erobert ein neuer Begriff die Welt der Softwareentwicklung: Soziotechnische Systeme sind das neue Buzzword, mit dem vieles zu erklären versucht wird.</p>
<p>Damit dieser Erklärungen funktionieren, muss aber klar sein, was ein soziotechnisches System ist und wie es sich von einem technischen oder einem sozialen System unterscheidet. Um es mit den Worten von Niklas Luhmann zu formulieren: Was ist bei einem soziotechnischen System der Unterschied, der einen
Unterschied macht?</p>
<p>Die Idee der soziotechnischen Systeme ist schon sehr alt, Grundgedanken können bis zu Karl Marx zurückverfolgt werden, der verschiedene Aspekte in seinem Maschinenfragment beleuchtet hat. Ebenso haben Frederick W. Taylor und Henry Ford mit ihren Überlegungen dazu beigetragen.</p>
<h3><a id="dassoziotechnischesystemdekonstruiert" href="https://www.innoq.com/de/articles/2024/02/es-lebe-die-buerokratie-soziotechnische-welten-teil-10/#dassoziotechnischesystemdekonstruiert">Das soziotechnische System dekonstruiert</a></h3>
<p>Der Begriff wurde aber erst in den 1950ern im Kontext des Bergbaus in Großbritannien geprägt. Frederick E. Emery und Eric L. Trist beschreiben den Kern eines soziotechnischen Systems als Arbeitssystem, bei dem ein Teil als technisches System und ein weiterer Teil als soziales System betrachtet werden kann:</p>
<ul>
<li>Das <em>technische System</em> kann eine Maschine sein oder heutzutage auch Software.</li>
<li>Das <em>soziale System</em> beschreibt in diesem Fall die Kommunikation, die zwischen handelnden Personen in einem Arbeitskontext besteht.</li>
</ul>
<figure>
<img alt="Abb.1: Das Arbeitssystem als soziotechnisches System, Darstellung nach: https://de.wikipedia.org/wiki/Soziotechnisches_System" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/wu3ft6se0cg3sidwhgljc8o16g3v 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/wu3ft6se0cg3sidwhgljc8o16g3v 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/wu3ft6se0cg3sidwhgljc8o16g3v 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/wu3ft6se0cg3sidwhgljc8o16g3v 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/wu3ft6se0cg3sidwhgljc8o16g3v 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/wu3ft6se0cg3sidwhgljc8o16g3v 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/wu3ft6se0cg3sidwhgljc8o16g3v 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/wu3ft6se0cg3sidwhgljc8o16g3v 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/wu3ft6se0cg3sidwhgljc8o16g3v 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Abb.1: Das Arbeitssystem als soziotechnisches System, Darstellung nach: https://de.wikipedia.org/wiki/Soziotechnisches_System</figcaption></figure>
<p>Von einem soziotechnischen System lässt sich nur dann sprechen, wenn diese beiden Subsysteme untrennbar miteinander verbunden sind, also das eine nicht ohne das andere als System existieren kann (siehe dazu Artikel: <a href="https://www.innoq.com/de/articles/2022/06/ich-du-conways-law/">„Ich, Du und Conway’s Law“</a>). Im Folgenden soll als weitere Einschränkung gelten, dass soziotechnische Systeme nur in ihrer ursprünglichen Form, nämlich als Arbeitssysteme, betrachtet werden.</p>
<p>Das Modell eines soziotechnischen Systems lässt sich relativ einfach darstellen: Es gibt <em>Mitglieder</em>, das sind Personen, die innerhalb des soziotechnisches Systems tätig sind. Jedes Mitglied hat eine Rolle innerhalb der sozialen Struktur, mit der verschiedene <em>Aufgaben</em> verbunden sind. Damit diese Aufgaben erfüllt werden können, wird eine <em>Technologie</em> verwendet, die die Aufgabenerfüllung effizienter, einfacher oder sicherer machen kann (siehe Abbildung 1). Zwischen allen vier Elementen entstehen Wechselwirkungen, die sie aneinanderbinden: Wird eines der Elemente aus dem soziotechnischen System entfernt, hört das Arbeitssystem auf, zu funktionieren.</p>
<p>Zwar lassen sich grob die Mitglieder und ihre Rollen in das soziale Subsystem einordnen und die Aufgaben und Technologien in das technische Subsystem, allerdings ist diese Aufteilung als Modell zu verstehen.</p>
<h3><a id="vonderbürokratiezursoziotechnik" href="https://www.innoq.com/de/articles/2024/02/es-lebe-die-buerokratie-soziotechnische-welten-teil-10/#vonderb%C3%BCrokratiezursoziotechnik">Von der Bürokratie zur Soziotechnik</a></h3>
<p>Um zu diskutieren, was soziotechnische Systeme mit Bürokratie zu tun haben, ist es notwendig, diese zunächst als eine strukturierte Möglichkeit zu betrachten, kognitive Prozesse verteilt zu organisieren.</p>
<p>Bürokratie erlaubt es Menschen, in einer Organisationsform zusammenzuarbeiten, die Denkleistungen auf mehrere Köpfe verteilt. Wie gut das funktioniert, war schon im alten Rom bekannt. Bürokratie kann heute – trotz aller Unzulänglichkeiten – als eines der Fundamente der Zivilisation gesehen werden. Dabei beschränkt sich Bürokratie nicht auf staatliches oder öffentliches Handeln, sondern findet auch in privaten Organisationen statt. Dort wird die Bürokratie nur als Management bezeichnet, die soziale Funktion ist aber identisch.</p>
<p>Die Technisierung macht auch vor der Bürokratie nicht halt und so wundert es nicht, dass die ersten Versuche der Maschinisierung von Bürokratie bereits Anfang des 18. Jahrhunderts mit der Entwicklung von Schreibmaschinen unternommen wurden <a href="https://www.innoq.com/de/articles/2024/02/es-lebe-die-buerokratie-soziotechnische-welten-teil-10/#fn:1" id="fnref:1" title="see footnote" class="footnote">[1]</a>. Neben Schreibmaschinen können auch einfache Dinge wie Stempel, vorgedruckte Formulare oder ein Rohrpostsystem im Haus als Technologie betrachtet werden, die das Arbeitssystem Bürokratie in ein soziotechnisches System transformieren.</p>
<p>Die unvermeidbare Folge davon ist, dass sich Bürokratie bürokratischer anfühlt.</p>
<p>Die Erklärung dafür ist die Verschiebung von Kommunikation zwischen den Subsystemen. In der nicht-technisierten Bürokratie trat Kommunikation als ein Input auf, auf den die Bürokratie reagieren musste. Das konnte sie anhand sozialer Normen und Erwartungen (im Folgenden als „Social Contracts“ bezeichnet) tun und war in der Lage, durch die Kommunikation im sozialen System verschiedene Aspekte spontan einzubeziehen – oder auch auszublenden.</p>
<figure>
<img alt="Abb.2: Software Engineering übersetzt Social Contracts aus dem sozialen Subsystem in Software im technischen Subsystem" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/wssj6fudnmzejcjviu9v9m4teqrx 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/wssj6fudnmzejcjviu9v9m4teqrx 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/wssj6fudnmzejcjviu9v9m4teqrx 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/wssj6fudnmzejcjviu9v9m4teqrx 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/wssj6fudnmzejcjviu9v9m4teqrx 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/wssj6fudnmzejcjviu9v9m4teqrx 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/wssj6fudnmzejcjviu9v9m4teqrx 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/wssj6fudnmzejcjviu9v9m4teqrx 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/wssj6fudnmzejcjviu9v9m4teqrx 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Abb.2: Software Engineering übersetzt Social Contracts aus dem sozialen Subsystem in Software im technischen Subsystem</figcaption></figure>
<p>Diese Social Contracts konnten sich mit dem sozialen Subsystem weiterentwickeln und schnell an externe Faktoren anpassen. Damit war die Bürokratie in der Lage, einen akzeptablen Output zu erzeugen <a href="https://www.innoq.com/de/articles/2024/02/es-lebe-die-buerokratie-soziotechnische-welten-teil-10/#fn:2" id="fnref:2" title="see footnote" class="footnote">[2]</a>.</p>
<p>Die Technisierung der Bürokratie verschiebt nun Social Contracts aus dem sozialen Subsystem in das technische Subsystem und schreibt sie in Form von Technologie fest. Das erhöht einerseits die Effizienz der Bürokratie bei der Behandlung sämtlicher Standardinputs, erschwert aber das Handeln in Social Contracts, die die Technologie nicht adaptiert hat, weil sie vielleicht erst im Laufe der Zeit entstehen.</p>
<h3><a id="mehrdigitalisierungmehrbürokratie" href="https://www.innoq.com/de/articles/2024/02/es-lebe-die-buerokratie-soziotechnische-welten-teil-10/#mehrdigitalisierungmehrb%C3%BCrokratie">Mehr Digitalisierung, mehr Bürokratie</a></h3>
<p>Fast alle Menschen machen irgendwann in ihrem Leben die Erfahrung, dass eine andere Person ihnen sagt: „Tut mir leid, das kann ich nicht tun. Der Computer lässt das nicht zu.“
Dieser Satz ist die unmittelbare Konsequenz der Transformation von Bürokratie in soziotechnische Systeme.</p>
<p>Die Leistungsfähigkeit des sozialen Subsystems liegt darin, dass es auf unerwartete Ereignisse mit der Anpassung von Social Contracts reagieren kann. Damit kann es selbst dann einen erklärbaren Output erzeugen, wenn es keine vorab definierten Regeln gibt, nach denen das geschehen soll. Diese Fähigkeit wird durch die ins technische Subsystem übertragenen und nicht-veränderlichen Social Contracts begrenzt: Es geht nur, was vorgedacht wurde innerhalb der Regeln, die die Technologie erlaubt.</p>
<p>Das war bei Stempeln und Schreibmaschinen ein überschaubares Problem, hier wurden Social Contracts kaum ins technische Subsystem verschoben. Mit der Einführung von Software hat sich das allerdings deutlich geändert. Software-Engineering ist praktisch das methodische Verschieben von Social Contracts vom sozialen ins technische Subsystem (siehe Abbildung 2). Die Software als Ergebnis unterstützt die kognitiven Vorgänge wie Bewertungen und Entscheidungen anhand der implementierten Regeln. Diese sind üblicherweise aus den Social Contracts abgeleitete Prozesse, die formalisiert wurden. Damit ist die Software im technischen Subsystem nicht in der Lage, auf nicht vorhergesehene Aspekte oder externe Faktoren zu reagieren. Die Software kennt diese einfach nicht.</p>
<p>Natürlich kann Technologie weiterentwickelt und an neue Bedürfnisse angepasst werden, das gilt insbesondere für Software. Im konkreten Fall nützt diese Zukunftsperspektive aber wenig und führt entweder dazu, dass die Bürokratie keinen Output erzeugen kann oder an der Technologie vorbei arbeitet (siehe Artikel: <a href="https://www.innoq.com/de/articles/2023/06/illegale-softwarearchitekturen/">Illegale Softwarearchitekturen</a>). Beides ist weder nützlich noch erstrebenswert.</p>
<p>Praktisch bedeutet die Einführung von Software als Komponente des technischen Subsystems immer eine Reduktion der Handlungsoptionen im sozialen Subsystem. Das ist einer der Gründe, warum sich entweder Leute gegen die Einführung von Software sperren – das soziale Subsystem strebt nach seiner Erhaltung – oder warum die Realisierung solche Software sehr aufwendig ist: Die Nutzerinnen und Nutzer sind daran gewöhnt, dass es einen Entscheidungsspielraum gibt, der nicht formal zu beschreiben ist. Eine solche formale Beschreibung ist aber der Kern jeder Software.</p>
<h3><a id="diegeisterdieichrief..." href="https://www.innoq.com/de/articles/2024/02/es-lebe-die-buerokratie-soziotechnische-welten-teil-10/#diegeisterdieichrief...">Die Geister, die ich rief …</a></h3>
<p>Eine Betrachtung soziotechnischer Systeme im Kontext von Softwareentwicklung zeigt also: Die Einführung von Technologie macht manches effizienter und manches schwieriger. Als Mittel zur Beseitigung negativer Effekte von Bürokratie wird Software nur wenig beitragen können, denn mit jeder Verschiebung eines Social Contracts vom sozialen in das technische Subsystem gehen Handlungsoptionen im sozialen Subsystem verloren.
Vor diesem Hintergrund wird klar: Die Anerkennung, dass Software-Engineering ein Treiber erlebter Bürokratie ist, ist unumgänglich, wenn es um Digitalisierung von Abläufen geht, die auf Social Contracts beruhen.</p>
<foot-notes class="footnotes">
<ol class="footnotes__list">
<li id="fn:1">
<p>Man könnte in der Geschichte noch weitergehen: Der Abakus ist gut 4500 Jahre alt und die Römer nutzen ihr Zahlensystem und den Abakus, um Rechenaufgaben zu parallelisieren. <a href="https://www.innoq.com/de/articles/2024/02/es-lebe-die-buerokratie-soziotechnische-welten-teil-10/#fnref:1" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
<li id="fn:2">
<p>Dieser war zugegebenermaßen manchmal für den Inputgeber nicht befriedigend, kam aber zuverlässig und war erklärbar. <a href="https://www.innoq.com/de/articles/2024/02/es-lebe-die-buerokratie-soziotechnische-welten-teil-10/#fnref:2" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
</ol>
</foot-notes>
</body></html>
https://www.innoq.com/en/articles/2024/02/architektur-teil-1/2024-02-20T00:00:00+01:002024-02-19T12:03:21+01:00Fundamentals of software architecture: Part 1Dr. Gernot Starkegernot.starke@innoq.comhttps://www.innoq.com/en/staff/?person=gernot-starke<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Welcome to the first part in the mini-series on software architecture. We start by examining the original source of the term – since the word architecture presumably conjures in most people thoughts of buildings.</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<h3><a id="architecture:ἀρχιτέκτων" href="https://www.innoq.com/en/articles/2024/02/architektur-teil-1/#architecture:%E1%BC%80%CF%81%CF%87%CE%B9%CF%84%CE%AD%CE%BA%CF%84%CF%89%CE%BD">Architecture: ἀρχιτέκτων</a></h3>
<p>The word <em>architecture</em> comes from the Greek ἀρχιτέκτων (arkhitekton) and can be defined as</p>
<blockquote>
<p>the art and technique of designing and building, as distinguished from the skills associated with construction. It is both the process and the product of sketching, conceiving, planning, designing, and constructing buildings or other structures.
from <a href="https://en.wikipedia.org/wiki/Architecture">Wikipedia: Architecture</a>.</p>
</blockquote>
<p>The architecture of buildings is frequently based on their <em>purpose</em>. In software, we speak here of <em>requirements</em>. Residential building for one or more families? Office or administrative building? Museum, music hall, or sports arena?</p>
<h3><a id="samepurposedifferentarchitectures" href="https://www.innoq.com/en/articles/2024/02/architektur-teil-1/#samepurposedifferentarchitectures">Same purpose, different architectures</a></h3>
<p>Figure 1 shows three different homes. Despite the identical purpose (living space for a family), they look entirely different.</p>
<figure>
<img src="https://res.cloudinary.com/innoq/image/upload/v1/uploads-production/57awvc1mqq6h2f6hhaxp0xryp85f" alt="Figure 1: Three homes" loading="lazy">
<figcaption>Figure 1: Three homes</figcaption></figure>
<p>In contrast to mathematics or physics, architecture is not an exact science. Architecture involves decisions arising from the specific context and a hefty dose of personal <em>taste</em>.</p>
<p>The three homes depicted above were built in different places, each subject to different conditions with regard to geography, weather, and surroundings:</p>
<ul>
<li>The Norwegian house on the left stands on stilts because the water level of the adjacent lake varies considerably.</li>
<li>The middle house is located in a small town in Bavaria. It consists of a timber framework. In the Middle Ages, this was the preferred style for homes and businesses since buildings made entirely of stone were too expensive and difficult to create.</li>
<li>The house on the right is located in the Swiss mountains, and the roof must be capable of withstanding the weight of heavy snowfalls. The ground floor is built in stone, while the upper story uses wood.</li>
</ul>
<p>Individual decisions arising from the specific situations can be clearly identified. But the range of possibilities is yet greater still. If money is no object, a home can even look like the modern villa in Figure 2.</p>
<figure>
<img src="https://res.cloudinary.com/innoq/image/upload/v1/uploads-production/jrn4k8xdl67v0qtbkrvf4hb66upv" alt="Figure 2: Villa in Spain (Ralph Kayden@Unsplash)" loading="lazy">
<figcaption>Figure 2: Villa in Spain (Ralph Kayden@Unsplash)</figcaption></figure>
<p>In other types of buildings, we also see significant differences in material and design. Let’s take a look at the two hospitals in Figure 3.</p>
<figure>
<img src="https://res.cloudinary.com/innoq/image/upload/v1/uploads-production/guhnqusb2kkz5xitsymqkli5oaou" alt="Figure 3: Two hospitals" loading="lazy">
<figcaption>Figure 3: Two hospitals</figcaption></figure>
<p>The University Hospital Aachen (left photo) personally reminds me more of an industrial facility or a power plant. The building on the right is the <a href="https://en.wikipedia.org/wiki/Lou_Ruvo_Center_for_Brain_Health">Lou Ruvo Center for Brain Health</a>.</p>
<p>Allow me to offer yet another example of differing architectures for identical purposes: Figure 4 depicts two cathedrals – in other words, religious buildings (in this case even for practically the same form of belief as both belong to the Christian church).</p>
<figure>
<img src="https://res.cloudinary.com/innoq/image/upload/v1/uploads-production/nl7sb1lfgunhfangtxj34e7qjku4" alt="Figure 4: Two cathedrals" loading="lazy">
<figcaption>Figure 4: Two cathedrals</figcaption></figure>
<p>The cathedral on the right side is optimized for earthquakes – meaning that it satisfies a very specific quality requirement. The U.S. Geological Survey has the following to say about this building:</p>
<blockquote>
<p>A cathedral in the central square of Chillán, Chile replaces the ancient cathedral that collapsed during the strong earthquake of 1939. This modern structure was constructed with earthquake resistance as the primary consideration. The only damage caused by the M 8.8 earthquake on Feb. 27, 2010 was broken windows.</p>
</blockquote>
<p>In other regions of the world, there are (practically) no earthquakes, which is why this type of requirement only applies to some buildings. Where it does apply, however, it is an extremely important consideration and calls for a number of sophisticated methods and techniques (more information on this topic can be found <a href="https://dreamcivil.com/earthquake-proof-buildings/">here</a>).</p>
<h3><a id="formstructureandfoundation" href="https://www.innoq.com/en/articles/2024/02/architektur-teil-1/#formstructureandfoundation">Form, structure, and foundation</a></h3>
<p>The architecture of buildings also includes their outer appearance, in other words the shape, size/height, and material of the facade.</p>
<p>However, it involves much more than these “external values.” For one thing, there is the structural engineering that provides for the stability of the entire building:</p>
<blockquote>
<p>Structural engineering involves understanding and calculating the stability, strength, rigidity, and earthquake-susceptibility of built structures for buildings and nonbuilding structures.</p>
<p>Adapted from: <a href="https://en.wikipedia.org/wiki/Structural_engineering">Wikipedia/Structural Engineering</a></p>
</blockquote>
<p>The selection and use of construction materials for the load-bearing walls and floors are critical aspects of structural engineering. Special attention is given to the foundation in particular: This requires that the responsible architects know something about the condition of the ground and soil as well as the surrounding landscape.</p>
<p>I was personally very impressed by <a href="https://youtu.be/VpJ4AjsYp4A">the explanation</a> of the technical skill and massive effort required to create the foundation for the Burj Kalifa, one of the tallest buildings in the world, in the loose desert sand of Dubai.</p>
<h3><a id="rooms–thecomponentmodelofbuildings" href="https://www.innoq.com/en/articles/2024/02/architektur-teil-1/#rooms%E2%80%93thecomponentmodelofbuildings">Rooms – the component model of buildings</a></h3>
<p>The floor plan is a key document in building architecture, see Figure 5. This documents the internal layout of a building, or more precisely one floor of the building.</p>
<figure>
<img src="https://res.cloudinary.com/innoq/image/upload/v1/uploads-production/epo4pvetcbrn8h0w3yxxwisogcup" alt="Figure 5: Floor plan (Juhan Sonin@Wikimedia)" loading="lazy">
<figcaption>Figure 5: Floor plan (Juhan Sonin@Wikimedia)</figcaption></figure>
<p>The floor plan also shows the position and dimensioning of windows and doors as well as entrances and exits. These can be compared with the interfaces in IT systems.</p>
<p>A number of other aspects are involved in this area of architecture:</p>
<ul>
<li>Position and design of stairs or elevators</li>
<li>Heating and/or cooling systems as well as ventilation to ensure a comfortable room climate</li>
<li>Sound insulation</li>
<li>Fire protection measures</li>
</ul>
<h3><a id="externalinterfaces..." href="https://www.innoq.com/en/articles/2024/02/architektur-teil-1/#externalinterfaces...">External interfaces…</a></h3>
<p>Many buildings require connections to external systems:</p>
<ul>
<li>Drinking water and energy supply</li>
<li>Wastewater systems</li>
<li>Communication systems, such as internet or telephone</li>
</ul>
<h3><a id="andmore..." href="https://www.innoq.com/en/articles/2024/02/architektur-teil-1/#andmore...">and more…</a></h3>
<ul>
<li>Consideration of statutory requirements, such as fire safety, escape routes, maximum loads</li>
<li>Consideration of other quality requirements, such as earthquake safety, water resistance, resistance to break-in attempts, vandalism, major weather events, especially high or low outside temperatures, etc.</li>
<li>Consideration of budget limits</li>
</ul>
<h3><a id="broadspectrumoftopics" href="https://www.innoq.com/en/articles/2024/02/architektur-teil-1/#broadspectrumoftopics">Broad spectrum of topics</a></h3>
<p>It is clear that our namesake architects in the construction and real-estate industry must consider and implement a wide range of aspects in their design work. Some of these correspond to aspects we deal with in software and IT architecture, which is the topic of the next article in this mini-series.</p>
<h3><a id="imagereferencesandothersources" href="https://www.innoq.com/en/articles/2024/02/architektur-teil-1/#imagereferencesandothersources">Image references and other sources</a></h3>
<ul>
<li><p><a href="https://unsplash.com/de/fotos/XOEAHbE_vO8">Seth Kane@Unsplash</a></p></li>
<li><p><a href="https://unsplash.com/de/fotos/yJZF-K4bvvo">Markus Spiske@Unsplash</a></p></li>
<li><p><a href="https://unsplash.com/de/fotos/JT-RUtO2sfs">Aswathy@Unsplash</a></p></li>
<li><p><a href="https://unsplash.com/de/fotos/mR1CIDduGLc">Ralph Kayden@Unsplash</a></p></li>
<li><p><a href="https://commons.wikimedia.org/wiki/File:Klinikum_aachen_fassade.jpg">Sascha Faber@Wikimedia</a></p></li>
<li><p><a href="https://unsplash.com/de/fotos/4lnPASdXn9I">Nick Fewings@Unsplash</a></p></li>
<li><p><a href="https://www.flickr.com/photos/usgeologicalsurvey/4479274376/in/photostream/">Walter Mooney@Flickr</a></p></li>
<li><p><a href="https://unsplash.com/de/fotos/z2IED5kTd-8">Lea V@Unsplasn</a></p></li>
<li><p><a href="https://commons.wikimedia.org/wiki/File:First_story_floor_plan.jpg">Juhan Sonin@Wikimedia</a></p></li>
<li><p><a href="https://en.wikipedia.org/wiki/Structural_engineering">Wikipedia/Structural Engineering</a></p></li>
<li><p><a href="https://en.wikipedia.org/wiki/Architecture">Wikipedia/Architecture</a></p></li>
</ul>
<h4><a id="nextpart:importanttermsinsoftwarearchitecture" href="https://www.innoq.com/en/articles/2024/02/architektur-teil-1/#nextpart:importanttermsinsoftwarearchitecture">Next Part: Important Terms in Software Architecture</a></h4>
<p>Next week, the second part will be released. </p>
</body></html>
https://www.innoq.com/en/podcast/145-architecture-antipatterns/2024-02-19T00:00:00+01:002024-02-19T11:10:09+01:00Sven Johannsven.johann@innoq.comhttps://www.innoq.com/en/staff/sven-johann/Christian Jacobschristian.jacobs@innoq.comhttps://www.innoq.com/en/staff/christian-jacobs/Andreas Voigtandreas.voigt@innoq.comhttps://www.innoq.com/en/staff/andreas-voigt/Felix Schumacherfelix.schumacher@innoq.comhttps://www.innoq.com/en/staff/felix-schumacher/Podcast #145: Architecture Antipatterns. Von guten Ideen und Kipppunkten Podcast #145: Architecture Antipatterns. Von guten Ideen und Kipppunkten <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Antipatterns können nicht nur auf Codeebene, sondern auch auf der Architekturebene auftreten. Sven, Andreas, Christian und Felix diskutieren in dieser Folge die Fallstricke solcher Antipatterns. Sie beleuchten, wie Dokumentation mit ADRs beim Verstehen und Anpassen von Architekturentscheidungen helfen kann und betonen die Wichtigkeit des Kontexts bei der Bewertung von Patterns und Antipatterns. Außerdem werfen sie einen Blick auf die Microsite architecture-antipatterns.tech und laden Euch ein, die Sammlung mit eigenen Case Studies zu erweitern.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Antipatterns können nicht nur auf Codeebene, sondern auch auf der Architekturebene auftreten. Sven, Andreas, Christian und Felix diskutieren in dieser Folge die Fallstricke solcher Antipatterns. Sie beleuchten, wie Dokumentation mit ADRs beim Verstehen und Anpassen von Architekturentscheidungen helfen kann und betonen die Wichtigkeit des Kontexts bei der Bewertung von Patterns und Antipatterns. Außerdem werfen sie einen Blick auf die Microsite architecture-antipatterns.tech und laden Euch ein, die Sammlung mit eigenen Case Studies zu erweitern.</p></body></html>
https://www.innoq.com/en/news/2024/02/new-service-data-and-ai/2024-02-14T00:00:00+01:002024-02-13T17:08:07+01:00INNOQinfo@innoq.comINNOQ launches Data and AI Consulting Services<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>We are expanding our services to include Data and AI, and now offer consulting services to help organizations of all sizes develop a sustainable AI strategy and support in the development of AI-driven software products.
</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Amidst the buzz of generative AI advances, particularly with large language models such as GPT-4, classical machine learning has firmly established itself as the backbone of data processing and analysis. Whether it’s predictive analytics, automated decision making, or business process optimization, proven machine learning techniques play a critical role. They enable organizations to extract valuable insights from their data and make smarter, data-driven decisions.</p>
<h3><a id="dataandaibyinnoq" href="https://www.innoq.com/en/news/2024/02/new-service-data-and-ai/#dataandaibyinnoq">Data and AI by INNOQ</a></h3>
<p>Our Data and AI services combine our 25 years of experience in software development and architecture with our expertise in Machine Learning and Data Engineering, <a href="https://ml-ops.org/">MLOps</a> and <a href="https://www.datamesh-architecture.com/">Data Mesh</a>. We now offer strategic consulting for companies of all sizes to integrate sustainable AI strategies into their existing business processes and support the development of AI-driven software products.</p>
<h3><a id="whatweoffer" href="https://www.innoq.com/en/news/2024/02/new-service-data-and-ai/#whatweoffer">What we offer</a></h3>
<figure>
<a href="https://data-ai.innoq.com/en">
<figure><img alt="Data and AI by INNOQ: business-aligned, cost-conscious, responsible, outcome-focused" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/yif8hchs7kk6p8ptyy5qld73a4x4 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/yif8hchs7kk6p8ptyy5qld73a4x4 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/yif8hchs7kk6p8ptyy5qld73a4x4 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/yif8hchs7kk6p8ptyy5qld73a4x4 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/yif8hchs7kk6p8ptyy5qld73a4x4 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/yif8hchs7kk6p8ptyy5qld73a4x4 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/yif8hchs7kk6p8ptyy5qld73a4x4 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/yif8hchs7kk6p8ptyy5qld73a4x4 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/yif8hchs7kk6p8ptyy5qld73a4x4 2800w" sizes="(min-width: 1400px) 1024px, 90vw"></figure>
</a>
</figure>
<ul>
<li>
<strong>AI Strategy</strong>: Tailored AI strategies for organizations of various sizes and industries.</li>
<li>
<strong>AI Products</strong>: Development and implementation of AI products.</li>
<li>
<strong>Machine Learning Operations</strong> (MLOps): We operationalize machine learning systems, ensuring the deployment, monitoring, and maintenance of AI models.</li>
<li>
<strong>Data Contracts</strong>: We support you from the design phase of your data contracts to the full automation of data product management in your data network.</li>
<li>
<strong>Data Mesh</strong>: Implementing Data Mesh requires organizational, technical, and cultural changes. We provide guidance, support, and tools to empower leaders and developers to take ownership and build trusted data products.</li>
<li>
<strong><a href="https://www.datamesh-manager.com/">Data Mesh Manager</a></strong>: A SaaS application designed for owners and users of data products within a Data Mesh architecture.</li>
<li>
<strong>Data Engineering</strong>: Build reliable data infrastructures.</li>
<li>
<strong>Platform Engineering</strong>: Build custom machine learning platforms.</li>
</ul>
<p>As the Head of Data and AI, <a href="https://www.linkedin.com/in/larysavisenger/">Dr. Larysa Visengeriyeva</a> leads our new service division. Larysa Visengeriyeva earned her doctorate in Augmented Data Quality at the Technical University of Berlin and has authored numerous <a href="https://scholar.google.de/citations?user=_9oHmkAAAAAJ&hl=en">scientific articles</a> on Data and ML topics. With years of practical experience in operationalizing Machine Learning (MLOps), she contributed significantly to <a href="https://ml-ops.org/">ml-ops.org</a>, one of the largest freely available MLOps resource collections. Her expertise also extends to Data Architectures, including Data Mesh.</p>
<long-quote person="Larysa Visengeriyeva" role="Head of Data and AI, INNOQ" avatar="https://res.cloudinary.com/innoq/image/upload/uploads-production/7gserhfxde3sbcqwt8oaymag4jcy">
<blockquote class="longquote">
<span class="longquote__zigzag"></span><p>INNOQ has been engaged in the fields of Data and Machine Learning for quite some time. With our new service area “Data and AI”, we’re consolidating our efforts to provide our clients with comprehensive consulting and development services. This includes everything from implementing sustainable AI strategies and identifying use cases to developing AI products, Data Mesh solutions, or ML platforms. Our focus is on delivering business-aligned, cost-optimized, and responsible solutions.</p>
<div class="longquote__avatar avatar avatar--sm"><img class="avatar__image" src="https://res.cloudinary.com/innoq/image/upload/uploads-production/7gserhfxde3sbcqwt8oaymag4jcy" alt=""></div>
<cite class="longquote__author" itemprop="name">Larysa Visengeriyeva</cite><span class="longquote__role" itemprop="jobTitle">Head of Data and AI, INNOQ</span>
</blockquote>
</long-quote>
<p>For more information about our new Data and AI services, please visit our <a href="https://data-ai.innoq.com/en">website</a>.</p>
<div class="text-center">
<a class="btn btn--cta" data-label="Learn more" href="https://data-ai.innoq.com/en">Learn more</a>
</div>
</body></html>
https://www.innoq.com/en/news/2024/02/neuer-leistungsbereich-data-und-ai/2024-02-14T00:00:00+01:002024-02-13T17:12:54+01:00INNOQinfo@innoq.comNeu bei INNOQ: Beratung und Entwicklung im Bereich Data und AI<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Wir ergänzen unser Leistungsangebot um den Bereich „Data und AI”. Damit bieten wir Organisationen unterschiedlicher Größe ab sofort Beratung bei der Entwicklung einer nachhaltigen KI-Strategie sowie Unterstützung bei der Entwicklung KI-getriebener Softwareprodukte.
</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Neben den aufsehenerregenden Entwicklungen im Bereich der generativen KI, insbesondere durch Large Language Models wie GPT-4, hat sich das klassische Machine Learning als tragende Säule in der Datenverarbeitung und -analyse etabliert. Ob es um prädiktive Analysen, automatisierte Entscheidungsfindung oder die Optimierung von Geschäftsprozessen geht – bewährte Machine Learning-Techniken spielen eine entscheidende Rolle. Sie ermöglichen es Unternehmen, aus ihren Datenmengen wertvolle Einsichten zu gewinnen und darauf basierend intelligentere, datengestützte Entscheidungen zu treffen.</p>
<h3><a id="dataundai" href="https://www.innoq.com/en/news/2024/02/neuer-leistungsbereich-data-und-ai/#dataundai">Data und AI</a></h3>
<p>Im Leistungsbereich “Data and AI” bündeln wir nun unsere Kompetenzen aus 25 Jahren Softwareentwicklung und -architektur sowie unser Know-how in den Bereichen Machine Learning und Data Engineering, <a href="https://ml-ops.org/">MLOps</a> und <a href="https://www.datamesh-architecture.com/">Data Mesh</a>. Damit bieten wir Unternehmen unterschiedlicher Größe ab sofort strategische Beratung bei der Implementierung einer nachhaltigen KI-Strategie in ihre bestehenden Geschäftsprozesse sowie Unterstützung bei der Entwicklung KI-getriebener Softwareprodukte.</p>
<h3><a id="unserangebot" href="https://www.innoq.com/en/news/2024/02/neuer-leistungsbereich-data-und-ai/#unserangebot">Unser Angebot</a></h3>
<figure>
<a href="https://data-ai.innoq.com/de">
<figure><img alt="Data und AI by INNOQ: business-aligned, cost-conscious, responsible, outcome-focused" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/b7ij6ck7a1hg6c2duuh3s7feskth 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/b7ij6ck7a1hg6c2duuh3s7feskth 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/b7ij6ck7a1hg6c2duuh3s7feskth 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/b7ij6ck7a1hg6c2duuh3s7feskth 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/b7ij6ck7a1hg6c2duuh3s7feskth 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/b7ij6ck7a1hg6c2duuh3s7feskth 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/b7ij6ck7a1hg6c2duuh3s7feskth 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/b7ij6ck7a1hg6c2duuh3s7feskth 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/b7ij6ck7a1hg6c2duuh3s7feskth 2800w" sizes="(min-width: 1400px) 1024px, 90vw"></figure>
</a>
</figure>
<ul>
<li>
<strong>KI-Strategie</strong>: Entwicklung individueller KI-Strategien für Unternehmen unterschiedlicher Größe und Branchen</li>
<li>
<strong>KI-Produkte</strong>: Entwicklung und Implementierung von KI-Produkten</li>
<li>
<strong>MLOps</strong> (Machnine Learning Operations): Wir bringen Machine Learning-Systeme in Produktion und gewährleisten die Bereitstellung, Überwachung und Wartung von KI-Modellen.</li>
<li>
<strong>Data Contracts</strong>: Wir unterstützen Sie von der Entwurfsphase Ihrer Data Contracts bis hin zur vollständigen Automatisierung des Data Product Managements in Ihrem Datennetzwerk.</li>
<li>
<strong>Data Mesh</strong>: Die Einführung von Data Mesh erfordert organisatorische, technische und kulturelle Veränderungen. Wir bieten Beratung, Unterstützung und Werkzeuge, damit Führungskräfte und Entwickler:innen Verantwortung übernehmen und vertrauenswürdige Datenprodukte entwickeln können.</li>
<li>
<strong><a href="https://www.datamesh-manager.com">Data Mesh Manager</a></strong>: Eine SaaS-Anwendung, die speziell für Besitzer:innen und Nutzer:innen von Datenprodukten in einer Data Mesh-Architektur entwickelt wurde.</li>
<li>
<strong>Data Engineering:</strong> Aufbau zuverlässiger Dateninfrastrukturen</li>
<li>
<strong>Plattformen und Infrastrukturen</strong>: Entwicklung maßgeschneiderter Machine-Learning-Plattformen</li>
</ul>
<p>Als Head of Data und AI verantwortet <a href="https://www.linkedin.com/in/larysavisenger/">Dr. Larysa Visengeriyeva</a> den neuen Leistungsbereich. Larysa Visengeriyeva hat im Bereich Augmented Data Quality an der TU Berlin promoviert und eine Vielzahl <a href="https://scholar.google.de/citations?user=_9oHmkAAAAAJ&hl=en">wissenschaftlicher Artikel</a> zu Data- und ML-Themen veröffentlicht. Sie verfügt über jahrelange praktische Erfahrungen in Daten-Architekturen wie Data Mesh sowie in der Operationalisierung von Machine Learning (MLOps), die sie als Autorin von <a href="https://ml-ops.org/">ml-ops.org</a> – eine der größten frei verfügbaren MLOps-Ressourcensammlungen – einem großen Publikum zugänglich gemacht hat.</p>
<long-quote person="Larysa Visengeriyeva" role="Head of Data und AI, INNOQ" avatar="https://res.cloudinary.com/innoq/image/upload/uploads-production/7gserhfxde3sbcqwt8oaymag4jcy">
<blockquote class="longquote">
<span class="longquote__zigzag"></span><p>INNOQ beschäftigt sich seit langem mit den Themen Data und Machine Learning. Mit dem neuen Leistungsbereich “Data und AI” bündeln wir nun unsere Aktivitäten und bieten unseren Kunden Beratung und Entwicklung aus einer Hand - sei es bei der Implementierung einer nachhaltigen KI-Strategie, der Identifikation von Use Cases oder der Entwicklung von KI-Produkten, Data Mesh-Lösungen oder ML-Plattformen. Wir bieten geschäftsorientierte, kostenoptimierte und verantwortungsvolle Lösungen.</p>
<div class="longquote__avatar avatar avatar--sm"><img class="avatar__image" src="https://res.cloudinary.com/innoq/image/upload/uploads-production/7gserhfxde3sbcqwt8oaymag4jcy" alt=""></div>
<cite class="longquote__author" itemprop="name">Larysa Visengeriyeva</cite><span class="longquote__role" itemprop="jobTitle">Head of Data und AI, INNOQ</span>
</blockquote>
</long-quote>
<p>Mehr Informationen zu unserem neuen Leistungsbereich “Data und AI” erhalten Sie auf unserer <a href="https://data-ai.innoq.com/de">Website</a>.</p>
<div class="text-center">
<a class="btn btn--cta" data-label="Zur Website" href="https://data-ai.innoq.com/de">Zur Website</a>
</div>
</body></html>
https://www.innoq.com/de/cases/neue-it-loesung-fuer-hilfe-fuer-jungs-ev/2024-03-07T00:00:00+01:002024-03-07T09:20:16+01:00HILFE FÜR JUNGS: Verlässliche Software für die mobile SprechstundeINNOQinfo@innoq.com<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Mitarbeitende bei INNOQ haben und nutzen immer wieder die Möglichkeit, privat initiierte IT-Projekte auch während der Arbeitszeit zu verwirklichen. Vor allem wenn es um einen guten Zweck geht. Auch die neue Software für das Projekt „subway“ des Berliner Vereins Hilfe-für-Jungs e.V. ging auf so eine Privatinitiative zurück: Pedro Lafuente Blanco, Senior Consultant bei INNOQ, wurde durch einen befreundeten Arzt, der sich für den Verein engagiert, auf die IT-Probleme aufmerksam.
Innerhalb eines zweiwöchigen Sprints entwickelte er die erste Version einer neuen Software, die für die Nutzer eine entscheidende Arbeitserleichterung bietet. Das wichtigste Feature: nichts nervt.</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<h3><a id="Überhilfe-fÜr-jungse.v." href="https://www.innoq.com/de/cases/neue-it-loesung-fuer-hilfe-fuer-jungs-ev/#%C3%9Cberhilfe-f%C3%9Cr-jungse.v.">Über HILFE-FÜR-JUNGS e.V.</a></h3>
<p>Der Berliner Verein unterstützt seit 1994 Jungen und junge Männer, die von sexueller Ausbeutung und Gewalt bedroht oder betroffen sind. Die Ziele des Vereins sind, die Rechte von Jungs auf ein Leben ohne sexuelle Gewalt zu stärken, ihre Gesundheit zu fördern und ihnen Chancen auf persönliche Entwicklung und Partizipation zu geben. Eines der Vereinsprojekte ist „subway“, eine Beratungs- und Anlaufstelle für Jungen, Männer und Transpersonen unter 28 Jahren, die unterwegs sind und anschaffen. In den Räumlichkeiten arbeiten Sozialarbeiter, Sozialpädagogen und Dolmetscher. Dort gibt es neben der Versorgung der Grundbedürfnisse (Essen, Wäsche waschen, Duschen, Schlafen und Kleiderkammer) auch eine ärztliche Sprechstunde. Sowie ein Arztmobil, das nachts ab 22 Uhr mit zwei Sozialarbeiter:innen und einem Arzt oder einer Ärztin zu bestimmten Brennpunkten unterwegs ist. Dort können sich die oft wohnungslosen Jungs medizinisch untersuchen lassen – für viele ist das der einzige ärztliche Kontakt überhaupt – und sie haben die Gelegenheit, über Probleme aller Art zu sprechen.</p>
<h3><a id="medizinischhelfengeldgeberüberzeugen" href="https://www.innoq.com/de/cases/neue-it-loesung-fuer-hilfe-fuer-jungs-ev/#medizinischhelfengeldgeber%C3%BCberzeugen">Medizinisch helfen, Geldgeber überzeugen</a></h3>
<p>Bisher benutzte das Ärzt:innenteam in der Sprechstunde sowie im Arztmobil eine etwas in die Jahre gekommene Software- und Datenbanklösung. Sie wurde jedoch nicht von einer professionellen Software-Firma programmiert, sondern nebenbei von einem Mitarbeitenden des Vereins. Sie erfüllte zwar die wichtigsten Zwecke, war jedoch fehleranfällig, und auch die Usability war verbesserungsfähig. Die wichtigsten Aufgaben der Software waren zum einen, die medizinischen Daten der Jungs zu erfassen, um die ärztliche Therapie zu erleichtern bzw. fortzuführen. Zum anderen wurden einige Daten wie Herkunft der Jungs, Sprachen oder Häufigkeit der Untersuchungen dringend für Statistiken benötigt, um die nötigen finanziellen Mittel von Geldgebern wie z.B. dem Berliner Senat zu bekommen. Doch der wichtigste Faktor – und ein wesentlicher Painpoint: die Anwendung sollte schnell, einfach und problemlos bedienbar sein. Denn schließlich müssen sich die Ärzt:innen, vor allem beim nächtlichen Einsatz, voll und ganz auf ihre Patienten konzentrieren. Diese stehen oft unter Drogen, sind alkoholisiert, brauchen dringend Hilfe. Für eine Software, die nicht funktioniert, ist keine Zeit.</p>
<long-quote person="Volker W." role="Arzt bei Subway/HILFE-FÜR-JUNGS e.V." avatar="">
<blockquote class="longquote">
<span class="longquote__zigzag"></span><p>Für uns als Ärzteteam steht die Hilfe für die Jungs im Vordergrund. Umso wichtiger ist es, eine problemlose und einfache Software im Hintergrund zu haben.</p>
<cite class="longquote__author" itemprop="name">Volker W.</cite><span class="longquote__role" itemprop="jobTitle">Arzt bei Subway/HILFE-FÜR-JUNGS e.V.</span>
</blockquote>
</long-quote>
<h3><a id="persönlicheerfahrungfüreinbesseresergebnis" href="https://www.innoq.com/de/cases/neue-it-loesung-fuer-hilfe-fuer-jungs-ev/#pers%C3%B6nlicheerfahrungf%C3%BCreinbesseresergebnis">Persönliche Erfahrung für ein besseres Ergebnis</a></h3>
<p>So wurde Pedro als Software-Spezialist von seinem Freund Volker, dem beim Verein tätigen Arzt, angesprochen. Nicht nur er, sondern auch seine Vorgesetzten bei INNOQ waren gleich begeistert von der Idee, den Verein mit einer besseren Software-Lösung zu unterstützen. Pedro bekam die Möglichkeit, innerhalb eines zweiwöchigen Sprints eine erste Version während der Arbeitszeit zu entwickeln.</p>
<p>Um die Arbeit bei Subway und die Anforderungen an die Software besser zu verstehen, begleitete Pedro seinen Freund und dessen Kolleg:innen bei der nächtlichen Fahrt mit dem Arztmobil. Er erlebte dabei, wie wichtig dieses Engagement für die oft noch sehr jungen Männer ist. Die meisten von ihnen sind wohnungs- oder obdachlos, sprechen kein Deutsch und kaum Englisch und haben keinen Zugang zum Gesundheitssystem. Eine ebenso bedrückende wie motivierende Erfahrung für den Softwareentwickler.</p>
<h3><a id="mehrsicherheitfürdatenundpatienten" href="https://www.innoq.com/de/cases/neue-it-loesung-fuer-hilfe-fuer-jungs-ev/#mehrsicherheitf%C3%BCrdatenundpatienten">Mehr Sicherheit für Daten und Patienten</a></h3>
<p>Zunächst sah sich Pedro die bisherige Software genauer an und setzte sich mit dem Verein für eine IST-Analyse zusammen. Sie sprachen über die Anforderungen, Prozesse und Painpoints. Was ist die Hauptanwendung der Software, welche sekundären Ziele gibt es? Wie läuft typischerweise eine Sprechstunde ab, welche Daten werden benötigt? Wie und wie oft benutzen sie welche Daten, usw.</p>
<p>Bei der bisherigen Software handelte es sich im Wesentlichen um eine einfache Microsoft Access Datenbank mit mehreren Masken für die Eingabe und Suche von Daten. Das Problem: Oft wurden eigentlich vorhandene Daten nicht angezeigt, eigentlich selbstverständliche Verknüpfungen zwischen den Daten nicht hergestellt und – der Worst-Case – vorhandene Protokolle von neuen Protokollen überschrieben. Auch mussten Zählungen für Statistiken teilweise aufwändig händisch durchgeführt werden. Ursache für die Probleme waren zum einen kaputte Anwendungen, zum anderen das fehlende Wissen bei den Anwendern bzw. die wenig intuitive Usability.</p>
<p>Vor allem verlorengegangene Daten sind für den Verein problematisch. Denn wenn sie beispielsweise weniger Sprechstunden und Beratungen angeben können als tatsächlich durchgeführt, bekommen sie auch weniger finanzielle Mittel als benötigt. Der Verein muss wissen, welche Sprachen häufig gesprochen werden, um entsprechende Dolmetscher:innen zur Verfügung zu stellen. Wo und wann am meisten Hilfe benötigt wird, um personellen Aufwand und Routen besser planen zu können. Eine weitere Herausforderung war die Datensicherheit. Die Anforderungen an solche Systeme haben sich in den letzten Jahren stark gewandelt. Deshalb war es wichtig, die neue Software mit entsprechenden Features auszustatten und den Verein bezüglich DSGVO-Konformität in allen Prozessen zu beraten.</p>
<h3><a id="statistikenfürmehrmenschlichkeit" href="https://www.innoq.com/de/cases/neue-it-loesung-fuer-hilfe-fuer-jungs-ev/#statistikenf%C3%BCrmehrmenschlichkeit">Statistiken für mehr Menschlichkeit</a></h3>
<p>Auch bei der neuen Anwendung ist das Hauptziel selbstverständlich, die Gesundheit der Patienten zu verbessern. Deshalb musste sie, wie bei einer normalen ärztlichen Sprechstunde, die medizinische Historie des Patienten abbilden, also Anamnese, Symptome, Behandlung, Diagnose und Therapie. Dazu zählen auch Hinweise oder Fragestellungen für die nächste Sprechstunde bzw. andere behandelnde Ärzt:innen sowie Überweisungen zu Fachärzten. Ein ganz neues Feature war beispielsweise ein elektronischer Impfpass.</p>
<p>Ein sekundäres Ziel ist es, statistische Daten zu erheben – für die Geldgeber und für die interne Organisation des Vereins. Hierfür erfasst die Software auch Kontaktdaten des Patienten. Wichtig: Die jungen Männer müssen nicht ihre echten Namen angeben. Sie können sich auch für ein Pseudonym entscheiden. Gefunden werden sie dann beispielsweise anhand ihres Geburtsdatums. Wichtige Daten für die Statistik sind allerdings die Wohnsituation, Herkunft und gesprochene Sprachen. Für ärztliche Atteste oder Überweisungen kann auch die Angabe einer Anschrift notwendig sein.</p>
<p>Pedro war es aber auch wichtig, einen starken Fokus auf die UX zu legen. Die Software sollte einfach, schnell und vor allem selbsterklärend nutzbar sein. Die Nutzenden sollten „angstfrei“ damit umgehen können, ohne die Gefahr „etwas kaputt zu machen“. Weitere wichtige Verbesserungen waren eine DSVGO konforme Datenerfassung sowie eine automatisierte Erstellung detaillierter Statistiken, z.B. Anzahl der Behandlungen pro Nacht, pro Sprache, pro Zeitraum.</p>
<p>Aus technischer Sicht evaluierte Pedro zunächst eine Low Code Lösung, entschied sich dann schließlich für eine Spring Boot Anwendung, weil dies eine schnelle und effiziente Entwicklung ermöglicht. Für die wenigen reaktiven Komponenten nutzte er JavaScript. Auch wenn es sich hauptsächlich um eine CRUD Anwendung handelt, hat er die Prinzipien einer Onion-Architektur angewandt.</p>
<h3><a id="technischefakten" href="https://www.innoq.com/de/cases/neue-it-loesung-fuer-hilfe-fuer-jungs-ev/#technischefakten">Technische Fakten</a></h3>
<check-list>
<ul class="checklist">
<li>Spring-Boot mit Thymeleaf</li>
<li>Javascript</li>
<li>Neben Standard Spring Boot Testmittel, auch Selenium Webdriver und Testcontainers</li>
<li>OAuth2-“Ready” dank Spring-Security</li>
</ul>
</check-list>
<long-quote person="Lukas Weber" role="Geschäftsführer bei HILFE-FÜR-JUNGS e.V." avatar="">
<blockquote class="longquote">
<span class="longquote__zigzag"></span><p>Was ihr auf die Beine gestellt habt, ist nicht in Worte zu fassen für uns. Gerade für unsere Ärzte*innen ist dies ein so großes Geschenk und eine Professionalisierung, die so dringend notwendig war.</p>
<cite class="longquote__author" itemprop="name">Lukas Weber</cite><span class="longquote__role" itemprop="jobTitle">Geschäftsführer bei HILFE-FÜR-JUNGS e.V.</span>
</blockquote>
<p></p></long-quote>
<p>Die neue Software ist bereits im Einsatz und wurde inzwischen von allen dort tätigen Ärzt:innen getestet und es gab erste sehr positive Feedbackrunden. Kleinere Fehlerbehebungen und Änderungen kann Pedro weiterhin während seiner Arbeitszeit bei INNOQ erledigen und über größere Themen redet man gemeinsam.</p>
<p>Das erste Feedback zeigt, dass ein wichtiges Ziel der Software auf jeden Fall erreicht wurde: sie schont die Nerven der Anwender:innen. Von seinem Freund Volker weiß Pedro, dass eine funktionierende Ausstattung tatsächlich ein entscheidender Faktor sein kann, ob jemand seine Tätigkeit weiterführt bzw. länger dabei bleibt. Und das entscheidet letztendlich darüber, ob die Jungs Hilfe bekommen oder nicht. „Ein schöner Gedanke“, sagt Pedro, „dass man mit der Software wirklich geholfen hat.“</p>
</body></html>
https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/2024-02-13T00:00:00+01:002024-03-05T17:10:40+01:00GitOps geht auch ohne KubernetesAnja Kammeranja.kammer@innoq.comhttps://www.innoq.com/en/staff/?person=anja-kammer<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Im Kubernetes-Universum ist GitOps erfolgreich. Dank der Versionskontrolle mit Git lässt sich die Betriebsumgebung transparenter und zuverlässiger konfigurieren. Aber GitOps klappt auch ohne Kubernetes.</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>“GitOps funktioniert nur mit Kubernetes als Betriebsplattform!“ Dieses Missverständnis von GitOps hält sich hartnäckig und ist vermutlich der Grund, weshalb es sich noch nicht als Standard-Workflow für Software-Delivery-Prozesse etabliert hat. Wer GitOps einsetzen möchte, findet nahezu ausschließlich Tooling, das Kubernetes voraussetzt. Im Kern gibt GitOps lediglich vor, dass ein Version Control System (VCS) wie Git als Schnittstelle für einen Software-Agent dient, der Deployment- und Betriebsaufgaben innerhalb einer Zielumgebung ausführt.</p>
<h3><a id="dievorteilevongitopsnutzen" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#dievorteilevongitopsnutzen">Die Vorteile von GitOps nutzen</a></h3>
<p>Im Folgenden geht es daher zunächst darum, die Kernfunktionen eines GitOps-getriebenen Delivery-Prozesses zu identifizieren, um darauf aufbauend Strategien kennenzulernen, mit denen sich GitOps auch ohne Kubernetes umsetzen lässt.
Wer sich mit GitOps beschäftigt, merkt schnell, dass das verfügbare Tooling Kubernetes als Betriebsplattform voraussetzt. Etablierte Werkzeuge von der Stange, mit denen sich GitOps außerhalb von Kubernetes einsetzen ließe, fehlen bisher noch.</p>
<p>Ein Grund dafür ist, dass zu der Zeit, als <a href="https://docs.gitops.weave.works/docs/intro-weave-gitops/">Weaveworks</a> das GitOps-Konzept populär gemacht hat, Kubernetes bereits als de facto Standard für moderne Betriebsplattformen in der IT-Community anerkannt war. Es ist eine standardisierte Betriebsumgebung, die eine einfache Verwaltung der Ressourcen über eine API ermöglich. Daher ist es sinnvoll, neues Tooling auch bevorzugt für Kubernetes zu entwickeln.
Allerdings sollte nicht jedes Softwaresystem mit Kubernetes betrieben werden, und die Gründe hierfür sind nicht technischer Natur.</p>
<p>Beispielsweise ist die Größe eines Unternehmens oder der IT-Abteilung ein entscheidender Faktor. Ein kleines Team könnte Schwierigkeiten haben, die Komplexität von Kubernetes zu bewältigen und nötiges Know-how aufzubauen. Selbst die bloße Verwendung und Konfiguration eines vom Cloud-Provider betriebenen Kubernetes-Clusters erweist sich in vielen Fällen schon als komplexe Herausforderung. Stattdessen ist es für kleinere Organisationen möglicherweise sinnvoller, auf stärker abstrahierte Plattform wie <a href="https://www.heroku.com/">Heroku</a> zurückzugreifen.</p>
<p>Dasselbe Prinzip lässt sich auf die Größe und Komplexität des zu betreibenden Softwaresystems anwenden. Wenn lediglich eine Handvoll Container deployt werden sollen, übersteigt die Komplexität des Betriebs von Kubernetes den Nutzen. Für große Systemlandschaften andererseits kann es sinnvoller sein, eine eigene Kubernetes-basierte Infrastruktur aufzubauen, da es langfristig kosteneffektiver sein kann, eigene Kompetenz aufzubauen und mehr Flexibilität bietet.
Darüber hinaus gibt es auch Organisationen, die bereits mit ihrer bestehenden Betriebsplattform zufrieden sind oder aufgrund laufender Verträge an eine bestimmte Plattform gebunden sind.</p>
<p>Dennoch sollten die Organisationen, die Kubernetes nicht einsetzen können, von den Vorteilen profitieren können, die GitOps bietet<a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fn:1" id="fnref:1" title="see footnote" class="footnote">[1]</a>:</p>
<ul>
<li>Einfache und schnelle Fehlerbehebung</li>
<li>Sichere Deployments</li>
<li>Sich selbst dokumentierende Deployments</li>
<li>Leichtere Personalbesetzung und schnelleres Onboarding</li>
</ul>
<h3><a id="gitopsmitundohnekubernetes" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#gitopsmitundohnekubernetes">GitOps mit und ohne Kubernetes</a></h3>
<figure>
<img alt="Abb. 1: GitOps-Workflow mit Kubernetes" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/wiwp3o74c9b14s1a1l2efc0uedfk 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/wiwp3o74c9b14s1a1l2efc0uedfk 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/wiwp3o74c9b14s1a1l2efc0uedfk 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/wiwp3o74c9b14s1a1l2efc0uedfk 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/wiwp3o74c9b14s1a1l2efc0uedfk 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/wiwp3o74c9b14s1a1l2efc0uedfk 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/wiwp3o74c9b14s1a1l2efc0uedfk 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/wiwp3o74c9b14s1a1l2efc0uedfk 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/wiwp3o74c9b14s1a1l2efc0uedfk 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Abb. 1: GitOps-Workflow mit Kubernetes</figcaption></figure>
<p>Abbildung 1 zeigt eine stark vereinfachte Darstellung eines GitOps-Workflows mit Kubernetes. Im Zentrum des Workflow-Modells steht das Repository, das Änderungen an der Anwendung aufzeichnet. Eine Build-Pipeline wird von Aktivitäten im Repository getriggert, die die Anwendung aktualisiert.</p>
<p>Im Kubernetes-Cluster befindet sich der GitOps-Operator und die von diesem gemanagten Applikationen. Die Definition eines Operators im Kubernetes-Universum ist sehr spezifisch. Ein Operator ist speziell darauf ausgelegt, eine Anwendung oder einen Service innerhalb des Clusters zu verwalten und zu betreiben. Ein Operator kann dabei auch komplexe Aufgaben wie die Konfiguration von Load Balancern oder die Integration mit anderen Services übernehmen.</p>
<p>Der Operator hat also die Aufgabe, erst nach dem vollständigen Durchlaufen der Build-Pipeline, die Anwendung in die Betriebsumgebung, den Kubernetes-Cluster, zu deployen. Üblicherweise beschreibt dabei der Main-Branch die aktuell deployfähige Softwareversion, alternativ lassen sich Git-Tags verwenden. Einige Komponenten wie die Image-Registry fehlen in der Abbildung, sind aber selbstverständliche Bestandteile des Systems.</p>
<p>Das Repository enthält die gesamte Anwendungs- und Infrastrukturkonfiguration, mit deren Hilfe sich eine Anwendung jederzeit von Grund auf deployen und regelmäßig aktualisieren lässt. Der Build-Pipeline fällt dabei die Aufgabe zu, ebenfalls die Infrastrukturkonfiguration zu testen. Auch das Ausführen von Integrationstests ist so leicht umsetzbar. Das Repository kann zudem den eigentlichen Anwendungscode enthalten, um die Umgebungskonfiguration gemeinsam mit der Anwendung selbst zu versionieren.</p>
<p>Die Bezeichnung dieses Repositorys kann je nach Präferenz variieren, im Folgenden soll der Begriff Environment-Repository verdeutlichen, dass die enthaltene Anwendungs- und Infrastrukturkonfiguration idealerweise eine vollständige Betriebsumgebung mit all ihren nötigen Komponenten (z.B.: Web-Worker, Scheduler, Funktionen und Persistenz) abbildet.</p>
<figure>
<img alt="Abb.2: GitOps-Workflow ohne Kubernetes" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/50cf0ekz4neu68xywcf9wsxf1ks5 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/50cf0ekz4neu68xywcf9wsxf1ks5 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/50cf0ekz4neu68xywcf9wsxf1ks5 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/50cf0ekz4neu68xywcf9wsxf1ks5 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/50cf0ekz4neu68xywcf9wsxf1ks5 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/50cf0ekz4neu68xywcf9wsxf1ks5 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/50cf0ekz4neu68xywcf9wsxf1ks5 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/50cf0ekz4neu68xywcf9wsxf1ks5 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/50cf0ekz4neu68xywcf9wsxf1ks5 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Abb.2: GitOps-Workflow ohne Kubernetes</figcaption></figure>
<p>Abbildung 2 zeigt einen GitOps-Prozess ohne Kubernetes. Gegenüber Abbildung 1 ändern sich lediglich die Bezeichnungen: Die allgemeine Betriebsumgebung ersetzt den Kubernetes-Cluster. Kubernetes lässt sich auch als eine Betriebsumgebung bezeichnen. Aus dem Operator wird ein generisches GitOps-Tool, meint aber im Prinzip dasselbe. Abbildung 2 spiegelt damit wider, dass die Konzepte aus dem Kubernetes-Universum auf andere Betriebsumgebungen übertragbar sind. Zu den beiden essenziellen Komponenten Operator und Betriebsumgebung folgen in den nächsten Abschnitten detaillierte Definitionen.</p>
<figure>
<img alt="Abb. 3: Vergleich von GitOps mit und ohne Kubernetes " loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/wof63w4y5fxxxwguwezbzimp3ry5 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/wof63w4y5fxxxwguwezbzimp3ry5 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/wof63w4y5fxxxwguwezbzimp3ry5 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/wof63w4y5fxxxwguwezbzimp3ry5 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/wof63w4y5fxxxwguwezbzimp3ry5 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/wof63w4y5fxxxwguwezbzimp3ry5 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/wof63w4y5fxxxwguwezbzimp3ry5 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/wof63w4y5fxxxwguwezbzimp3ry5 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/wof63w4y5fxxxwguwezbzimp3ry5 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Abb. 3: Vergleich von GitOps mit und ohne Kubernetes </figcaption></figure>
<h3><a id="operator–einepragmatischedefinition" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#operator%E2%80%93einepragmatischedefinition">Operator – eine pragmatische Definition</a></h3>
<p>Im Kubernetes-Universum ist das Operator-Konzept klar definiert. Den Operator kennzeichnen folgende Eigenschaften:</p>
<ul>
<li>Überwacht, verwaltet und steuert Ressourcen in einer Betriebsumgebung</li>
<li>Agiert als intelligente Schicht, die diese Aufgaben anhand bestimmter Verhaltensregeln ausführt</li>
<li>Ist ein Stück konfigurierbare Software, programmiert mit diesen Verhaltensregeln</li>
</ul>
<p>Ein Operator lässt sich als ein Hintergrundprozess oder Agent betrachten. Ein Daemon ist solch ein Hintergrundprozess, der auf einem Linux-System läuft und bestimmte Aufgaben ausführt, während der Agent eine Softwarekomponente ist, die für das Überwachen, Verwalten oder Steuern eines bestimmten Systems oder Dienstes zuständig ist. Ein Agent lässt sich als eine Art Daemon betrachten, der speziell für die Aufgabe der Verwaltung oder Überwachung entwickelt wurde.</p>
<h4><a id="einoperatormussnichtzwingendeinstücksoftwaresein" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#einoperatormussnichtzwingendeinst%C3%BCcksoftwaresein">Ein Operator muss nicht zwingend ein Stück Software sein</a></h4>
<p>Es gibt die Rolle eines Operators, als menschliches Mitglied einer Operations-Abteilung. Ein menschlicher Operator ist ebenfalls für das Überwachen und Verwalten von Systemen verantwortlich, ob Software oder Hardware. Darum ist es wichtig, bezüglich der Definition von Operatoren nicht dogmatisch zu sein.
GitOps sagt aus, dass ein Versionskontrollsystem wie Git als Schnittstelle für einen Operator dient, der wiederum Deployment- und Betriebsaufgaben innerhalb einer Zielumgebung ausführt. In Anbetracht dieser Konzepte lässt sich GitOps-Tooling, das innerhalb der Zielumgebung läuft, ebenfalls als Operator betrachten – zumal dieser Begriff nicht auf das Kubernetes-Universum beschränkt ist.</p>
<p>Daraus lässt sich ableiten, dass Organisationen, die nicht auf Kubernetes-natives GitOps-Tooling zurückgreifen können, eigene Operatoren bauen können. Dabei ist lediglich zu klären, welche Kernfunktionen implementiert sein müssen.</p>
<h3><a id="kernfunktionen" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#kernfunktionen">Kernfunktionen</a></h3>
<p>Viele der vorhandenen Kubernetes-nativen GitOps-Tools haben zahlreiche nützliche Funktionen, die den Betrieb erleichtern. Einige davon sind jedoch für einen GitOps-getriebenen Workflow nicht notwendig. Das OpenGitOps-Projekt hat dazu Prinzipien definiert<a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fn:2" id="fnref:2" title="see footnote" class="footnote">[2]</a>, aus denen sich die Kernfunktionen eines GitOps-Operators ableiten lassen.</p>
<h4><a id="prinzip1:deklarativ" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#prinzip1:deklarativ">Prinzip 1: Deklarativ</a></h4>
<p>“Bei einem von GitOps verwalteten System sollte der gewünschte Zustand deklarativ ausgedrückt werden.” (OpenGitOps-Projekt – Principles)
Das erste Prinzip setzt eine deklarative Beschreibung des gewünschten Zustands (Desired State)<a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fn:3" id="fnref:3" title="see footnote" class="footnote">[3]</a> eines von GitOps verwalteten Systems voraus. Diese Zustandsbeschreibung lässt sich mit beliebigen Infrastructure-as-Code Tools erstellen, die oft auch für Anwendungskonfigurationen zum Einsatz kommen. Das verwendete GitOps-Tool beziehungsweise der Operator muss dann herausfinden, wie sich der vorhandene Zustand des Systems in den gewünschten überführen lässt und diese Transformation vornehmen. Darüber hinaus hängt die Umsetzung dieses Prinzips von der Definition der Infrastruktur- und Anwendungskomponenten ab. Dient beispielsweise Terraform als Deployment- und Provisioning-Tool, gilt das Prinzip bereits als erfüllt.</p>
<h4><a id="prinzip2:versioniertundunveränderlich" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#prinzip2:versioniertundunver%C3%A4nderlich">Prinzip 2: Versioniert und unveränderlich</a></h4>
<p>“Der gewünschte Zustand wird auf eine Weise gespeichert, die Unveränderlichkeit und Versionierung erzwingt und eine vollständige Versionshistorie aufbewahrt.” (OpenGitOps-Projekt – Principles)
Um beim Terraform-Beispiel zu bleiben: das zweite Prinzip gilt als erfüllt, wenn die Terraform-Dateien im Environment-Repository abgelegt sind. Dabei ist jedoch das Neuschreiben der Git-Historie zu unterbinden, da sich ansonsten die Commit-Historie manipulieren lässt.</p>
<p>Es gilt zu beachten, dass dieses Prinzip nicht nur für die Produktivumgebung, sondern unter anderem auch für Dev- und Test-Umgebungen wichtig ist. Dies ermöglicht es, jederzeit auf frühere Versionen der Zustandsbeschreibung zurückzugreifen, was besonders bei der Fehlerbehebung und bei automatisierten Rollbacks hilfreich sein kann. Eine derartige Historie eröffnet eine lückenlose Nachvollziehbarkeit von Änderungen, die allerdings in der Praxis nur selten einen Anwendungszweck findet.</p>
<h4><a id="prinzip3:pull-basiert" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#prinzip3:pull-basiert">Prinzip 3: Pull-basiert</a></h4>
<p>“Software-Agents ziehen die Beschreibung des gewünschten Zustands automatisch aus der Quelle.” (OpenGitOps-Projekt – Principles)
Während traditionelle CI/CD-Systeme (Continuous Integration / Continuous Delivery) einem Push-basierten Workflow folgen, bei dem der Deployment-Prozess Zugriff auf die Zielumgebung über ein Token, einen Schlüssel oder ein Zertifikat erhält, sieht das dritte Prinzip des OpenGitOps-Projekts ein Pull-basiertes (ziehen) Deployment vor. Push-basierte Workflows gelten als vulnerabel, denn sobald das CI/CD-System kompromittiert ist, lässt sich der Zugriff zur Zielumgebung ausnutzen. In der Regel verwalten zentrale Build-Server alle Secrets und Schlüssel für Anwendungen und Systeme eines gesamten Unternehmens. Wenn Angreifende in den Build-Server eindringen und diese Schlüssel und Secrets auslesen, können sie somit alle Systeme des Unternehmens kompromittieren.</p>
<p>Im Vergleich dazu stellt der Pull-basierte Ansatz von GitOps sicher, dass der Operator den Deployment-Prozess ausschließlich innerhalb der Zielumgebung ausführt. Das trägt zu erhöhter Sicherheit bei, da die Zielumgebung, beispielsweise die Produktionsumgebung, typischerweise besser geschützt ist als ein Build-Server, auf dem alle Secrets liegen.
Abbildung 4 macht deutlich, dass es sich bei diesem Ansatz im Prinzip um das Verlagern der Deployment-Pipeline in die Zielumgebung handelt.</p>
<figure>
<img alt="Abb.4: Pull-basiertes Deployment hebt die Deployment-Pipeline in die Zielumgebung." loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/hvqcmyydrgm26cv4nudspt5om80w 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/hvqcmyydrgm26cv4nudspt5om80w 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/hvqcmyydrgm26cv4nudspt5om80w 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/hvqcmyydrgm26cv4nudspt5om80w 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/hvqcmyydrgm26cv4nudspt5om80w 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/hvqcmyydrgm26cv4nudspt5om80w 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/hvqcmyydrgm26cv4nudspt5om80w 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/hvqcmyydrgm26cv4nudspt5om80w 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/hvqcmyydrgm26cv4nudspt5om80w 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Abb.4: Pull-basiertes Deployment hebt die Deployment-Pipeline in die Zielumgebung.</figcaption></figure>
<p>Ein pull-basierter Workflow lässt sich auf zwei Arten implementieren, mit Polling und Webhooks.
Beim Polling holt der Operator im Takt weniger Minuten den Inhalt des Repositorys mit der Zustandsbeschreibung ab. Mit Webhooks schickt der Git-Provider (GitHub, GitLab etc.) eine Meldung an den Operator, sobald es im Repository eine Änderung gibt. Der Operator holt sich bei dieser Variante sofort nach dieser Meldung die Zustandsbeschreibung ab und passt gegebenenfalls die Zielumgebung an. Das hat den Vorteil, dass Entwicklungsteams bei einem Deployment nicht auf die Ausführung des nächsten Zyklus warten müssen, sondern innerhalb weniger Sekunden die Anpassung durch den Operator stattfindet.</p>
<p>Oft kommen beide Varianten, Polling und das Reagieren auf Webhooks, zum Einsatz. Das hat etwas mit dem nächsten Prinzip zu tun, dem kontinuierlichen Abgleich.</p>
<h4><a id="prinzip4:kontinuierlicherabgleich" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#prinzip4:kontinuierlicherabgleich">Prinzip 4: Kontinuierlicher Abgleich</a></h4>
<p>“Software-Agents beobachten kontinuierlich den aktuellen Systemzustand und versuchen, den gewünschten Zustand herzustellen.” (OpenGitOps-Projekt – Principles)
Der kontinuierliche Abgleich (Reconciliation Loop)<a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fn:4" id="fnref:4" title="see footnote" class="footnote">[4]</a> stellt den anspruchsvollsten Teil des GitOps-Workflows dar. Hierbei geht es nicht nur darum, die Umgebung bei jeder Konfigurationsänderung entsprechend anzupassen, sondern auch, zu jeder Zeit den gewünschten Zustand zu erhalten. Konfigurationsänderungen, die manuell oder versehentlich passiert sind, werden so in einem regelmäßigen Zyklus wieder zurückgesetzt.</p>
<p>Es ist daher üblich, Personen oder Prozessen nicht zu erlauben, Ressourcen in der Betriebsumgebung zu verändern, die unter der Kontrolle des Operators stehen. Ein Nachteil dieser Vorgehensweise ist, dass sich beispielsweise Hotfixes nicht mehr bereitstellen lassen, sollte der Operator ausfallen.
Am Beispiel von Terraform lässt sich dieses Prinzip leicht nachvollziehen. Terraform überschreibt den Zustand der Ressourcen, falls sie nicht der aktuellen Konfiguration entsprechen. Um das zu erreichen, gilt es, regelmäßig einen Ausführungsplan zu erzeugen und bei auftretenden Divergenzen anzuwenden.</p>
<p>Zusätzlich lässt sich eine Kopie der zuletzt gezogenen Zustandsbeschreibung für den Operator vorhalten, um die Umgebung jederzeit in den zuletzt bekannten Zustand zurückzuversetzen, auch wenn kein Zugriff auf das Quell-Repository möglich sein sollte.</p>
<h3><a id="implementierung" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#implementierung">Implementierung</a></h3>
<p>Anhand der vier GitOps-Prinzipien sind die Kernfunktionen eines GitOps-Operators nun identifiziert:</p>
<ul>
<li>Deklarativ</li>
<li>Versioniert und unveränderlich</li>
<li>Pull-basiert</li>
<li>Kontinuierlicher Abgleich</li>
</ul>
<p>Das Umsetzen dieser Prinzipien erfordert keine speziellen Tools, allerdings gibt es bereits Systeme, die in der Lage sind, mit Automatisierungsprozessen umzugehen. Mithilfe einer Workflow-Engine, die über Webhooks angesprochen wird, lassen sich beliebige Prozesse wie etwa das Terraform CLI ausführen. Eine Workflow-Engine bildet im Prinzip die Grundform eines CI/CD-Systems.</p>
<p>Deswegen erfüllen CI/CD-Systeme in der Praxis oft schon alle Anforderungen:</p>
<ul>
<li>Lauschen auf Webhooks für Git-Aktionen</li>
<li>Ziehen von Repository-Inhalten</li>
<li>Ausführen von Prozessen</li>
<li>Rückmelden des Ergebnisses (optional)</li>
</ul>
<p>Aus diesem Grund ist es vollkommen valide, einen GitOps-Operator mithilfe eines CI/CD-Systems zu implementieren, das innerhalb der Zielumgebung läuft.
Eine weitere Möglichkeit, einen GitOps-Operator zu implementieren, ist der Einsatz spezialisierter Tools wie <a href="https://github.com/runatlantis/atlantis">Atlantis</a> für das Verwalten von Terraform-Ressourcen. Das unter Apache-2.0-Lizenz verfügbare Atlantis ist ein Server, der auf Webhooks reagiert und Terraform-Kommandos daraus liest. Neben Git-Provider-Webhooks lassen sich auch Slack-Webhooks verwenden, mit denen es möglich ist, Deployments nicht durch das Beobachten des Git-Repositorys auszulösen, sondern durch Chat-Nachrichten. Das wiederum entspricht nicht den GitOps-Prinzipien. Chat-Plattform-Webhooks lassen sich besser dafür nutzen, mehr Einblick in das System durch Statusabfragen zu erlangen. Diese Statusabfragen sind im Slack-Kanal leicht für viele Personen einsehbar.</p>
<p>Obwohl Atlantis auf den ersten Blick einfach zu bedienen ist, stellt das Implementieren des kontinuierlichen Abgleichs eine gewisse Hürde dar. Glücklicherweise bietet Atlantis eine API, über die sich der Prozess von außen steuern lässt. Das kann per einfachem Cron-Skript oder über eine zeitgesteuerte Funktion erfolgen.</p>
<p>Im Prinzip lassen sich alle erforderlichen Funktionen eines GitOps-Workflows mit Git, einem Infrastrukturautomatisierungswerkzeug der Wahl und ein wenig Glue-Code implementieren.</p>
<h3><a id="nice-to-have-features" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#nice-to-have-features">Nice-to-Have-Features</a></h3>
<p>GitOps-Tools stehen gelegentlich dafür in der Kritik, dass ihnen essenzielle Features wie zum Beispiel Secrets Management fehlen. Hintergrund dieser Einschätzung ist die Annahme, dass GitOps-Tools per se darauf ausgelegt seien, CI/CD-Systeme zu ersetzen und daher auch die gleichen Funktionen anbieten sollten.</p>
<h4><a id="secretsmanagement" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#secretsmanagement">Secrets Management</a></h4>
<p>Gerade weil CI/CD-Systeme oftmals Laufzeit-Secrets beinhalten, zählen sie mittlerweile zu den kritischen Einfallstoren innerhalb der Software Supply Chain. Ein prominentes Beispiel dafür ist der Vorfall bei <a href="https://www.theregister.com/2021/09/15/travis_ci_leak">Travis CI</a>, bei dem sieben Tage lang alle Secrets offen lagen. Vergleichbare Sicherheitslücken sind auch bei anderen CI/CD-Providern möglich, wie weitere Vorfälle in der Vergangenheit gezeigt haben.</p>
<p>Auch das bereits angesprochene Prinzip des pull-basierten Workflows von GitOps zeigt, dass Zugänge zur Betriebsumgebung nicht in CI/CD-Systeme abgelegt sein sollten. Deshalb ist es unabhängig von GitOps wichtig, eine Secrets-Management-Strategie zu implementieren, da es sich um einen sensiblen Faktor handelt. Es gibt unterschiedliche Ansätze, Secrets bereitzustellen. Zum einen gibt es Secrets-Vaults wie <a href="https://www.vaultproject.io">HashiCorp Vault</a>, die erst in der Zielumgebung benötigte Secrets bereitstellen, basierend auf Berechtigungen und Referenzen. Zum anderen entschlüsseln Tools wie etwa <a href="https://github.com/bitnami-labs/sealed-secrets">Bitnami Sealed Secrets</a>, zuvor verschlüsselte Secrets erst in der Zielumgebung wieder.</p>
<h4><a id="rollback-mechanismen" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#rollback-mechanismen">Rollback-Mechanismen</a></h4>
<p>Ein entscheidender Vorteil des GitOps-Ansatzes ist, dass sich Rollbacks von Entwicklungsseite so einfach ausführen lassen wie ein ‘git revert’-Kommando. Schlägt jedoch ein Deployment fehl, kann es zu Abweichungen zwischen der Beschreibung des gewünschten Zustands im Environment-Repository und dem aktuellen Zustand der Zielumgebung kommen.</p>
<p>Selbst im Kubernetes-Kontext existiert kein automatischer Rollback-Mechanismus. Stattdessen bleibt das fehlerhafte Deployment hängen und startet immer wieder erneut, bis es erfolgreich ist. Das ist eine Design-Entscheidung, die trotz ihrer Einfachheit sehr resilient ist.</p>
<p>Dringend zu empfehlen ist daher, im Falle eines fehlgeschlagenen Deployments einen Alarm auszulösen, damit Entwicklungsteams manuell im Environment-Repository die fehlerhaften Konfigurationen korrigieren können. Dadurch wird der GitOps-Operator befähigt, die Zustandsbeschreibung erneut einzulesen und auszuführen.</p>
<h4><a id="grafischebenutzungsoberflächen" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#grafischebenutzungsoberfl%C3%A4chen">Grafische Benutzungsoberflächen</a></h4>
<p>Zwar zählen grafische Benutzungsoberflächen nicht zu den Kernfunktionalitäten von GitOps-Tools, dennoch finden jene Werkzeuge, die eine anbieten, besonders viel Anklang bei Nutzenden. Diese Oberflächen locken mit einer anschaulichen Visualisierung der System-Topologie und stellen Knöpfe bereit, um unter anderem ein Deployment manuell anzustoßen. Solche Funktionen lassen sich allerdings auch mit dedizierten Werkzeugen bereitstellen, wobei das manuelle Anstoßen eines Deployments nur ein Git-Commit entfernt ist.</p>
<p>Das Implementieren aller bisher beschriebenen Nice-to-Have-Features sowie weiterer Funktionalitäten kann hilfreich sein, ist aber keineswegs notwendig. Tatsächlich ist es der Unix-Philosophie zufolge oft von Vorteil, solche Aufgaben auf spezialisierte, eigenständige Tools auszulagern. Die Unix-Philosophie propagiert das Entwickeln kleiner, modularer Komponenten, die leicht integrierbar sind und jeweils einen einzigen Zweck erfüllen. Das Motto ist: “Do one thing and do it well”.</p>
<p>Bereits ausgereifte Tools für die erweiterten Anforderungen des GitOps-Workflows sollten demnach als eigenständige Komponenten zum Einsatz kommen. Es ist nicht notwendig, dass ein einzelnes GitOps-Tool Aufgaben übernimmt, die nicht zur Kernfunktionalität gehören.</p>
<h3><a id="fürgitopsgeeignetebetriebsumgebungen" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#f%C3%BCrgitopsgeeignetebetriebsumgebungen">Für GitOps geeignete Betriebsumgebungen</a></h3>
<p>Nachdem die Kernfunktionalitäten eines GitOps-Operators geklärt sind, gilt es nun noch, die Betriebsumgebung zu definieren. Sie umfasst alle Komponenten und Anwendungen, die der Operator verwaltet. Handelt es sich um eine einzelne Virtual Machine (VM), lässt sich der Operator als Daemon betreiben. Die Betriebsumgebung kann aber auch als Cluster mehrerer Maschinen vorliegen, in dem der Operator als eine von vielen Workloads läuft. In beiden Fällen benötigt der Operator die Berechtigung, Workloads zu modifizieren, die sich innerhalb der Betriebsumgebung selbst befinden.</p>
<p>Oft wird vergessen, dass solch eine Betriebsumgebung aber auch die Gesamtheit aller Ressourcen in einer abgeschlossenen Cloud-Umgebung sein kann. Der Operator kann dabei als Funktion oder als VM innerhalb der Umgebung laufen und Zugriff auf die API des Cloud-Providers haben. In diesem Szenario kann es herausfordernd sein, den Überblick über die verschiedenen Dienste, Ressourcen und Deployments zu behalten, die der Operator verwaltet. Es bietet sich daher an, konkrete Berechtigungsgrenzen festzulegen. Die großen Cloud-Provider arbeiten dazu mit dem Konzept der Landing Zones.<a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fn:5" id="fnref:5" title="see footnote" class="footnote">[5]</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fn:6" id="fnref:6" title="see footnote" class="footnote">[6]</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fn:7" id="fnref:7" title="see footnote" class="footnote">[7]</a></p>
<figure>
<img alt="Abb. 5: Beispiele von Betriebsumgebungen: VM, Cluster und Cloud Landing Zone" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/zj18li2vjxqp0i2bdxz79mrgmzjy 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/zj18li2vjxqp0i2bdxz79mrgmzjy 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/zj18li2vjxqp0i2bdxz79mrgmzjy 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/zj18li2vjxqp0i2bdxz79mrgmzjy 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/zj18li2vjxqp0i2bdxz79mrgmzjy 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/zj18li2vjxqp0i2bdxz79mrgmzjy 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/zj18li2vjxqp0i2bdxz79mrgmzjy 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/zj18li2vjxqp0i2bdxz79mrgmzjy 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/zj18li2vjxqp0i2bdxz79mrgmzjy 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Abb. 5: Beispiele von Betriebsumgebungen: VM, Cluster und Cloud Landing Zone</figcaption></figure>
<p>Die in Abbildung 5 gezeigten Beispiele für Betriebsumgebungen (VM, Cluster, Cloud Landing Zone) sind im GitOps-Kontext vollkommen valide, denn sie entsprechen der Beschreibung einer Betriebsumgebung (bzw. Laufzeitumgebung) des <a href="https://github.com/open-gitops/documents/blob/release-v1.0.0/GLOSSARY.md#software-system">OpenGitOps-Projekts</a>:</p>
<ol>
<li>Die Laufzeitumgebungen bestehen aus den zu verwaltenden Ressourcen.</li>
<li>Die Management-Agents laufen innerhalb jeder Laufzeitumgebung.</li>
<li>Es gibt Richtlinien zur Kontrolle des Zugriffs und der Verwaltung von Repos, Deploys und Runtimes.</li>
</ol>
<p>Im Übrigen gehört ein GitOps-konformes Pull-basiertes Deployment für stark abstrahierte Plattformen wie Heroku und <a href="https://www.netlify.com/">Netlify</a> zur Standardfunktionalität. Damit ist also die Betriebsumgebung definiert durch ein Projekt bei einer dieser Plattformen.</p>
<h3><a id="isolation" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#isolation">Isolation</a></h3>
<p>GitOps eröffnet eine sichere Möglichkeit, Anwendungen zu deployen, weil der zentrale Build-Server wegfällt, der alle möglichen Applikationen einer gesamten Organisation verwaltet. Sicherheit bietet GitOps dadurch, dass der Operator innerhalb einer spezifischen Betriebsumgebung lebt, in der auch die Anwendungen selbst verwaltet werden. Was darüber hinaus allerdings noch sichergestellt werden muss, ist Isolation.</p>
<figure>
<img alt="Abb. 6: Definition der Resource Boundary: Team, Domain, Applikation" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/n1w915yd9xj2rruw5qov60fe3wlz 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/n1w915yd9xj2rruw5qov60fe3wlz 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/n1w915yd9xj2rruw5qov60fe3wlz 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/n1w915yd9xj2rruw5qov60fe3wlz 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/n1w915yd9xj2rruw5qov60fe3wlz 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/n1w915yd9xj2rruw5qov60fe3wlz 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/n1w915yd9xj2rruw5qov60fe3wlz 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/n1w915yd9xj2rruw5qov60fe3wlz 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/n1w915yd9xj2rruw5qov60fe3wlz 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Abb. 6: Definition der Resource Boundary: Team, Domain, Applikation</figcaption></figure>
<p>Um den Operator von Ressourcen zu isolieren, die nicht verwaltet werden sollen, und das System vor Angriffen durch die Manipulationen der Infrastrukturkonfiguration zu schützen, sind zwei Fragen zu beantworten:</p>
<ul>
<li>Soll ein Operator pro Team, Domain oder Applikation zum Einsatz kommen?</li>
<li>Gilt es innerhalb dieser Dimensionen noch zusätzlich pro Stage (Dev, Staging, Prod) zu unterscheiden?</li>
</ul>
<p>Sind diese Fragen beantwortet, geht es darum, die Resource Boundary zu definieren<a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fn:8" id="fnref:8" title="see footnote" class="footnote">[8]</a>. Sie gibt an, welche Ressourcen der Operator konkret verwalten darf. Eine Applikation, die als Self-Contained System (SCS)<a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fn:9" id="fnref:9" title="see footnote" class="footnote">[9]</a> konzipiert ist, kann aus mehreren Komponenten bestehen, wie etwa Web-Applikationen, Scheduler, Funktionen und S3 Buckets.</p>
<p>Doch sollten auch Daten, Netzwerkkonfiguration, Quotas und Berechtigungskonfigurationen (IAM) von der Resource Boundary eingeschlossen werden? Im Hinblick auf die Sicherheit macht es einen großen Unterschied, ob der Operator lediglich die Berechtigungen für ein S3 Bucket setzen darf oder auch in der Lage ist, neue Benutzungskonten zu erstellen. Überlegungen dazu sind aus Sicherheitsgründen essenziell, vor allem, wenn die Betriebsumgebung eine vollständige Cloud-Umgebung ist.</p>
<h3><a id="fazit:dahilftnurselbstbauen" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fazit:dahilftnurselbstbauen">Fazit: Da hilft nur selbst bauen</a></h3>
<p>Es gibt Organisationen und Softwareprojekte, für die das Betreiben ihrer Anwendungen in Kubernetes keine Option ist. In diesen Fällen kann ein selbst gebauter GitOps-Operator Abhilfe schaffen. Dabei können Workflow-Engines und vorhandenes Deployment und Provisioning-Tooling als Grundlage dienen. In Kombination mit einer geeigneten Ressourcenisolation über die Einschränkung der Operator-Berechtigungen kann ein GitOps-Workflow auch in erweiterten Betriebsumgebungen wie Cloud Landing Zones umgesetzt werden.</p>
<hr>
<p>Vielen Dank an meine Kollegen <a href="https://www.innoq.com/de/staff/eberhard-wolff/">Eberhard Wolff</a>, <a href="https://www.innoq.com/de/staff/joachim-praetorius/">Joachim Praetorius</a>, <a href="https://www.innoq.com/de/staff/lucas-dohmen/">Lucas Dohmen</a>, <a href="https://www.innoq.com/de/staff/michael-vitz/">Michael Vitz</a>, <a href="https://www.innoq.com/de/staff/sascha-selzer/">Sascha Selzer</a> und <a href="https://www.innoq.com/de/staff/theo-pack/">Theo Pack</a> für das Feedback zu einer früheren Version des Artikels.</p>
<h3><a id="quellen" href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#quellen">Quellen</a></h3>
<foot-notes class="footnotes">
<ol class="footnotes__list">
<li id="fn:1">
<p><a href="https://www.innoq.com/de/books/gitops">https://www.innoq.com/de/books/gitops</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fnref:1" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
<li id="fn:2">
<p><a href="https://github.com/open-gitops/documents/blob/release-v1.0.0/PRINCIPLES.md">https://github.com/open–gitops/documents/blob/release–v1.0.0/PRINCIPLES.md</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fnref:2" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
<li id="fn:3">
<p><a href="https://github.com/open-gitops/documents/blob/release-v1.0.0/GLOSSARY.md#desired-state">https://github.com/open–gitops/documents/blob/release–v1.0.0/GLOSSARY.md#desired–state</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fnref:3" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
<li id="fn:4">
<p><a href="https://github.com/open-gitops/documents/blob/release-v1.0.0/GLOSSARY.md#reconciliation">https://github.com/open–gitops/documents/blob/release–v1.0.0/GLOSSARY.md#reconciliation</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fnref:4" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
<li id="fn:5">
<p><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/migration-aws-environment/understanding-landing-zones.html">https://docs.aws.amazon.com/prescriptive–guidance/latest/migration–aws–environment/understanding–landing–zones.html</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fnref:5" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
<li id="fn:6">
<p><a href="https://cloud.google.com/architecture/landing-zones">https://cloud.google.com/architecture/landing–zones</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fnref:6" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
<li id="fn:7">
<p><a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone">https://learn.microsoft.com/en–us/azure/cloud–adoption–framework/ready/landing–zone</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fnref:7" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
<li id="fn:8">
<p><a href="https://youtu.be/-Jo1VhWF4PU?t=1036">https://youtu.be/–Jo1VhWF4PU?t=1036</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fnref:8" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
<li id="fn:9">
<p><a href="https://scs-architecture.org">https://scs–architecture.org</a> <a href="https://www.innoq.com/de/articles/2024/02/gitops-kubernetes/#fnref:9" title="return to body" class="reversefootnote"> ↩</a></p>
</li>
</ol>
</foot-notes>
</body></html>
https://www.innoq.com/en/news/2024/02/kartenspiel-arcard-softwarearchitektur/2024-02-12T00:00:00+01:002024-02-14T10:28:17+01:00INNOQinfo@innoq.comDas neue Lernkartenspiel „ARCARD“<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Markus Harrer veröffentlicht über unseren Trainingspartner socreatory ein neues Kartenspiel, mit
dem Teilnehmende unserer Architekturschulungen wichtige Begriffe aus dem
Softwarearchitekturumfeld spielerisch erlernen.
</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<figure style="padding: 0 5%;">
<img src="https://res.cloudinary.com/innoq/image/upload/uploads-production/extmzt6da8koaalgoczynoeut7zb" alt="Die finale Version des Kartenspiels" loading="lazy">
</figure>
<long-quote person="Markus Harrer" role="Erfinder von ARCARD, Senior Consultant bei INNOQ" avatar="https://res.cloudinary.com/innoq/image/upload/uploads-production/y5bw5adgsy2i0vlezqofbkivxxmh">
<blockquote class="longquote">
<span class="longquote__zigzag"></span><p>Bei ARCARD geht es darum, in einer Spielgruppe den am besten passenden Begriff für eine Lücke in
einem Satz zu wählen. Das Besondere daran ist, dass dazu aus zwei Begriffen abgewogen werden
muss. Zudem können auch sehr unpassende Begriffe durch die Spielenden motiviert werden, wenn
diese über die notwendigen Kommunikations-Skills, Kreativität und Überzeugungskraft verfügen.</p>
<div class="longquote__avatar avatar avatar--sm"><img class="avatar__image" src="https://res.cloudinary.com/innoq/image/upload/uploads-production/y5bw5adgsy2i0vlezqofbkivxxmh" alt=""></div>
<cite class="longquote__author" itemprop="name">Markus Harrer</cite><span class="longquote__role" itemprop="jobTitle">Erfinder von ARCARD, Senior Consultant bei INNOQ</span>
</blockquote>
</long-quote>
<p>Das Kartenspiel ist als Geschenk für die Teilnahme an ausgewählten Softwarearchitekturschulungen sowie an den Konferenzständen von socreatory erhältlich. Eine Version zum Selberdrucken findet ihr auf <a href="https://www.socreatory.com/de/trainers/markus-harrer#:~:text=Arcard">socreatory.com</a>.</p>
<p>Das finale Kartenset könnt ihr außerdem auf LinkedIn in einem kurzen <a href="https://www.linkedin.com/posts/socreatory_softwarearchitektur-lernspiele-activity-7136292876971302912-a-R_/">Vorstellungsvideo</a> sehen oder ihr schaut bei <a href="https://www.youtube.com/watch?v=FFcquNj4y_g">„Softwarearchitektur im Stream“</a> vorbei, wo eine Runde live gespielt wird.</p>
<div class="text-center">
<a class="btn btn--cta" data-label="Zu unseren Schulungen" href="https://www.socreatory.com/de/ref-news-arcard">Zu unseren Schulungen</a>
</div>
</body></html>
https://www.innoq.com/en/links/813/2024-02-09T00:00:00+01:002024-02-09T09:16:33+01:00INNOQinfo@innoq.comMarkus Harrer über seine Rolle als Software Development Analyst und Software Evolutionist<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<blockquote>
<p>Wir entdecken die Geheimnisse hinter der Software-Revitalisierung mit Markus Harrer von INNOQ. Vom Nürnberger Software Architecture Meetup bis zu den neuesten Trends in der Software-Architektur – Markus teilt seine wertvollen Erfahrungen und gibt Einblicke in seine Rollen als Software Development Analyst und Software Evolutionist. </p>
</blockquote>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<blockquote>
<p>Wir entdecken die Geheimnisse hinter der Software-Revitalisierung mit Markus Harrer von INNOQ. Vom Nürnberger Software Architecture Meetup bis zu den neuesten Trends in der Software-Architektur – Markus teilt seine wertvollen Erfahrungen und gibt Einblicke in seine Rollen als Software Development Analyst und Software Evolutionist. </p>
</blockquote>
</body></html>
https://www.innoq.com/de/articles/2024/02/ssr-components/2024-02-09T00:00:00+01:002024-02-09T08:49:27+01:00Kombinierbare Elemente für Benutzungsoberflächen im WebMichael Vitzmichael.vitz@innoq.comhttps://www.innoq.com/en/staff/?person=michael-vitz<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Die Art und Weise, wie wir Benutzungsoberflächen im Web entwickeln, hat sich
über die letzten Jahre stark verändert. In dieser Kolumne wollen wir uns
deswegen ansehen, wie ein moderner Ansatz aussieht und vor welche
Herausforderungen dieser, vor allem für klassische Templateengines der
Java-Welt, uns stellt.</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Benutzungsoberflächen in Webanwendungen bestehen zwingend aus HTML und CSS.
Durch diese werden die Struktur und deren Aussehen beschrieben. Dabei ist es
erst einmal egal, ob wir das HTML bereits auf dem Server erzeugen und direkt an
den Browser senden oder ob dieses innerhalb des Browsers per JavaScript erzeugt
wird.</p>
<p>Um zu HTML und CSS zu kommen, wurde dabei in der Vergangenheit häufig Seite für
Seite in Form von Bildern, in Photoshop oder ähnlichen Tools, erstellt. Diese
wurden dann innerhalb der Anwendung, vielfach Pixel genau, in HTML und CSS
übersetzt. Bereits in diesem Ansatz kamen, wenn auch sehr implizit und
versteckt, Komponenten zum Einsatz. Tauchte ein Element, beispielsweise ein
Button, auf mehreren Seiten auf, wurde dieser, in der Regel, nicht jedes Mal
komplett neu und anders designt, sondern glich sich. Mit der Zeit haben wir
deswegen gelernt, dass es sinnvoller und effektiver ist, diese Komponenten zu
spezifizieren und die Seiten der Anwendung aus diesen zu kombinieren.</p>
<p>Hierzu werden Komponentenbibliotheken erstellt. Diese entstehen, idealerweise,
in enger Zusammenarbeit von Menschen mit Erfahrung in User Experience, User
Interface Design und Frontendentwicklung. Beispiele für solche Bibliotheken gibt
es viele. Die wohl bekanntesten Implementierungen sind <a href="https://getbootstrap.com">Bootstrap</a> und
<a href="https://mui.com">Material UI</a>. Aber auch <a href="https://polaris.shopify.com">Shopify Polaris</a>, <a href="https://sapui5.hana.ondemand.com">SAP UI5</a> oder
<a href="https://innoq.style">INNOQ</a> sind offen ansehbare Komponentenbibliotheken.</p>
<p>Eine solche Komponentenbibliothek hat dabei mehrere Aufgaben. Als Erstes gibt
sie einen Überblick über alle vorhandenen, und damit nutzbaren, Komponenten.
Jede Komponente selbst besteht dann aus der Dokumentation, ihrer Schnittstelle
und dem notwendigen HTML und CSS, das benötigt wird, um diese in der
eigentlichen Anwendung zu verwenden. Die Dokumentation sollte dabei neben
technischen Belangen vor allem beschreiben, für welche Anwendungsfälle die
Komponente gedacht ist. Je nach Komponentenbibliothek stellt diese sogar fertige
Bibliotheken für eine bestimmte Templateengine, wie Material UI für <a href="https://react.dev">React</a>,
zur Verfügung.</p>
<h3><a id="schnittstelleeinerkomponente" href="https://www.innoq.com/de/articles/2024/02/ssr-components/#schnittstelleeinerkomponente">Schnittstelle einer Komponente</a></h3>
<p>Wie bereits angeteasert, besitzt jede Komponente, neben dem eigentlichen Markup
und Styling, auch eine Schnittstelle. Betrachten wir hierzu die
<a href="https://getbootstrap.com/docs/5.3/components/card/">Card-Komponente aus Bootstrap</a>, die wie in Abbildung 1
dargestellt aussieht.</p>
<figure>
<img alt="" id="image_01" title="Abb. 1: Bootstrap-Card-Beispiel" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/4hq9pa7ft1mflbz8sfc172yf3b8z 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/4hq9pa7ft1mflbz8sfc172yf3b8z 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/4hq9pa7ft1mflbz8sfc172yf3b8z 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/4hq9pa7ft1mflbz8sfc172yf3b8z 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/4hq9pa7ft1mflbz8sfc172yf3b8z 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/4hq9pa7ft1mflbz8sfc172yf3b8z 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/4hq9pa7ft1mflbz8sfc172yf3b8z 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/4hq9pa7ft1mflbz8sfc172yf3b8z 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/4hq9pa7ft1mflbz8sfc172yf3b8z 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Abb. 1: Bootstrap-Card-Beispiel</figcaption></figure>
<p>Hierbei handelt es sich um eine primär visuelle Komponente, die in der Basis um
einen Block von Text einen Rahmen setzt, um diesen Block hervorzuheben.
Zusätzlich ist es jedoch auch möglich, einen Header und Footer zu verwenden. Und
auch der Inhalt ist nicht rein auf Text beschränkt, sondern kann andere
Komponenten, wie hier einen Button, beinhalten.</p>
<p>Wollen wir nun exakt diese Card in einer Anwendung einsetzen, müssen wir das
dokumentierte HTML-Markup, siehe Listing 1, in diese kopieren. Dieses Markup
besteht nun aus technischen Details, wie den konkreten CSSKlassen, und dem
eigentlichen Inhalt. Hierzu zählt neben den eigentlichen Texten auch der
enthaltene Button. Bevor wir also diesen Code blind in unsere Anwendung
kopieren, müssen wir uns, mithilfe der Dokumentation der Komponente, erarbeiten,
welche Teile wirklich zur eigentlichen Komponente gehören, welche variabler
Bestandteil dieser sind und was dann individueller Inhalt ist.</p>
<figure><div class="highlight" title="Listing 1: HTML-Markup für Bootstrap-Card-Beispiel"><pre class=""><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"card text-center"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card-header"</span><span class="nt">></span>
Featured
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card-body"</span><span class="nt">></span>
<span class="nt"><h5</span> <span class="na">class=</span><span class="s">"card-title"</span><span class="nt">></span>Special title treatment<span class="nt"></h5></span>
<span class="nt"><p</span> <span class="na">class=</span><span class="s">"card-text"</span><span class="nt">></span>With supporting text ...<span class="nt"></p></span>
<span class="nt"><a</span> <span class="na">href=</span><span class="s">"#"</span> <span class="na">class=</span><span class="s">"btn btn-primary"</span><span class="nt">></span>Go somewhere<span class="nt"></a></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card-footer text-body-secondary"</span><span class="nt">></span>
2 days ago
<span class="nt"></div></span>
<span class="nt"></div></span></code></pre></div>
<figcaption>Listing 1: HTML-Markup für Bootstrap-Card-Beispiel</figcaption></figure>
<p>Benötigen wir in unserer Anwendung diese Card an mehreren Stellen, können wir an
jeder Stelle das notwendige Markup erneut, meistens durch Copy & Paste,
schreiben und die Stellen mit individuellem Inhalt anpassen. Dabei laufen wir
allerdings in die üblichen Probleme, die ein solches Vorgehen mit sich bringt.
Primär sind das Kopieren und Anpassen fehleranfällig und ineffizient. Außerdem
verursacht eine Änderung am Markup der Komponente hohen Aufwand. Anschließend
muss jede kopierte Stelle gefunden und nachgezogen werden. Wer einmal von
Bootstrap 4 auf 5 migrieren musste, weiß, wovon ich spreche. Kurz gesagt, wir
verstoßen hier sehr stark gegen das Konzept von <a href="https://www.martinfowler.com/ieeeSoftware/repetition.pdf">Don’t Repeat Yourself</a>.</p>
<p>Aufgrund dieser Erkenntnis enthalten alle gängigen, neueren, JavaScript
basierten Templateengines, wie <a href="https://angular.io">Angular</a>, <a href="https://lit.dev/docs/libraries/standalone-templates/">lit</a> oder <a href="https://facebook.github.io/jsx/">JSX</a>, Unterstützung,
um solche Komponenten zu kapseln. Dadurch werden wir bei der Komponentennutzung
nicht nur von der Aufgabe befreit, größere Mengen HTML-Markup zu kopieren,
sondern uns steht auch eine klare und definierte Schnittstelle zur Verfügung.
Außerdem kann nun die Implementierung innerhalb der Komponente geändert werden,
ohne dass dies Aufwand an allen Nutzungsstellen verursacht. Je nach konkreter
Technologie können wir nun sogar die Schnittstelle ändern und werden von einem
Compiler unterstützt, um alle Stellen, die wir ändern müssen, bequem zu finden.</p>
<h3><a id="klassischetemplateengines" href="https://www.innoq.com/de/articles/2024/02/ssr-components/#klassischetemplateengines">Klassische Templateengines</a></h3>
<p>Im Gegensatz zu den oben genannten neuen JavaScript basierten Templateengines
bieten uns die „klassischen“ Vertreter, die wir in der Regel auf Server-Seite
nutzen, wie <a href="https://github.com/HubSpot/jinjava">jinjava</a>, <a href="https://github.com/spullara/mustache.java">mustache.java</a> oder <a href="https://www.thymeleaf.org">Thymeleaf</a>, keine
direkte Abstraktion, um Komponenten zu schreiben und zu verwenden. Je nach
Engine können wir aber die vorhandenen Mittel nutzen, um zu einem ähnlichen
Ergebnis zu gelangen. Das funktioniert aber nicht immer.</p>
<p>Um herauszufinden, in welcher Engine was, wie funktioniert und um beurteilen zu
können, welche Engine eher geeignet ist, einen solchen Komponentenansatz
konsequent umzusetzen, haben ein paar meiner Kollegen von INNOQ eine
<a href="https://github.com/innoq-component-challenge">Komponenten-Challenge</a> ins Leben gerufen. Diese
definiert, aktuell, sechs Komponenten, die implementiert werden sollen. Diese
enthalten dabei exemplarische Herausforderungen, die uns in Projekten immer
wieder begegnet sind und im Folgenden vorgestellt werden.</p>
<h3><a id="komponentenderchallenge" href="https://www.innoq.com/de/articles/2024/02/ssr-components/#komponentenderchallenge">Komponenten der Challenge</a></h3>
<p>Die erste Komponente ist das <em>Badge</em>, eine visuelle Komponente, die einen Text
auf einem farbigen Hintergrund darstellt und häufig mit abgerundeten Ecken
daherkommt. Innerhalb der Challenge dient diese vor allem zur Überprüfung, ob es
überhaupt möglich ist, in einer Templateengine eine Komponente zu definieren und
wiederzuverwenden. Listing 2 zeigt das zu erzeugende Markup und, als Kommentar,
eine mögliche Syntax zur Verwendung.</p>
<figure><div class="highlight" title="Listing 2: Badge-Komponente"><pre class=""><code><span class="c"><!-- <badge type="alert">Critical!</badge> --></span>
<span class="nt"><span</span> <span class="na">class=</span><span class="s">"badge bg-danger"</span><span class="nt">></span>Critical!<span class="nt"></span></span></code></pre></div>
<figcaption>Listing 2: Badge-Komponente</figcaption></figure>
<p>Die zweite Komponente ist ein <em>Button</em>. Dieser basiert auf einem der mächtigsten
nativen HTML-Elemente, dem <code><button></code>. Dieses unterstützt, selbst ohne Aria mit
einzubeziehen, eine <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button">sehr hohe Anzahl von Attributen</a>. Diese Komponente
prüft deshalb, ob es in der Templateengine möglich ist, einen Mechanismus
anzubieten, um quasi beliebige Attribute durchzureichen, ohne jedes explizit in
der Schnittstelle definieren zu müssen. Das Ganze klingt erst mal wie ein Hack
oder Workaround, ist aber in der Praxis oft notwendig. Listing 3 zeigt auch
hier, beispielhaft, wie die Komponente aussehen soll. Die
<a href="https://github.com/innoq-component-challenge/pattern-lib/blob/main/button.md">Challenge selbst</a> listet noch weitere Kombinationen auf.</p>
<figure><div class="highlight" title="Listing 3: Button-Komponente"><pre class=""><code><span class="c"><!-- <mybutton cta class="text-uppercase">Click me!</mybutton> --></span>
<span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn btn-primary text-uppercase"</span><span class="nt">></span>Click me!<span class="nt"></button></span></code></pre></div>
<figcaption>Listing 3: Button-Komponente</figcaption></figure>
<p>Als Nächstes ist auch die schon vorgestellte <em>Card</em>-Komponente Teil der
Challenge. Diese dient vor allem dazu zu prüfen, ob es möglich ist, benannte
Blöcke zu verwenden. Eine Card besteht dabei aus den beiden optionalen Blöcken
<code>header</code> und <code>footer</code> und einem verpflichtenden Hauptblock. Wichtig ist dabei
vor allem, dass diese Blöcke nicht nur reinen Text akzeptieren, sondern es uns
auch ermöglichen, HTML-Markup, natürlich inklusive weiterer eigener Komponenten,
zu übergeben. Nur so kann eine Komposition von verschiedenen Komponenten
ermöglicht werden. Auch hier zeigt Listing 4, wie so eine Card aussehen kann.</p>
<figure><div class="highlight" title="Listing 4: Card-Komponente"><pre class=""><code><span class="c"><!--
<card>
Some content with a <badge>Badge</badge>
<slot="footer">Footer with <mybutton>Button</mybutton></slot>
</card>
--></span>
<span class="nt"><section</span> <span class="na">class=</span><span class="s">"card"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card-body"</span><span class="nt">></span>
<span class="nt"><p</span> <span class="na">class=</span><span class="s">"card-body"</span><span class="nt">></span>
Some content with a <span class="nt"><span</span> <span class="na">class=</span><span class="s">"badge bg-default"</span><span class="nt">></span>Badge<span class="nt"></span></span>
<span class="nt"></p></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card-footer"</span><span class="nt">></span>
Footer with <span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn"</span><span class="nt">></span>Button<span class="nt"></button></span>
<span class="nt"></div></span>
<span class="nt"></section></span></code></pre></div>
<figcaption>Listing 4: Card-Komponente</figcaption></figure>
<p>Mit der <em>List</em>-Komponente wird vor allem überprüft, ob es möglich ist, innerhalb
der Komponenten auch mit komplexeren Datentypen aus der Hostumgebung, wie Listen
oder Maps, umzugehen. Weiterhin kann diese Komponente auch dazu genutzt werden,
um zu gucken, ob es möglich ist, konkrete Typen zu erzwingen und diese
gegebenenfalls auch noch zur Kompilierungszeit zu verifizieren. Listing 5
enthält auch hier wieder ein Beispiel.</p>
<figure><div class="highlight" title="Listing 5: List-Komponente"><pre class=""><code><span class="c"><!--
<list ratio="1:3" items="{ 'key': 'value', 'more': 'content' }">
--></span>
<span class="nt"><dl</span> <span class="na">class=</span><span class="s">"row"</span><span class="nt">></span>
<span class="nt"><dt</span> <span class="na">class=</span><span class="s">"col-sm-3"</span><span class="nt">></span>key<span class="nt"></dt></span>
<span class="nt"><dd</span> <span class="na">class=</span><span class="s">"col-sm-9"</span><span class="nt">></span>value<span class="nt"></dd></span>
<span class="nt"><dt</span> <span class="na">class=</span><span class="s">"col-sm-3"</span><span class="nt">></span>more<span class="nt"></dt></span>
<span class="nt"><dd</span> <span class="na">class=</span><span class="s">"col-sm-9"</span><span class="nt">></span>content<span class="nt"></dd></span>
<span class="nt"></dl></span></code></pre></div>
<figcaption>Listing 5: List-Komponente</figcaption></figure>
<p>Die <em>Magic-Header</em>-Komponente benötigt, im Gegensatz zu allen vorherigen,
Zustand. Dieser wird benötigt, um innerhalb dieser Komponente Überschriften
automatisch mit dem richtigen Level zu versehen, ohne dass dies bei der
Verwendung explizit angegeben werden muss. Listing 6 zeigt auch dies.</p>
<figure><div class="highlight" title="Listing 6: Magic-Header-Komponente"><pre class=""><code><span class="c"><!--
<magic-header>
<header>Überschrift (h1)</header>
<magic-header>
<header>Überschrift (h2)</header>
</magic-header>
<header>Überschrift (wieder h1)</header>
</magic-header>
--></span>
<span class="nt"><h1></span>Überschrift (h1)<span class="nt"></h1></span>
<span class="nt"><h2></span>Überschrift (h2)<span class="nt"></h2></span>
<span class="nt"><h1></span>Überschrift (wieder h1)<span class="nt"></h1></span></code></pre></div>
<figcaption>Listing 6: Magic-Header-Komponente</figcaption></figure>
<p>Die sechste, und letzte, Komponente ist die <em>Field</em>-Group. Diese hat eigentlich
keine neue Herausforderung mehr, die nicht bereits von einer der vorherigen
Komponenten überprüft wurde. Vielmehr dient diese als komplexeres Beispiel aus
der realen Welt, da eine solche Komponente in fast jedem Projekt benötigt wird.
Dabei ist diese Komponente dafür verantwortlich, innerhalb einer <code><form></code> ein
<code><input></code>-Feld, inklusive <code><label></code> und optionalen Validierungsfehlern,
darzustellen.</p>
<p>Diese Komponente kann optional auch noch als erweiterte Version umgesetzt
werden. Diese Version sollte sehr stark mit dem verwendeten Webframework
interagieren können und Aspekte wie Validierungsfehler oder
Internationalisierung unterstützen.</p>
<p>In dieser Kolumne haben wir die Idee von UIKomponenten kennengelernt. Dabei
handelt es sich, zusammengefasst, um gekapselte und damit wiederverwendbare
Elemente einer Benutzungsoberfläche. Diese ermöglichen es uns, im Gegensatz zum
Ansatz mit Copy & Paste, Benutzungsoberflächen effizient und wartbar zu bauen.</p>
<p>Da viele der klassischerweise in Java eingesetzten Templateengines kein direktes
Konzept für solche Komponenten mitbringen, haben wir zusätzlich die
INNOQ-Komponenten-Challenge kennengelernt. Diese definiert, aktuell, sechs
beispielhafte Komponenten, um überprüfen zu können, ob und wie gut der
Komponentenansatz in einer konkreten Templateengine funktioniert. Neben der
Challenge enthält diese auch bereits eine Reihe von Implementierungen.</p>
<p>Auch wenn diese Kolumne etwas theoretischer und weniger Java zentriert war,
hoffe ich, dass Sie etwas dabei gelernt haben. An dieser Stelle möchte ich mich
gerne auch noch bei meinen Kollegen FND, Joachim, Lucas, Michael und besonders
bei Till bedanken. In diesem Artikel steckt meine Essenz von vielen Diskussionen
mit euch. Ich hoffe, er wird euren Ansprüchen gerecht.</p>
</body></html>
https://www.innoq.com/en/talks/2024/02/architecture-communication-canvas-isaqb-meetup-2024/2024-01-26T00:00:00+01:002024-01-26T08:29:53+01:00Benjamin Wolfbenjamin.wolf@innoq.comhttps://www.innoq.com/en/staff/benjamin-wolf/Vortrag: "Architecture Communication Canvas" by Benjamin Wolf — 2024-02-08 iSAQB Software Architektur Meetup Stuttgart (Stuttgart)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>In Projekten treffen wir oft auf zwei Arten von Architekturdokumentation:
Gar keine oder veraltet in gigantischem Umfang. Der Effekt ist der gleiche – es wird nichts mehr dokumentiert, begründet mit “Keine Zeit” oder “Findet niemand mehr”.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<h4><a id="darumwirdesimvortraggehen" href="https://www.innoq.com/en/talks/2024/02/architecture-communication-canvas-isaqb-meetup-2024/#darumwirdesimvortraggehen">Darum wird es im Vortrag gehen</a></h4>
<p>In Projekten treffen wir oft auf zwei Arten von Architekturdokumentation:
Gar keine oder veraltet in gigantischem Umfang. Der Effekt ist der gleiche – es wird nichts mehr dokumentiert, begründet mit “Keine Zeit” oder “Findet niemand mehr”.</p>
<p>Benjamin stellt Euch den Architecture Communication Canvas (ACC) vor, mit welchem Ihr in kurzer Zeit die wichtigsten Aspekte Eurer Architektur dokumentieren und kommunizieren könnt. Mit Praxisbezug zeigt Benjamin Euch, dass Dokumentation sparsam und dabei nützlich für Stakeholder sein kann – und Spaß macht!</p>
<h4><a id="agenda" href="https://www.innoq.com/en/talks/2024/02/architecture-communication-canvas-isaqb-meetup-2024/#agenda">Agenda</a></h4>
<ul>
<li>ab 18.00 Uhr Get-together mit Getränken</li>
<li>18.30 Uhr Vortrag von Benjamin Wolf zum Thema: Architecture Communication - Canvas</li>
<li>anschließend Diskussion / Austausch bei Snacks und Getränken</li>
</ul>
</body></html>
https://www.innoq.com/en/talks/2024/02/tech-stack-canvas/2023-11-27T00:00:00+01:002023-11-27T16:32:18+01:00Jörg Müllerjoerg.mueller@innoq.comhttps://www.innoq.com/en/staff/joerg-mueller/Vortrag: "Tech Stack Canvas" by Jörg Müller — 2024-02-07 INNOQ Technology Lunch 02/24 (Online)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Wart ihr schon mal in einer der folgenden Situationen?</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Wart ihr schon mal in einer der folgenden Situationen?</p>
<ul>
<li>Ein Stakeholder benötigt einen schnellen Überblick über die Technologien im Projekt und alles, was ihr findet, ist ein langes Dokument oder eine veraltete Wikiseite.</li>
<li>Am Anfang des Projektes sollten die wichtigsten Technologien festgelegt werden, aber es fehlt eine Checkliste, welche das sind.</li>
<li>Es gibt mehrere autonome Teams im Unternehmen und es fehlt der Überblick, wer welche Technologien einsetzt.</li>
</ul>
<p>Dann könnte euch der Tech Stack Canvas helfen. In diesem kurzen Vortrag wird diese Methode vorgestellt. Sie bietet ein einfaches Tool zum Erstellen, Dokumentieren und Kommunizieren aller wesentlichen Technologien, die in der Entwicklung eines Produktes eingesetzt werden.
Wir werden uns die wesentlichen Elemente und den Prozess zur Erstellung eines neuen Canvas ansehen. Ziel ist es, euer Team in die Lage zu versetzen, fundierte Technologieentscheidungen zu treffen und den Tech Stack den Stakeholdern effektiv zu vermitteln.</p>
<p>Einige Informationen findet ihr vorab unter: <a href="https://techstackcanvas.io">https://techstackcanvas.io</a></p>
</body></html>
https://www.innoq.com/de/blog/2024/02/was-treibt-dich-an/2024-02-07T00:00:00+01:002024-02-07T15:50:06+01:00Was treibt dich an?Markus Harrermarkus.harrer@innoq.comhttps://www.innoq.com/en/staff/markus-harrer/<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>In der heutigen schnelllebigen Welt der Softwareentwicklung ist die Modernisierung von Software eine unerlässliche Aufgabe, um mit ständig ändernden Anforderungen und Technologien Schritt zu halten. Es gibt immens viele Ansätze zur Modernisierung von Software. Aber es ist eine Herausforderung, die richtigen Werkzeugkästen in den richtigen Situationen zu finden. Wie schön wäre es, die eine «Ultimative Softwaremodernisierungsmethode™️» in den Händen zu halten, die all die Probleme mit den in die Jahre gekommenen Softwaresysteme lösen kann?</p>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Leider gilt auch in der Softwaremodernisierung, dass es sehr stark darauf ankommt, um welche Art der Modernisierung es sich handelt. Je nach Ausgangsbasis und Zielen sind unterschiedliche Kategorien von Werkzeugen notwendig. In diesem Artikel wird eine Einordnung möglicher Vorgehensweisen auf Basis der Auslöser eines Modernisierungsvorhabens vorgestellt: Strategie, Business oder Entwicklung.</p>
<h3><a id="strategie-initiiertesoftwaremodernisierung" href="https://www.innoq.com/de/blog/2024/02/was-treibt-dich-an/#strategie-initiiertesoftwaremodernisierung">Strategie-initiierte Softwaremodernisierung</a></h3>
<figure>
<img alt="Beispielablauf einer Strategie-initiierten Softwaremodernisierung" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/xmymg9nf5s0rvoem4g8x1eft0tbc 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/xmymg9nf5s0rvoem4g8x1eft0tbc 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/xmymg9nf5s0rvoem4g8x1eft0tbc 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/xmymg9nf5s0rvoem4g8x1eft0tbc 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/xmymg9nf5s0rvoem4g8x1eft0tbc 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/xmymg9nf5s0rvoem4g8x1eft0tbc 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/xmymg9nf5s0rvoem4g8x1eft0tbc 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/xmymg9nf5s0rvoem4g8x1eft0tbc 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/xmymg9nf5s0rvoem4g8x1eft0tbc 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Beispielablauf einer Strategie-initiierten Softwaremodernisierung</figcaption></figure>
<p>Bei der Strategie-initiierten Softwaremodernisierung liegt der Schwerpunkt auf der Umsetzung eines übergeordneten Transformationsprogramms oder -plans innerhalb eines Unternehmens. Typische Programme sind etwa «Digitale Transformation», «Cloud-Migration» oder unternehmensweite Konsolidierungsvorhaben. Charakteristisch für diese Art von Modernisierungsprogrammen ist, dass diese von der Unternehmensleitung oder der IT-Führungsebene initiiert sind und darauf abzielen, die gesamte IT-Infrastruktur und/oder Softwarelandschaft des Unternehmens zu modernisieren. Die Modernisierung wird im Einklang mit den strategischen Zielen und Bedürfnissen des Unternehmens geplant und durchgeführt, um langfristige Wettbewerbsvorteile (wieder) zu erzielen. Ausgangsbasis sind oft Strategiepapiere und grob beschriebene Transformationsprogramme. Meist sind die Ziele der Modernisierung noch nebulös beschrieben («schnellere Time-to-Market», «360° Customer Excellence», «Digitalisierungspartner für Branche XY werden»). Doch der Umfang dieser Art der Modernisierung kann enorm sein, da er nicht nur Veränderungen in der Technik umfasst, sondern auch Prozesse, Arbeitsabläufe und Organisationsstrukturen. Daher unterliegen Strategie-initiierte Softwaremodernisierungen einem hohen Risiko des Scheiterns, da im Worst Case über Jahre hinweg unklare Ziele mit extrem viel Aufwand im zwei- bis dreistelligen Millionenbereich verfolgt werden. Zudem sind typische Herausforderungen ein fehlendes Verständnis in der vorhandenen Belegschaft, ein großer Gap zwischen den Wünschen und der vorhandenen IT, sowie dadurch eine inhärente Orientierungslosigkeit und institutionelle Überforderung. Daher wird bei dieser Modernisierungsvariante neben einem separaten Budget auch ein Fokus auf ein umfassendes Change Management notwendig.</p>
<p>Auf der IT-Seite ist es ratsam, konsequent die Konkretisierung der strategischen Ziele einzufordern. Insbesondere muss die Notwendigkeit und der Hintergrund der Programmdurchführung motiviert werden. Ein Check auf Verständlichkeit und der inhaltlichen Integrität der Veränderungsbedarfe ist dringend notwendig, um zu vermeiden, mit unterschiedlich verstandenen Zielen Aktionen loszutreten. Daher ist eine unabhängige Prüfung der vorhandenen Zukunftspläne unerlässlich. Danach erfolgt die Ausarbeitung bzw. die Aktualisierung der IT-Strategie. Im besten Fall enthält diese aus der Transformationsvision abgeleitete greifbare und verständliche Ziele. Auch können hier bereits schnell nachvollziehbar abgeleitete erste No-Brainer-Maßnahmen (z. B. Ausruf eines Einkaufsstopps von neuer On-Premise-Software, wenn Softwaresysteme in die Cloud migriert werden sollen) erarbeitet werden.</p>
<p>Da der Scope bei Programmen meist sämtliche IT-Systeme in einem Unternehmen umfasst, ist hier die Erarbeitung eines Überblicks z. B. in Form eines Softwareinventars sinnvoll. Das Unternehmen muss wissen, wo es aktuell steht, bevor die Schritte in Richtung der Ziele vorgenommen werden können. Um dieses Situationsbewusstsein initial effizient zu erstellen, sollten betroffene Systeme und IT-Infrastrukturkomponenten als Blackbox grob erfasst werden, um sich anfangs nicht in Details zu verlieren. Relevanter sind die Zusammenhänge zwischen Systemen und der von ihnen benötigten Infrastruktur. Um Überblicke über die derzeitige Situation zu erhalten, sind hier Techniken aus dem klassischen Enterprise-Architektur-Umfeld wie ArchiMate unter Verwendung von Enterprise-Architektur-Management-Softwaresystem (z. B. leanIX) möglich. Innovative Startpunkte bieten hier die Wardley-Mapping-Technik zur wertorientierten Visualisierung der Zusammenhänge in der IT im Zusammenspiel mit Domain-Driven Design (DDD) zur Bewertung von Softwaresystemen. Für die Dokumentation von IT-Prozess- und Organisationsthemen können klassische Verfahren aus der Geschäftsprozessmodellierung als auch Organisationsschaubilder verwendet werden, um ein gemeinsames Bild über Abläufe und Personal zu gewinnen. Auf der anderen Seite liegen hier derzeit Team Topologies sowie Big Picture Event Stormings aus dem DDD derzeit im Trend, welche in agilen Umfeldern passender einsetzbar sind.</p>
<p>Damit eine Strategisch-initiierte Softwaremodernisierung nicht im Sande verläuft, braucht es hier auch eine grobe Modernisierungs-Roadmap, welche die geplanten Schritte, messbare Meilensteine und Zeitpläne für die Umsetzung des Transformationsprogramms festlegt, einschließlich der Priorisierung von notwendigen Initiativen und der zugeordneten Personalkapazitäten und Ressourcen. Leichtgewichtig kann dies etwa mit einer Technologie-Roadmap umgesetzt werden, in der verschiedene Praktiken und Technologien mit verschiedenen Support-Zeithorizonten verortet oder einfachen Lebensphasen (abwarten, ansehen, austesten, anwenden, aufhören, abbauen) versehen werden. Die detaillierten Ziele und klaren Prioritäten helfen bei der Fokussierung und dem späteren Check, ob das Gesamtvorhaben auf dem richtigen Weg ist. Dies schafft eine solide Basis, nachfolgend die Modernisierung einzelner Softwaresysteme und deren Infrastruktur Schritt für Schritt zielgerichtet anzugehen.</p>
<h3><a id="business-initiiertesoftwaremodernisierung" href="https://www.innoq.com/de/blog/2024/02/was-treibt-dich-an/#business-initiiertesoftwaremodernisierung">Business-initiierte Softwaremodernisierung</a></h3>
<figure>
<img alt="Beispielablauf einer Business-initiierten Softwaremodernisierung" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/ig2v8xfiejjjoftjbba5n3a26f6i 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/ig2v8xfiejjjoftjbba5n3a26f6i 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/ig2v8xfiejjjoftjbba5n3a26f6i 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/ig2v8xfiejjjoftjbba5n3a26f6i 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/ig2v8xfiejjjoftjbba5n3a26f6i 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/ig2v8xfiejjjoftjbba5n3a26f6i 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/ig2v8xfiejjjoftjbba5n3a26f6i 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/ig2v8xfiejjjoftjbba5n3a26f6i 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/ig2v8xfiejjjoftjbba5n3a26f6i 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Beispielablauf einer Business-initiierten Softwaremodernisierung</figcaption></figure>
<p>Die Business-initiierte Softwaremodernisierung bezieht sich auf die Modernisierung eines bestimmten Softwareprodukts (auch: Produktlinie oder Teil eines Produkts) aus geschäftlichen Gründen. Diese Art der Modernisierung wird in der Regel von den Produktmanagern oder Verantwortlichen für eine spezifische Produktsparte aufgrund von äußeren Einflüssen ausgelöst. Typischerweise hat die Business-initiierte Modernisierung einen starken Fokus auf die eigene Produktqualität, wo es darum geht, dass fachliche Anforderungen sowie Qualitätsanforderungen an das Produkt angemessen umgesetzt wurden (oder zukünftig umgesetzt werden können). Dazu konzentriert sich die Modernisierung vorwiegend auf die Verbesserung der Benutzererfahrung, die einfachere Erweiterung um neue Funktionen und Integrationsmöglichkeiten.</p>
<p>Quellen, um eine fundierte Zielvision eines Modernisierungsvorhaben zu erarbeiten, sind vorhandenes Kundenfeedback und Marktanforderungen als auch die konkreten Bedürfnisse neuer Benutzer und Benutzerinnen des Systems sowie die Weiterentwicklungsideen der Projektverantwortlichen. Wichtig ist hier auch die Klärung des notwendigen Veränderungsdrucks. Dieser kann etwa in Form von fundamental neuzudenkender fachlicher Ansätzen, höheren Qualitätsansprüchen oder geänderten Randbedingungen (z. B. aufgrund von regulatorischen Änderungen oder auch durch die bereits beschriebenen Modernisierungsprogramme) vorliegen. Konkret lassen sich diese Maßnahmen im Rahmen eines Architektur-Assessments (hohe Flugebene) oder Architektur-Reviews (tiefere Flugebene) durchführen. Hierdurch wird eine Analyse der Zielsetzung des Systems mit dem aktuellen Zustand vorgenommen. Teilnehmende sind hier nicht nur Beteiligte aus der Softwareentwicklung selbst, sondern vor allem Stakeholder mit Produkt-strategischer Entscheidungskompetenz (wie Produktmanager oder Sales).
In diesem Scope können zur Kommunikation der notwendigen architekturellen Veränderungen klassische Methoden aus der Architekturentwicklung wie arc42 verwendet werden. Hier bietet es sich an, eine Dokumentation der aktuellen Softwarearchitektur nachzuerfassen sowie die geplante Zielarchitektur detailliert auszuformulieren. Im Abgleich mit dem vorhandenen Status Quo des bestehenden Softwaresystems folgt aus einer Gap-Analyse ein Handlungsbedarf. Durch die anschließende, risikoadäquate Bewertung der anstehenden Änderungen sowie das Festlegen der Handlungsbereiche ergibt sich hier eine konkrete Todo-Liste zum Abarbeiten für das Softwareentwicklungsteam. Diese kann auch gezielte Abbaumaßnahmen von nicht mehr benötigter Anwendungsfunktionalität beinhalten.</p>
<p>Bei länger dauernden Umbaumaßnahmen kann auch das Erarbeiten und Festhalten einer Übergangsarchitektur sinnvoll sein, welche die Zwischenwelt beim Wandel vom Ist- in den Soll-Zustand des Softwaresystems beschreibt. Konkret können damit notwendige Mittel zur Modernisierung wie Datenmigrationen, inkrementelle Sanierungskonzepte oder konkrete Architekturtaktiken zur gezielten Verbesserung von Qualitätsdefiziten beschrieben werden. Die Implementierung der Übergangsarchitektur wird jedoch letztendlich entfernt, wenn die Modernisierung abgeschlossen ist.</p>
<p>Parallel laufende Anpassungen der Produktphilosophie und -Roadmap haben hier zudem einen fundamentalen Einfluss auf die Details dieser Modernisierungsvariante.</p>
<h3><a id="entwicklungs-initiiertesoftwaremodernisierung" href="https://www.innoq.com/de/blog/2024/02/was-treibt-dich-an/#entwicklungs-initiiertesoftwaremodernisierung">Entwicklungs-initiierte Softwaremodernisierung</a></h3>
<figure>
<img alt="Beispielablauf einer Entwicklungs-initiierten Softwaremodernisierung" loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/wuuj0nmuutgn7sd6kdd9mcvp8p2d 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/wuuj0nmuutgn7sd6kdd9mcvp8p2d 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/wuuj0nmuutgn7sd6kdd9mcvp8p2d 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/wuuj0nmuutgn7sd6kdd9mcvp8p2d 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/wuuj0nmuutgn7sd6kdd9mcvp8p2d 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/wuuj0nmuutgn7sd6kdd9mcvp8p2d 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/wuuj0nmuutgn7sd6kdd9mcvp8p2d 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/wuuj0nmuutgn7sd6kdd9mcvp8p2d 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/wuuj0nmuutgn7sd6kdd9mcvp8p2d 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Beispielablauf einer Entwicklungs-initiierten Softwaremodernisierung</figcaption></figure>
<p>Die Entwicklungs-initiierte Softwaremodernisierung konzentriert sich auf die Verbesserung spezifischer Problemstellungen innerhalb des Softwareprodukts selbst bzw. dessen Erstellung. Die Initiierung erfolgt durch direkt an der Produktentwicklung beteiligten Stakeholdern wie Softwareentwickelnde, Tester oder Product Owner. Auslöser sind typischerweise interne Probleme bei der effizienten Weiterentwicklung des Softwaresystems, wie Performance-Probleme, schlechte Wartbarkeit oder Security-Issues, aber auch Ineffizienzen beim Entwicklungsprozess. Diese sind von den einzelnen Mitentwickelnden selbstständig lösbar oder unter Hinzuziehung externer Experten bewältigbar.
Der Scope dieser Modernisierungsarbeiten benötigt keine zusätzlichen, außerhalb des Budgetrahmens liegenden Zusatzaufwände. Die Modernisierung (meist: Refactorings oder kleinere Restrukturierungen) können in der Regel innerhalb eines definierten Zeitrahmens konzentriert neben der eigentlichen Produktweiterentwicklung durchgeführt werden.</p>
<p>Generell ist dies die einfachste Art der Modernisierung, da hier viele Verfahren zur Durchführung existieren. Typische Startpunkte sind die Sammlung von aktuellen Problemen im Rahmen einer regelmäßig durchgeführten Retrospektive als auch kleine Workshops wie Pre Mortem oder Risk Storming. Oft sind die Probleme jedoch Projekt-intern bekannt und können direkt durch passende Maßnahmen wie Code-Refactorings, Bibliotheks-Refresh, Threat-Modeling, Performance-Optimierungen als auch durch Pair Programming oder Schulungen umgesetzt werden. Was inhaltlich modernisiert wird, entscheidet das konkret vorliegende Problem. Anhaltspunkte hierfür bieten etwa die Idee der Quality Driven Software Architecture, wodurch sich passende Lösungen in Form von Architekturtaktiken bei spezifischen Qualitätsproblemen identifizieren lassen, als auch die spezielle Fachliteratur in den vorhandenen Problemfeldern.</p>
<p>Die Entwicklungs-initiierte Softwaremodernisierung ist risikoärmer als die beiden anderen Varianten, da die durchgeführten Maßnahmen direkt Feedback in der weiteren Entwicklung erzeugen. Kontinuierlich durchgeführt, hält sie Softwaresysteme über einen langen Zeitraum «fit for purpose», ohne ein (vielleicht zu großes) Rad drehen zu müssen. Die wesentliche Herausforderung bei der Entwicklungs-initiierten Softwaremodernisierung in der Realität ist jedoch, dass dieser Variante wenig Aufmerksamkeit und das Verständnis der Notwendigkeit zukommt. Insbesondere bei Unternehmen, die nicht ursprünglich mit digitalen Geschäftsmodellen starteten, ist das Vertrauen und die Zuversicht in die passende und wirtschaftliche Umsetzung von Verbesserungen direkt aus den Teams heraus gering. Daher erfolgt diese Variante nicht in dem Maß, das notwendig wäre, um ein Softwaresystem dauerhaft erfolgreich weiterzuentwickeln. Dies führt oft dazu, dass nach einiger Zeit die beiden oben beschriebenen Initiierungen benötigt werden, da die Teams vor nicht mehr zu überwindenden Hürden stehen. Bei Unternehmen mit einer starken Engineering-Kultur ist die projekt-initiierte Softwaremodernisierung bei der täglichen Entwicklungsarbeit bereits in Fleisch und Blut übergegangen. Es wird nicht mehr nach einer Modernisierung gefragt, sondern notwendige Verbesserungen werden bei Problemen eigenständig durchgeführt.</p>
<h3><a id="zusammenfassung" href="https://www.innoq.com/de/blog/2024/02/was-treibt-dich-an/#zusammenfassung">Zusammenfassung</a></h3>
<figure>
<img alt="Verschiedenen Treiber von Softwaremodernisierungen, welche ineinander verschachtelt sind." loading="lazy" srcset="https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_400/v1/uploads-production/83edwyz8t81q328619eehc3ari2h 400w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_800/v1/uploads-production/83edwyz8t81q328619eehc3ari2h 800w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1024/v1/uploads-production/83edwyz8t81q328619eehc3ari2h 1024w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1200/v1/uploads-production/83edwyz8t81q328619eehc3ari2h 1200w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_1600/v1/uploads-production/83edwyz8t81q328619eehc3ari2h 1600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2048/v1/uploads-production/83edwyz8t81q328619eehc3ari2h 2048w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2300/v1/uploads-production/83edwyz8t81q328619eehc3ari2h 2300w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2600/v1/uploads-production/83edwyz8t81q328619eehc3ari2h 2600w, https://res.cloudinary.com/innoq/image/upload/c_limit,f_auto,q_auto,w_2800/v1/uploads-production/83edwyz8t81q328619eehc3ari2h 2800w" sizes="(min-width: 1400px) 1024px, 90vw">
<figcaption>Verschiedenen Treiber von Softwaremodernisierungen, welche ineinander verschachtelt sind.</figcaption></figure>
<p>Dieser Artikel hat die wesentlichen Charakteristika der verschiedenen Varianten von Software-Modernisierungsvorhaben herausgestellt und Vorschläge für Ansatzpunkte von auch umfangreichen Softwaremodernisierungen aufgeführt. Insgesamt unterscheiden sich das Vorgehen und die konkrete Maßnahmen zwischen Strategie-initiierten, Business-initiierten und Entwicklungs-initiierten Softwaremodernisierungen. Diese Unterscheidung hilft, am Anfang direkt in die richtige Werkzeugkiste zu greifen und dadurch ein Softwaremodernisierungsvorhaben nicht falsch zu beginnen. Denn auch hier gilt: «no size fits all»!</p>
<p><em>Vielen Dank an Stefan Paal und Joachim Praetorius für das Feedback zu einer früheren Version dieses Beitrags! Das Header-Bild wurde mit leonardo.ai generiert.</em></p>
</body></html>
https://www.innoq.com/en/podcast/004-legacy-modernisierung-4/2024-02-06T00:00:00+01:002024-02-06T14:56:16+01:00Dr. Stefan Paalstefan.paal@innoq.comhttps://www.innoq.com/en/staff/stefan-paal/Podcast #4: Legacy-Modernisierung: Ein Update für die Sozialwirtschaft. Zu Gast: Torsten Schlörmann, Geschäftsführer VRG IT<span class="translation_missing" title="translation missing: en.feed_contents.items.podcast_episode.cto_need_to_know.title, episode_number: 4, episode_title: Legacy-Modernisierung: Ein Update für die Sozialwirtschaft, episode_subtitle: Zu Gast: Torsten Schlörmann, Geschäftsführer VRG IT">Title</span><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>In Folge 4 diskutieren Stefan Paal und Torsten Schlörmann, Geschäftsführer der VRG IT, Modernisierungsansätze für Standardsoftware in der Sozialwirtschaft. Wie modernisiert man Softwareprodukte für die Leistungsdokumentation, Personalplanung und -abrechnung in sozialen Einrichtungen wie Lebens- oder Eingliederungshilfen? Produkte, die bereits bei hunderten von Kund:innen im Einsatz sind. Diese Folge bietet tiefe Einblicke in die Komplexität von Updates für Standardsoftware außerhalb des eigenen Unternehmens und wie man diesen Herausforderungen mit einem Fokus auf Kundennutzen und Effizienz begegnet.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>In Folge 4 diskutieren Stefan Paal und Torsten Schlörmann, Geschäftsführer der VRG IT, Modernisierungsansätze für Standardsoftware in der Sozialwirtschaft. Wie modernisiert man Softwareprodukte für die Leistungsdokumentation, Personalplanung und -abrechnung in sozialen Einrichtungen wie Lebens- oder Eingliederungshilfen? Produkte, die bereits bei hunderten von Kund:innen im Einsatz sind. Diese Folge bietet tiefe Einblicke in die Komplexität von Updates für Standardsoftware außerhalb des eigenen Unternehmens und wie man diesen Herausforderungen mit einem Fokus auf Kundennutzen und Effizienz begegnet.</p></body></html>
https://www.innoq.com/en/links/812/2024-02-05T00:00:00+01:002024-02-05T20:20:58+01:00INNOQinfo@innoq.comAndré Aulich on Migrate P5 Archive Server to the Cloud<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<blockquote>
<p>One of my customers has been using Archiware’s P5 Archive server for years to move unused media data from expensive, on-premises, high-performance storage to cheaper second tier storage. P5 Archive manages archived data very well and is able to preserve all kinds of file- and filesystem-related metadata across all kinds of storages. Over the years, the customer’s production environment has changed a couple of times to adapt to the availability of new technology, a preference of remote work of employees, and so on. This article explains what is required to migrate an on-premises P5 server to a new instance (cloud or new on-premises server) when it archives data to S3 storage like WASABI.</p>
</blockquote>
</body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<blockquote>
<p>One of my customers has been using Archiware’s P5 Archive server for years to move unused media data from expensive, on-premises, high-performance storage to cheaper second tier storage. P5 Archive manages archived data very well and is able to preserve all kinds of file- and filesystem-related metadata across all kinds of storages. Over the years, the customer’s production environment has changed a couple of times to adapt to the availability of new technology, a preference of remote work of employees, and so on. This article explains what is required to migrate an on-premises P5 server to a new instance (cloud or new on-premises server) when it archives data to S3 storage like WASABI.</p>
</blockquote>
</body></html>
https://www.innoq.com/en/podcast/144-enabling-teams/2024-02-05T00:00:00+01:002024-02-09T09:41:15+01:00Anja Kammeranja.kammer@innoq.comhttps://www.innoq.com/en/staff/anja-kammer/Michael Plödmichael.ploed@innoq.comhttps://www.innoq.com/en/staff/michael-pl%C3%B6d/Sven Johannsven.johann@innoq.comhttps://www.innoq.com/en/staff/sven-johann/Podcast #144: Enabling Teams. Feature Teams erfolgreich mit Spezialwissen unterstützenPodcast #144: Enabling Teams. Feature Teams erfolgreich mit Spezialwissen unterstützen<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Enabling Teams bilden einen der vier grundlegenden Teamtypen innerhalb des Frameworks von 'Team Topologies'. Über diesen Teamtyp sprechen Anja und Sven mit Michael, der kürzlich das dazugehörige Buch von Matthew Skelton und Manuel Pais ins Deutsche übersetzt hat. Es geht darum, was Enabling Teams auszeichnet, welche spezifischen Aufgaben sie innerhalb von Organisationen erfüllen können und auf welche Schlüsselkompetenzen es bei den Enablern besonders ankommt, um andere Teams im Erlernen und der Anwendung neuer Fähigkeiten zu unterstützen. </p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Enabling Teams bilden einen der vier grundlegenden Teamtypen innerhalb des Frameworks von 'Team Topologies'. Über diesen Teamtyp sprechen Anja und Sven mit Michael, der kürzlich das dazugehörige Buch von Matthew Skelton und Manuel Pais ins Deutsche übersetzt hat. Es geht darum, was Enabling Teams auszeichnet, welche spezifischen Aufgaben sie innerhalb von Organisationen erfüllen können und auf welche Schlüsselkompetenzen es bei den Enablern besonders ankommt, um andere Teams im Erlernen und der Anwendung neuer Fähigkeiten zu unterstützen. </p></body></html>
https://www.innoq.com/en/talks/2024/02/misserfolge-und-lehren-bei-der-anwendung-von-ddd-beispiele-aus-der-realen-welt-oop-2024/2023-10-24T00:00:00+02:002024-01-05T11:41:24+01:00Michael Plödmichael.ploed@innoq.comhttps://www.innoq.com/en/staff/michael-pl%C3%B6d/Vortrag: "Misserfolge und Lehren bei der Anwendung von DDD: Beispiele aus der realen Welt" by Michael Plöd — 2024-02-02 OOP 2024 (München)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>DDD ist momentan extrem populär, was zu falschen oder überzogenen Erwartungen führen kann. Diese werden in meinem Vortrag angesprochen. Ich stelle Ihnen anhand zahlreicher konkreter Beispiele aus der Praxis vor, wie man die meisten Probleme mit DDD vermeiden oder wie man mit DDD erfolgreich arbeiten kann. Dabei werden Facetten aus verschiedensten Blickwinkeln aufgegriffen: Entwicklung, Strategie, Fachmodellierung, Organisation und Agilität.
Der Vortrag soll ein Gespür für den Einsatz von DDD im Kontext Ihrer Organisation vermitteln.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>DDD ist momentan extrem populär, was zu falschen oder überzogenen Erwartungen führen kann. Diese werden in meinem Vortrag angesprochen. Ich stelle Ihnen anhand zahlreicher konkreter Beispiele aus der Praxis vor, wie man die meisten Probleme mit DDD vermeiden oder wie man mit DDD erfolgreich arbeiten kann. Dabei werden Facetten aus verschiedensten Blickwinkeln aufgegriffen: Entwicklung, Strategie, Fachmodellierung, Organisation und Agilität.
Der Vortrag soll ein Gespür für den Einsatz von DDD im Kontext Ihrer Organisation vermitteln.</p>
<p><strong>Zielpublikum:</strong> Architekt:innen, Entwickler:innen, Manager:innen</p>
<p><strong>Voraussetzungen:</strong> Grundverständnis von DDD</p>
<p><strong>Schwierigkeitsgrad:</strong> Fortgeschritten</p>
<h4><a id="extendedabstract" href="https://www.innoq.com/en/talks/2024/02/misserfolge-und-lehren-bei-der-anwendung-von-ddd-beispiele-aus-der-realen-welt-oop-2024/#extendedabstract">Extended Abstract</a></h4>
<p>Domain-Driven Design ist kein Patentrezept und löst kein Problem auf magische Weise. Die Herausforderungen und die Komplexität, die wir mit DDD zu bewältigen versuchen, sind schwierig und es gibt keinen einfachen Lösungsansatz. Dennoch gibt es eine wachsende Popularität und Wertschätzung für das Thema auf dem Markt, was zu überhöhten Erwartungen und schließlich zu Enttäuschungen führen kann.
Der Referent dieses Vortrags arbeitet seit 17 Jahren mit Domain-Driven Design an vielen Softwaresystemen und dieser Vortrag handelt von meinen Erfahrungen mit dem Scheitern. Glauben Sie mir: Ich bin oft gescheitert, aber es gibt immer eine Gelegenheit, etwas daraus zu lernen. Der Vortrag zielt darauf ab, Ihnen die Möglichkeit zu geben, aus den Fehlern von mir als Berater und den Teams/Organisationen, mit denen ich gearbeitet habe, zu lernen.
Der Vortrag behandelt Themen wie:</p>
<ul>
<li>Domain-Driven Design im Wasserfall</li>
<li>Ignoranz für Code (aka nur Fokus auf strategisches Design)</li>
<li>Übermäßiger Gebrauch von Mustern um ihrer selbst willen</li>
<li>Kulturelle Implikationen</li>
<li>Cargo-Kult</li>
<li>Developer Experience</li>
<li>Eingeschränkte Verfügbarkeit von Fachexperten</li>
<li>Umgang mit etablierten Modellierungstechniken/Methoden</li>
<li>Unkenntnis über die Definitionen/Bedeutung von Heuristiken und Mustern</li>
</ul>
<p>Dieser Vortrag zielt darauf ab, Ihnen eine Sensibilität für potenzielle Gefahren zu vermitteln, wenn Sie versuchen, DDD für Ihr Team und Ihre Organisation einzuführen, und wird nur reale Situationen beschreiben, die tatsächlich passiert sind. Jeder beschriebene Misserfolg wird mit einer Erkenntnis darüber einhergehen, wie man es besser machen kann.
Es handelt sich um einen interaktiven Vortrag, bei dem Ihnen, dem Publikum, Fragen und Umfragen (über ein Online-Tool) gestellt werden. Sie werden sich beteiligen können.</p>
</body></html>
https://www.innoq.com/en/news/2024/02/software-architecture-summit-2024/2024-02-01T00:00:00+01:002024-02-01T12:44:22+01:00INNOQinfo@innoq.comSoftware Architecture Summit 2024<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Trefft uns vom 11. bis 13. März 2024 beim Software Architecture Summit in München.
</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Das Weiterbildungsevent vor Ort bietet ein vielseitiges Programm, das von technischen und methodischen Aspekten moderner Softwarearchitektur über Softskills bis hin zur Karriereentwicklung reicht und für alle geeignet ist, egal ob Basic oder Advanced.</p>
<h3><a id="workshops" href="https://www.innoq.com/en/news/2024/02/software-architecture-summit-2024/#workshops">Workshops</a></h3>
<p>Während des dreitägigen Summits leiten unsere Trainer*innen praxisorientierte Workshops, die aktuelle Entwicklungen und Herausforderungen in der Softwarearchitektur beleuchten.</p>
<ul>
<li><p><a href="https://software-architecture-summit.de/beyond-architecture/business-fuer-software-architektinnen/">Business für Software Architekt:innen</a> (12.03.) mit Martina Freers und Jörg Müller</p></li>
<li><p><a href="https://software-architecture-summit.de/session/deep-dive-software-analytics/">Deep-Dive Software Analytics</a> (12.03.) mit Markus Harrer</p></li>
<li><p><a href="https://software-architecture-summit.de/session/architekturarbeit-im-mob/">Architekturarbeit im Mob</a> (13.03.) mit Nikolas Hermman (Breuninger), Joshua Töpfer (INNOQ)</p></li>
<li><p><a href="https://software-architecture-summit.de/softwarearchitecture/durch-bessere-requirements-zu-besseren-architekturen/">Durch bessere Requirements zu besseren Architekturen</a> (13.03.) mit Gernot Starke und Daniel Lauxtermann</p></li>
<li><p><a href="https://software-architecture-summit.de/session/bessere-architekturentscheidungen-tiger-kommt/">Bessere Architekturentscheidungen: Tiger kommt – weglaufen?</a> (13.03.) mit Gernot Starke</p></li>
</ul>
<p><em>Für alle, die sich frühzeitig entscheiden: Bis zum 8. Februar sind die Tickets zu einem reduzierten Preis erhältlich.</em></p>
<div class="text-center">
<a class="btn btn--cta" data-label="Mehr Infos zum Summit" href="https://software-architecture-summit.de/muenchen/">Mehr Infos zum Summit</a>
</div>
</body></html>
https://www.innoq.com/en/talks/2024/02/neue-qualitaeten-braucht-das-land-oop-2024/2023-10-11T00:00:00+02:002024-01-11T10:50:01+01:00Dr. Gernot Starkegernot.starke@innoq.comhttps://www.innoq.com/en/staff/gernot-starke/Vortrag: "Neue Qualitäten braucht das Land" by Dr. Gernot Starke — 2024-02-01 OOP 2024 (München)<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>Heutzutage erfordern Systeme eine hohe Bandbreite an Qualitäten: immer online, schnell, robust, elastisch, skalierbar und sicher, oder was auch immer Ihre Interessenvertreter unter Qualität verstehen.
Ich erkläre, was Software-Entwicklungsprojekte brauchen: spezifische, konkrete und überprüfbare Qualitätsanforderungen, und warum bestehende Normen (wie ISO) in dieser Hinsicht nicht ausreichen.
Abschließend zeige ich einen pragmatischen, leichtgewichtigen (Open-Source-) Ansatz, der zu spezifischen und umsetzbaren Qualitätsanforderungen führt.</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Heutzutage erfordern Systeme eine hohe Bandbreite an Qualitäten: immer online, schnell, robust, elastisch, skalierbar und sicher, oder was auch immer Ihre Interessenvertreter unter Qualität verstehen.
Ich erkläre, was Software-Entwicklungsprojekte brauchen: spezifische, konkrete und überprüfbare Qualitätsanforderungen, und warum bestehende Normen (wie ISO) in dieser Hinsicht nicht ausreichen.
Abschließend zeige ich einen pragmatischen, leichtgewichtigen (Open-Source-) Ansatz, der zu spezifischen und umsetzbaren Qualitätsanforderungen führt.</p>
<p><em>Zielpublikum:</em> Entwicklungsteams
<em>Voraussetzungen:</em> Projekterfahrung
<em>Schwierigkeitsgrad:</em> Fortgeschritten</p>
</body></html>