Connect with us

Entwicklung & Code

npm als Sicherheitsrisiko: Warum Angriffe zunehmen und wie man vorbeugen kann


Wer mit npm arbeitet, kennt das Risiko: Beim Installieren einer neuen Bibliothek oder beim Aktualisieren bestehender Pakete wird Code aus fremden Quellen in die eigene Umgebung übernommen. Dieser Code ist weder geprüft noch stammt er notwendigerweise von der Person, die offiziell als Maintainer geführt wird. Angreiferinnen und Angreifer kapern Accounts, schleusen Schadsoftware ein und gefährden dadurch Projekte und Infrastruktur. Die jüngste Malware „Shai-Hulud“ hat dies erneut in großem Stil demonstriert.


the next big thing – Golo Roden

the next big thing – Golo Roden

Golo Roden ist Gründer und CTO von the native web GmbH. Er beschäftigt sich mit der Konzeption und Entwicklung von Web- und Cloud-Anwendungen sowie -APIs, mit einem Schwerpunkt auf Event-getriebenen und Service-basierten verteilten Architekturen. Sein Leitsatz lautet, dass Softwareentwicklung kein Selbstzweck ist, sondern immer einer zugrundeliegenden Fachlichkeit folgen muss.

Die Schadsoftware verbreitete sich selbstständig über npm-Pakete. Hunderte Module waren betroffen, darunter zahlreiche mit zehntausenden bis hunderttausenden Downloads. Die Malware griff Daten ab, manipulierte GitHub-Workflows und infizierte auf diesem Weg weitere Pakete. Wer diese Pakete installierte, konnte unbemerkt Zugangsdaten verlieren, darunter Token für private Repositories. Im schlimmsten Fall erlangten Unbefugte so Kontrolle über Quellcode und Build-Pipelines.

Empfohlener redaktioneller Inhalt

Mit Ihrer Zustimmung wird hier ein externes YouTube-Video (Google Ireland Limited) geladen.

Risikofaktor npm // deutsch

Diese Vorfälle reihen sich ein in eine Serie von Supply-Chain-Angriffen im JavaScript-Ökosystem. 2018 wurde etwa das Paket event-stream kompromittiert, nachdem der ursprüngliche Maintainer es an eine fremde Person übergeben hatte. 2021 war ua-parser-js betroffen und schleuste Krypto-Miner ein. Das Muster ist stets ähnlich: Ein populäres Paket mit vielen Downloads wird übernommen, eine neue Version mit Schadcode erscheint und fließt sofort in unzählige Projekte.

Warum gerade das JavaScript-Ökosystem so anfällig ist, wird bei näherer Betrachtung deutlich. JavaScript verfügt über keine besonders umfangreiche Standardbibliothek. Daraus hat sich in den vergangenen 20 Jahren eine Kultur entwickelt, selbst kleinste Funktionen als eigenständige Pakete zu veröffentlichen: für das Trimmen von Strings, das Formatieren von Datumswerten oder das tiefe Klonen von Objekten. Aus wenigen Zeilen Code entstehen so Module mit Millionen Downloads. Selbst wer nur einige Dutzend direkte Abhängigkeiten pflegt, zieht in der Regel Hunderte oder Tausende transitive Pakete mit.


enterJS Web Security Day

enterJS Web Security Day

(Bild: Alexander Supertramp/Shutterstock.com)

Neun von zehn Webanwendungen haben Sicherheitslücken – höchste Zeit für Web Developer, zu handeln. Auf dem ersten enterJS Web Security Day am 9. Oktober 2025 geht es um automatisierte Sicherheitsprüfungen, den Einsatz von Passkeys und den Schutz vor KI-basierten Angriffen.

Hinzu kommt die äußerst niedrige Einstiegshürde beim Veröffentlichen von Paketen. Eine Überprüfung des Quellcodes, Code-Signing oder eine Identitätsprüfung sind bislang nicht verpflichtend. Ein einfaches npm publish genügt. Das hat die Community über Jahre hinweg enorm beflügelt und Innovationen ermöglicht, öffnet jedoch zugleich Tür und Tor für Angriffe. Jeder kann Pakete bereitstellen, und solange kein Verdacht aufkommt, bleiben sie verfügbar.

Besonders kritisch ist, dass npm beim Installieren sofort Skripte ausführt, die theoretisch nahezu unbegrenzte Möglichkeiten haben: Dateien lesen, Daten abgreifen, Systemkommandos ausführen. Da es keine Sandbox gibt, geschieht dies direkt in der Build-Umgebung, oft mit denselben Berechtigungen wie die CI/CD-Pipeline. Wer mit Node.js arbeitet, moderne Frontends mit React oder Vue entwickelt oder Build-Tools wie Webpack, Vite oder ESLint verwendet, ist praktisch immer Teil dieses Systems. Sich dem komplett zu entziehen, ist kaum möglich.

Über viele Jahre war bekannt, dass diese Strukturen ein erhebliches Sicherheitsrisiko darstellen, dennoch blieb der Handlungsdruck gering. GitHub, das npm vor Jahren übernommen hat, und Microsoft als Mutterkonzern haben lange Zeit nur minimale Schutzmechanismen etabliert. Zwar existiert mit npm audit eine Funktion, die bekannte Schwachstellen meldet. Doch diese beschränkt sich im Wesentlichen auf eine Abfrage gegen bekannte Sicherheitsdatenbanken (CVEs). Neue, noch nicht erfasste Schadsoftware bleibt unerkannt. Warnungen beim Installieren gibt es, sie lassen sich jedoch leicht übersehen.

Verpflichtende Sicherheitsmechanismen fehlten lange: keine Zwei-Faktor-Authentifizierung für Paketveröffentlichende, keine konsequente Prüfung der Herkunft von Releases, keine automatische Quarantäne für verdächtige Pakete. Das machte npm über Jahre hinweg zu einem idealen Spielfeld für Supply-Chain-Angriffe. Viele Organisationen verließen sich darauf, dass jemand in der Community schon rechtzeitig Alarm schlagen würde. Häufig geschah das tatsächlich, jedoch oft zu spät.

GitHub als Eigentümer hätte deutlich früher reagieren können – spätestens nach den ersten großen Vorfällen. Anstatt grundlegende Sicherheitsmechanismen einzuführen, beließ man es lange bei punktuellen Warnungen und dem Hinweis auf npm audit. Erst nach dem Shai-Hulud-Wurm scheint nun Bewegung in das Thema zu kommen. Erstmals werden verpflichtende Maßnahmen angekündigt: Künftig müssen alle, die Pakete veröffentlichen, Zwei-Faktor-Authentifizierung aktivieren. Token werden zeitlich befristet und fein granular, alte unbefristete Zugänge verschwinden. Damit soll es Angreiferinnen und Angreifern erschwert werden, einmal gestohlene Zugangsdaten langfristig auszunutzen.

Parallel treibt GitHub „Trusted Publishing“ voran. Damit sollen Releases kryptografisch signiert und aus einer sauberen Continuous-Integration-Pipeline stammen. Ziel ist es, die Herkunft von Veröffentlichungen nachweisbar zu machen und Manipulationen zu verhindern. Diese Richtung ist grundsätzlich sinnvoll, kommt jedoch spät. In anderen Bereichen, etwa App-Stores oder Linux-Distributionen, sind signierte Pakete und Build-Prüfungen seit Jahren Standard. npm hat diese Entwicklung lange verschlafen und muss nun aufholen.

Neben GitHub versuchen auch andere Werkzeuge, die Situation zu verbessern. Ein Beispiel ist pnpm, das mit Version 10.16 die Option minimumReleaseAge eingeführt hat. Damit lässt sich festlegen, dass neue Pakete oder Updates nicht sofort installiert werden, sondern erst, nachdem sie eine bestimmte Zeit verfügbar waren. So können Entwicklerinnen und Entwickler beispielsweise angeben, dass eine Version mindestens 24 Stunden alt sein muss, bevor sie in Projekten genutzt wird. Die Idee dahinter: Wenn Schadcode eingeschleust wird, soll die Community Zeit gewinnen, diesen zu entdecken und zu melden, bevor er sich weiterverbreitet.

Auf den ersten Blick wirkt dieser Ansatz pragmatisch. Er reduziert das Risiko, dass frisch kompromittierte Versionen sofort in Builds gelangen. Allerdings hat er auch klare Grenzen. Das Modell verlagert die Verantwortung vollständig auf die Nutzerinnen und Nutzer von Paketen. Es löst nicht das grundlegende Problem, dass prinzipiell jede Person beliebig Pakete veröffentlichen kann. Wer Updates verzögert, erhält zwar eine zusätzliche Beobachtungsphase, sitzt aber zugleich länger auf möglicherweise verwundbaren Versionen. Gerade bei Sicherheitsupdates ist das problematisch: Eigentlich möchte man diese so schnell wie möglich einspielen.

Auch die neuen Sicherheitsmaßnahmen von GitHub und Funktionen wie minimumReleaseAge bei pnpm sind damit eher Pflaster auf einer offenen Wunde. Sie zeigen zwar, dass endlich reagiert wird, doch die strukturellen Risiken bleiben. Das Fundament – ein offenes, schwer kontrollierbares Registry-System mit Millionen von Modulen – verändert sich nicht. Wer sich allein auf diese Änderungen verlässt, wird langfristig enttäuscht werden.

Damit bleibt die Verantwortung bei denjenigen, die npm aktiv nutzen. Organisationen und Einzelpersonen müssen selbst Schritte unternehmen, um Projekte abzusichern. Zahlreiche Risiken lassen sich mit vergleichsweise überschaubarem Aufwand verringern, wenn bewusst auf Abhängigkeiten geachtet, die Build-Pipeline gehärtet und laufend kontrolliert wird, welche Pakete tatsächlich ins System gelangen.

Der erste, einfache, aber wirkungsvolle Schritt besteht darin, Abhängigkeiten zu reduzieren. Prüfen Sie regelmäßig, welche Pakete wirklich notwendig sind. Jedes zusätzliche Modul ist ein potenzielles Einfallstor. Gerade im JavaScript-Umfeld besteht die Gewohnheit, selbst für triviale Aufgaben externe Pakete einzubinden. Häufig reicht es aus, kleine Hilfsfunktionen selbst zu schreiben oder native Funktionen der Sprache zu verwenden. Ein flacher Abhängigkeitsbaum senkt die Angriffsfläche erheblich.

Ebenso wichtig ist es, Versionen gezielt zu steuern. Anstatt flexible Versionsbereiche zu definieren, sollten exakte Versionsnummern eingesetzt werden. Lockfiles gehören ins Repository und sollten Teil des Build-Prozesses sein. CI/CD-Pipelines sollten nur freigegebene Versionen installieren, beispielsweise durch den Einsatz von npm ci oder pnpm install --frozen-lockfile. So wird verhindert, dass unbemerkt neue, potenziell kompromittierte Versionen eingebunden werden.

Auch beim Umgang mit Skripten ist Vorsicht geboten. Viele Angriffe verstecken sich in Pre- oder Post-Install-Hooks. Falls diese nicht benötigt werden, empfiehlt es sich, ihre Ausführung global zu deaktivieren, zum Beispiel über npm install --ignore-scripts. Wenn Skripte unvermeidbar sind, sollten sie gezielt geprüft und ihre Herkunft hinterfragt werden.

Ein weiterer wichtiger Bereich betrifft Vertrauen und Kontrolle. Aktivieren Sie konsequent Zwei-Faktor-Authentifizierung für alle Accounts, die Pakete veröffentlichen oder Token erzeugen. Verwenden Sie moderne Token-Formate, die zeitlich befristet und minimal berechtigt sind. Bewahren Sie geheime Zugangsdaten strikt getrennt vom Quellcode auf. Konfigurationsdateien mit Klartext-Passwörtern dürfen nicht ins Repository gelangen, Token gehören weder in Logs noch in ungeschützte Umgebungsvariablen. Fremder Code könnte ansonsten auf diese Informationen zugreifen.

Wer selbst Pakete veröffentlicht, sollte sich mit dem bereits erwähnten Konzept des Trusted Publishing vertraut machen. Dadurch lassen sich Releases kryptografisch signieren und ihre Herkunft aus einer definierten CI/CD-Pipeline nachweisen. Das erschwert es Angreiferinnen und Angreifern erheblich, sich in den Veröffentlichungsprozess einzuklinken, und stärkt gleichzeitig das Vertrauen der Nutzenden in die Integrität des Pakets.

Vor der Installation neuer Pakete ist eine manuelle Überprüfung empfehlenswert. Ein kurzer Blick in das zugehörige Repository hilft, die Vertrauenswürdigkeit einzuschätzen:

  • Gibt es aktuelle Commits?
  • Reagieren Maintainer auf Meldungen?
  • Sind plötzliche Eigentümerwechsel oder verdächtige Änderungen erkennbar?

Solche Prüfungen dauern nur wenige Minuten, können aber langfristig große Sicherheitsprobleme vermeiden.

Darüber hinaus ist laufendes Monitoring zentral. npm audit bietet eine schnelle Grundprüfung bekannter Schwachstellen. Tools wie Dependabot oder Renovate schlagen automatisch Sicherheitsupdates vor. Dienste wie Socket oder Snyk gehen sogar noch einen Schritt weiter, indem sie nicht nur bekannte Lücken erkennen, sondern auch auffällige Veränderungen beobachten – etwa neu eingeführte Installationsskripte. Entscheidend ist, Warnungen ernst zu nehmen und regelmäßig Zeit für deren Bearbeitung einzuplanen. Wer besonders vorsichtig agieren möchte, kann neue Versionen zunächst in einer internen Testumgebung beobachten, bevor sie produktiv genutzt werden. So bleibt Flexibilität erhalten, um sicherheitskritische Updates trotzdem sofort einzuspielen.

Ein unterschätzter Faktor ist die Sicherheit von Build- und Deployment-Pipelines. Wenn es Angreiferinnen oder Angreifern gelingt, diese zu kompromittieren, werden alle folgenden Releases unsicher – unabhängig davon, wie sorgfältig einzelne Pakete geprüft wurden. Die Build-Umgebung sollte nur wohldefinierte Registries verwenden und strikt von der Deployment-Umgebung getrennt sein. So lässt sich verhindern, dass ein kompromittierter Build-Prozess direkt auch das Ausrollen von Schadcode ermöglicht.

Der Umgang mit Geheimnissen ist in CI/CD-Umgebungen besonders heikel. API-Keys, Token und Passwörter dürfen weder in Logs erscheinen noch ungeschützt in Variablen stehen, die von Drittpaketen eingesehen werden könnten. Prinzipien wie Least Privilege sind hier entscheidend: Token sollten ausschließlich die minimal notwendigen Rechte besitzen. Ein reines Lese-Token ist in vielen Szenarien ausreichend und reduziert die Angriffsfläche erheblich.

Hilfreich ist zudem, automatisiert Metadaten zu erzeugen. Am Ende eines Builds kann ein Dependency-Tree erstellt und archiviert werden. Dadurch lässt sich bei Sicherheitsvorfällen nachvollziehen, welche Paketversionen im Einsatz waren. Tools wie npm ls oder pnpm list unterstützen dabei. Der Aufwand ist gering, der Nutzen im Ernstfall sehr groß.

Letztlich ist Sicherheit kein Einzelthema, sondern Teil der Teamkultur. Neue Abhängigkeiten sollten genauso sorgfältig geprüft werden wie eigener Code. Pull Requests, die neue Pakete einführen oder bestehende aktualisieren, verdienen besondere Aufmerksamkeit:

  • Braucht das Projekt dieses Paket wirklich?
  • Wird es aktiv gepflegt?
  • Gibt es ungewöhnliche Veränderungen?

Verbindliche Richtlinien sind hier hilfreich. Dokumentieren Sie beispielsweise, welche Registries akzeptiert werden, wie Lockfiles zu verwenden sind, ob Parameter wie --ignore-scripts standardmäßig gesetzt werden und welche Prüfwerkzeuge in der Pipeline laufen müssen. So entstehen feste Standards, die Sicherheit im Alltag erleichtern.

Auch organisatorisch sollte Zeit und Budget für Sicherheitsaufgaben eingeplant werden. Viele Probleme entstehen nicht aus Gleichgültigkeit, sondern aus Termindruck. Regelmäßige Updates, Audits, Schulungen und Verbesserungen der Build-Prozesse brauchen feste Kapazitäten. Ebenso wichtig ist es, informiert zu bleiben: Sicherheitsvorfälle wie Shai-Hulud werden häufig zuerst in Entwickler-Communities oder auf Plattformen wie GitHub bekannt. Wer entsprechende Feeds oder Benachrichtigungen abonniert, kann schneller reagieren.

Die Sicherheit im JavaScript-Ökosystem bleibt somit leider eine Daueraufgabe. GitHub und pnpm haben erste sinnvolle Schritte unternommen, aber sie kommen spät und lösen die grundlegenden Probleme nicht. Das offene, kaum kontrollierbare Netzwerk aus Millionen Paketen macht npm weiterhin anfällig. Wirklich geschützt ist nur, wer selbst Verantwortung übernimmt: Abhängigkeiten reduzieren, Versionen exakt pinnen, Lockfiles einsetzen, Accounts absichern, Monitoring und Auditing etablieren, CI/CD härten und Sicherheit als festen Bestandteil der Entwicklungskultur verankern.

Wer diese Maßnahmen konsequent umsetzt, kann die Risiken deutlich verringern. Angriffe wie Shai-Hulud verschwinden dadurch nicht, treffen jedoch Projekte seltener und weniger hart. So lässt sich schneller reagieren und im besten Fall verhindern, dass Schadcode überhaupt produktiv eingesetzt wird.


(rme)



Source link

Entwicklung & Code

Kommandozeile adé: Praktische, grafische Git-Verwaltung für den Mac


close notice

This article is also available in
English.

It was translated with technical assistance and editorially reviewed before publication.

Ein neues, kostenloses Git-Management-Tool vereinfacht die Arbeit mit der Versionierungssoftware Git. Viele Funktionen lassen sich zusammenfassen oder schnell und übersichtlich ausführen, auch in älteren Commits. Dabei verwaltet es mehrere lokale Repositories gleichzeitig.

Weiterlesen nach der Anzeige

Anbieter RemObjects schreibt im Blog, dass das macOS-Tool GitBrowser die Alltagsaufgaben von Entwicklerinnen und Entwicklern beim Versionsmanagement beschleunigen soll. Das Fenster des Tools ist dreigeteilt: In der linken Sidebar findet sich eine Liste der Repos, die sich gruppieren und umbenennen lassen. Entwickler führen hier Aktionen über das Kontextmenü aus – auch in nicht aktiven Projekten.

Der Mittelteil zeigt die Versionen eines Repos, und zwar noch zu pushende in Fett, noch zu pullende kursiv und noch zu mergende blau. Auch die verschiedenen Autoren sind farblich unterschiedlich gekennzeichnet. Rechts im Fenster finden sich die betroffenen Dateien eines Commits und darunter eine Diff-Ansicht. Bei Doppelklick auf einen Commit öffnet sich ein Diff-Tool des Anwenders, derzeit Araxis Merge oder BBEdit. Weitere sollen laut Anbieter hinzukommen.

Ganz oben im Fenster steht der lokale Status, beim Klick darauf öffnet sich rechts die Bühne mit Checkboxen zum Hinzufügen oder Entfernen von Dateien. Darunter steht ein dreifach Diff: eine originale, lokale und auf der Stage liegende Variante.

Commiten und Pushen lässt sich mit einem Klick, und die Commit-Nachricht lässt sich auf Wunsch bereits beim Stagen von einer KI erzeugen. Möglich sind hier OpenAI, Claude, Gemini, Grok, Mistral oder eine lokale Verknüpfung mit LM Studio. Wer selbst die Nachricht schreibt, kann mit Pfeiltasten in älteren Ausgaben blättern.

Pullen lassen sich alle Repos auf einen Schlag oder alle einer Gruppe. Anwender ziehen Dateien, auch aus älteren Commits, per Drag-and-drop in andere Tools – ohne Checkout – GitBrowser extrahiert sie automatisch. Der Wechsel zwischen Zweigen erfolgt einfach über einen Popup-Button.

Der Anbieter betont im Blog, dass GitBrowser nicht für tiefergehende Funktionen gedacht sei, sondern alltägliche Verwaltungsvorgänge erleichtern soll. Anspruchsvolle Anwenderinnen und Anwender werden ganz ohne Kommandozeile also doch nicht auskommen.

Weiterlesen nach der Anzeige

Lesen Sie auch


(who)



Source link

Weiterlesen

Entwicklung & Code

Clean Architecture und Co.: Softwarearchitektur mit Mustern strukturieren


close notice

This article is also available in
English.

It was translated with technical assistance and editorially reviewed before publication.

Strukturierte Software basiert auf einem Plan, der die spezifischen Anforderungen an ein System berücksichtigt und in lose gekoppelte Bausteine überführt. In der arbeitsteiligen Softwareentwicklung benötigen Entwicklungsteams solche gemeinsamen Pläne, um eine harmonische und einheitliche Architektur zu entwickeln, ohne jedes Detail vorab miteinander abstimmen zu müssen. Bewähren sich die Pläne, entwickeln sich daraus Muster und Prinzipien auf unterschiedlichen Architekturebenen.

Weiterlesen nach der Anzeige


Matthias Eschhole

Matthias Eschhole

Matthias Eschhold ist Lead-Architekt der E-Mobilität bei der EnBW AG. Als Experte für Domain-driven Design gestaltet er die IT-Landschaft und Team-Topologien der E-Mobilität. Trotz strategischer Schwerpunkte bleibt er mit Java und Spring Boot nah am Code, entwickelt Prototypen und führt Refactorings durch. Als Trainer vermittelt er seit Jahren praxisnahe Softwarearchitektur, die Theorie und Projektrealität verbindet.

Bei der grundlegenden Strukturierung eines Systems muss man zwischen Architekturstilen und Architekturmustern unterscheiden, wobei sie sich nicht immer sauber abgrenzen. Ein Architekturstil ist ein Mittel, das dem System eine grundlegende Struktur verleiht. Beim Stil Event-driven Architecture basiert die Anwendung beispielsweise auf asynchroner Kommunikation, und Events beeinflussen die Architektur und den Code an vielen Stellen. Gleiches gilt für REST, das eine ressourcenorientierte Struktur vorgibt.

Entscheidet sich ein Entwicklungsteam für Microservices als Architekturstil, wählt es eine verteilte Systemarchitektur, beim Stil Modularer Monolith ist das Gegenteil der Fall. In komplexen Systemen kombinieren Architektinnen und Architekten in der Regel mehrere Stile. Manche Architekturstile ergänzen sich, etwa REST und Microservices, während sich andere gegenseitig ausschließen, wie Microservices und der Modulare Monolith.

Ob Microservices oder Modularer Monolith – beides sagt wenig über die Gestaltung der internen Strukturen aus. Auf dieser inneren Architekturebene, der Anwendungsarchitektur, kommen Muster zum Einsatz, die Entwurfsprinzipien und -regeln kombinieren und eine Basisstruktur der Anwendung prägen. Architekturmuster der Anwendungsarchitektur nutzen Verantwortungsbereiche und Beziehungsregeln als Strukturierungsmittel. Im Muster Clean Architecture sind dies beispielsweise konzentrische Ringe, wobei die Beziehungsrichtung stets zum inneren Kern des Ringmodells führt. Die geschichtete Architektur (Layered Architecture) hingegen unterteilt die Verantwortungsbereiche in hierarchische Schichten, wobei jede Schicht nur mit der darunter liegenden kommunizieren darf (siehe Abbildung 1).


Infografik Vergleich zwischen Clean Architecture und Schichtenarchitektur

Infografik Vergleich zwischen Clean Architecture und Schichtenarchitektur

Vergleich zwischen Clean Architecture und Schichtenarchitektur (Abb. 1).

Eine Mustersprache ergänzt Architekturmuster für einen ganzheitlichen Konstruktionsplan – von Modulen und Paketen bis hin zum Klassendesign. Sie bildet das Fundament für eine konsistente und verständliche Umsetzung der Muster und beschreibt eine Reihe von Entwurfsmustern für die Programmierung auf der Klassenebene.

Weiterlesen nach der Anzeige

Die Klassen der Mustersprache bilden Geschäftsobjekte, Fachlogik und technische Komponenten ab. Sie werden unter Einhaltung der definierten Beziehungsregeln in einem Klassenverbund implementiert. Diese Regeln bestimmen, wie die Klassen miteinander interagieren, wie sie voneinander abhängen und welche Aufgaben sie haben. Ein Geschäftsobjekt ist charakterisiert durch seine Eigenschaften und sein Verhalten, während ein Service Geschäftslogik und fachliche Ablaufsteuerung implementiert. Eine derartige, genaue Differenzierung gestaltet Architektur klar und nachvollziehbar.

Ein wichtiger Aspekt einer Mustersprache ist die Organisation des Codes in einer gut verständlichen Hierarchie. Dadurch fördert sie die Verteilung von Verantwortlichkeiten auf unterschiedliche Klassen. Prinzipiell kann jedes Projekt seine eigene Mustersprache definieren oder eine bestehende als Basis verwenden und mit individuellen Anforderungen ausbauen. Eine Mustersprache sorgt auch im Team dafür, dass alle Mitglieder dieselben Begriffe und Prinzipien verwenden.

Dieser Artikel wählt die DDD Building Blocks als Grundlage für eine Mustersprache, wie die folgende Tabelle und Abbildung 2 zeigen.

Value Object Ein Value Object repräsentiert einen unveränderlichen Fachwert ohne eigene Entität. Das Value Object ist verantwortlich für die Validierung des fachlichen Werts und sollte nur in einem validen Zustand erzeugt werden können. Ferner implementiert ein Value Object dazugehörige Fachlogik.
Entity Eine Entity ist ein Objekt mit einer eindeutigen Identität und einem Lebenszyklus. Die Entität wird beschrieben durch Value Objects und ist verantwortlich für die Validierung fachwertübergreifender Geschäftsregeln sowie die Implementierung dazugehöriger Fachlogik.
Aggregate Ein Aggregate ist eine Sammlung von Entitäten und Value Objects, die durch eine Root Entity (oder Aggregate Root bzw. vereinfacht Aggregate) zusammengehalten werden. Die Root Entity definiert eine fachliche Konsistenzgrenze, klar abgegrenzt zu anderen Root Entities (oder Aggregates).
Domain Service Ein Domain Service implementiert Geschäftslogik, die nicht zu einer Entität oder einem Value Object gehört. Weiter steuert der Domain Service den Ablauf eines Anwendungsfalls. Ein Domain Service ist zustandslos zu implementieren.
Factory Eine Factory ist für die Erstellung von Aggregates, Entitäten oder Value Objects verantwortlich. Die Factory kapselt die Erstellungslogik komplexer Domänenobjekte.
Repository Ein Repository ist verantwortlich für die Speicherung und das Abrufen von Aggregaten und Entitäten aus einer Datenquelle. Das Repository kapselt den Zugriff auf eine Datenbank oder auch andere technische Komponenten.


Infografik Mustersprache des taktischen Domain-driven Design

Infografik Mustersprache des taktischen Domain-driven Design

Mustersprache des taktischen Domain-driven Design (Abb. 2).

Ein Beispiel verdeutlicht den Unterschied zwischen einem Value Object und einer Entity: Eine Entity könnte ein bestimmtes Elektrofahrzeug sein. Entities sind also eindeutig und unverwechselbar. In der realen Welt zeigt sich das an der global eindeutigen Fahrgestellnummer (VIN). Der aktuelle Zustand eines E-Fahrzeugs wird zu einem bestimmten Zeitpunkt beispielsweise durch seinen Ladezustand beschrieben, ein Wert, der sich im Laufe der Nutzung des Fahrzeugs verändert. Der Ladezustand entspricht einem Value Object. Er verfügt über keine eigene Identität, sondern definiert sich ausschließlich durch seinen Wert.

Die Mustersprache der Building Blocks ist nicht vollständig. Sie benötigt weitere Elemente, die von den eingesetzten Architekturstilen und -mustern abhängen. REST als Architekturstil führt beispielsweise zwei Elemente in die Mustersprache ein: Controller und Resource. Bei der Integration von REST als Provider liegt der Fokus auf der Resource, die als Datentransferobjekt (DTO) über den API-Endpunkt bereitsteht. Der Controller fungiert als Schnittstelle zwischen der Anfrage des Konsumenten und der Fachlogik des Systems. Das heißt, der Controller nutzt den bereits eingeführten Domain Service und delegiert die Ausführung von Fachlogik an diesen.

Bei der Integration von REST als Consumer erhält die Mustersprache das Element Service Client, das dem Abrufen von Daten oder Ausführen von Funktionen über einen externen API-Endpunkt dient. Der Domain Service triggert dies als Teil der Fachlogik über den Service Client.

Der Stil Event-driven Architecture erweitert die Mustersprache um die Elemente Event Listener, Event Publisher und das Event selbst. Ein Event Listener hört auf Ereignisse und ruft den entsprechenden Domain Service auf, um die Ausführung der Geschäftslogik auszulösen. Der Event Publisher veröffentlicht eine Zustandsveränderung in der Fachlichkeit über ein Event. Der Domain Service triggert die Event-Veröffentlichung als Teil seiner Fachlogik und nutzt hierfür den Event Publisher.

Die in diesen Beispielen aufgeführten Begriffe sind im Vergleich zu den DDD Building Blocks nicht in der Literatur definiert und entstammen der Praxis. Abbildung 3 zeigt die Klassen der erweiterten Mustersprache.


Infografik Elemente der Mustersprache des taktischen Domain-driven Design

Infografik Elemente der Mustersprache des taktischen Domain-driven Design

Elemente der Mustersprache des taktischen Domain-driven Design (Abb. 3).

Architekturmuster kombinieren Regeln, Entwurfsmuster und Prinzipien. Muster wie Clean Architecture, die sich besonders für komplexe Systeme mit hohen Anforderungen an den Lebenszyklus eignen, bündeln mehrere Konzepte und beeinflussen daher die Mustersprache stärker als andere Muster. Ein Beispiel ist das Konzept Use Case in der Clean Architecture, das ein zentrales Element darstellt und die Mustersprache um die Elemente Use Case Input Port, Use Case Output Port und Use Case Interactor erweitert. Ein weiteres Beispiel ist die Anwendung des Dependency Inversion Principle (DIP) in der Clean Architecture, das zu dem Musterelement Mapper führt.

Nach dem Exkurs über die Mustersprachen stellt dieser Artikel verschiedene Architekturmuster vor, die sich in schichten- und domänenbasierende unterteilen.

Schichtenbasierende Architekturmuster sind datenzentrisch strukturiert. Je nach Muster ist dieser Aspekt mehr oder weniger ausgeprägt. Die Schichtung unterscheidet sich in technischer (horizontal geschnitten) und fachlicher (vertikal geschnitten) Hinsicht. Für die weitere Beschreibung eignet sich die Begriffswelt von Simon Brown mit „Package by …“ .

Package by Layer: Dieses Muster organisiert die Anwendung nach technischen Aspekten, zum Beispiel nach Controller, Service und Repository (Abbildung 4). Es kommt jedoch schnell an seine Grenzen: Mittlere und große Systeme mit komplizierter Fachlichkeit erfordern eine vertikale Schichtung anhand fachlicher Aspekte, andernfalls enden die Projekte erfahrungsgemäß in komplizierten Monolithen mit vielen Architekturverletzungen.

Vorteile:

  • Bekannt und verbreitet
  • Einfach zu verstehen und anzuwenden
  • In kleinen Projekten praktikabel

Nachteile:

  • Enge Kopplung zwischen Schichten, mit der Gefahr chaotischer Abhängigkeiten bei Wachstum des Systems
  • Fachlich zusammenhängende Funktionalitäten sind über viele Pakete verteilt
  • Schwer wartbar und erweiterbar bei mittleren bis großen Anwendungen


Infografik Das Architekturmuster Package by Layer

Infografik Das Architekturmuster Package by Layer

Das Architekturmuster Package by Layer (Abb. 4).

Package by Feature: Der Code organisiert sich vertikal anhand fachlicher Aspekte. Eine Schnitt-Heuristik, wie genau das Feature von den fachlichen Anforderungen abzuleiten ist, definiert das Architekturmuster nicht. Es definiert nur, dass dieser fachliche Schnitt zu erfolgen hat. Wird das taktische DDD angewendet, erfolgt der Schnitt entlang der Aggregates (siehe Abbildung 5).

Vorteile:

  • Fachlich kohäsiver Code ist lokal zusammengefasst, was zu hoher Wartbarkeit und Erweiterbarkeit führt.
  • Modularisierung ermöglicht die unabhängige Entwicklung fachlicher Module.
  • Fachliche Ende-zu-Ende-Komponenten sind lose gekoppelt.
  • Abhängigkeiten zwischen fachlichen Modulen müssen explizit gehandhabt werden, was die Robustheit der Architektur gegenüber ungewünschten Abhängigkeiten erhöht.
  • Fachlich komplexe, mittelgroße bis große Anwendungen lassen sich mit vertikalen Schichten besser beherrschen als mit Package by Layer und Package by Component.

Nachteile:

  • Abhängigkeiten zwischen fachlichen Modulen erfordern fortgeschrittene Kommunikationsmuster (zum Beispiel Events), was die architektonische Komplexität erhöht.
  • Vertikale Modularisierung muss gut durchdacht werden, um enge Kopplung zwischen Modulen zu vermeiden.


Infografik Architekturmuster Package by Feature

Infografik Architekturmuster Package by Feature

Das Architekturmuster Package by Feature (Abb. 5).

Package by Component: Das Muster strukturiert die Anwendung sowohl fachlich (vertikal) als auch technisch (horizontal), wobei sich ein fachliches Feature in eine Inbound-Komponente und eine Domain-Komponente aufteilt (siehe Abbildung 6). Die Domain-Komponente kapselt Geschäftslogik und die dazugehörige Persistenzschicht. Diese Unterteilung in fachliche Module ist ein entscheidender Unterschied zu Package by Layer.

Vorteile:

  • Gute Modularisierung durch fachliche Grenzen zwischen Komponenten
  • Hohe Wiederverwendbarkeit der Domain-Komponenten, durch unterschiedliche Inbound-Komponenten
  • Erleichterte Testbarkeit durch gesteigerte Modularisierung im Vergleich zu Package by Layer

Nachteile:

  • Enge Kopplung zwischen Inbound- und Domain-Schicht, mit dem Risiko indirekter Abhängigkeiten und Seiteneffekten bei Änderungen, insbesondere wenn die Anwendung wächst
  • Komponentenkommunikation schwer beherrschbar bei erhöhter fachlicher Komplexität
  • Schwerer erweiterbar für mittlere bis große Anwendungen mit höherer fachlicher Komplexität


Infografik Architekturmuster in Package by Component (Abb. 6).

Infografik Architekturmuster in Package by Component (Abb. 6).

Das Architekturmuster in Package by Component (Abb. 6).



Source link

Weiterlesen

Entwicklung & Code

Ein Tag im Leben eines Softwarearchitekten – Überleben im Unternehmensdschungel


Heute erzähle ich von einem typischen Arbeitstag als Softwarearchitekt, der schon vor dem Weg zur Arbeit beginnt.

Weiterlesen nach der Anzeige


Michael Stal

Michael Stal

Prof. Dr. Michael Stal arbeitet seit 1991 bei Siemens Technology. Seine Forschungsschwerpunkte umfassen Softwarearchitekturen für große komplexe Systeme (Verteilte Systeme, Cloud Computing, IIoT), Eingebettte Systeme und Künstliche Intelligenz.

Er berät Geschäftsbereiche in Softwarearchitekturfragen und ist für die Architekturausbildung der Senior-Software-Architekten bei Siemens verantwortlich.

Der Wecker schreit mit der Begeisterung eines Junior-Entwicklers, der gerade Designmuster entdeckt hat. Als Softwarearchitekt beginnt mein Tag nicht mit Kaffee, sondern mit einem kurzen Blick auf die Produktionswarnungen der letzten Nacht. Drei kritische Systeme sind ausgefallen, zwei Datenbanken laufen aus unerfindlichen Gründen so, als würden sie auf einem Server aus dem Jahr 1995 mit einem antiken Prozessor laufen, und es gibt eine dringende Slack-Nachricht von jemandem, der fragt, ob wir „einfach schnell Blockchain zu unserem Warenkorb hinzufügen können, weil der CEO gehört hat, dass das revolutionär ist“.

Ich schenke mir eine Tasse Kaffee ein, der so stark ist, dass er wahrscheinlich selbst Code kompilieren könnte, und bereite mich mental auf einen weiteren Tag vor, an dem ich Geschäftsträume in technische Realität umsetzen und mich dabei durch die tückischen Gewässer der Unternehmensbürokratie navigieren muss.

Während meiner Fahrt zur Arbeit erhalte ich den ersten von insgesamt siebzehn Anrufen, die ich heute erhalten werde. Er kommt vom Projektmanager, der entdeckt hat, dass unsere sorgfältig geplante Microservices-Architektur möglicherweise mehrere Dienste erfordert. Der Horror! Ich verbringe zwanzig Minuten damit, zu erklären, warum „einfach einen großen Dienst daraus zu machen“ den Zweck der letzten Sechs-Monats-Planung zunichtemacht. Dieses Gespräch wird sich heute noch viermal mit verschiedenen Personen wiederholen, die offenbar an derselben Besprechung teilgenommen haben, aber völlig unterschiedliche Dinge gehört haben wollen.



Source link

Weiterlesen

Beliebt