it-swarm.com.de

Hilft die Aufteilung einer potenziell monolithischen Anwendung in mehrere kleinere Anwendungen, Fehler zu vermeiden?

Eine andere Möglichkeit, dies zu fragen, ist: Warum sind Programme eher monolithisch?

Ich denke an so etwas wie ein Animationspaket wie Maya, das die Leute für verschiedene Workflows verwenden.

Wenn die Animations- und Modellierungsfunktionen in eine separate Anwendung aufgeteilt und separat entwickelt würden und Dateien zwischen ihnen übertragen würden, wären sie dann nicht einfacher zu warten?

48
dnv

Ja. Im Allgemeinen sind zwei kleinere, weniger komplexe Anwendungen viel einfacher zu warten als eine einzige große.

Sie erhalten jedoch eine neue Art von Fehler, wenn alle Anwendungen zusammenarbeiten, um ein Ziel zu erreichen. Damit sie zusammenarbeiten können, müssen sie Nachrichten austauschen, und diese Orchestrierung kann auf verschiedene Weise schief gehen, obwohl jede Anwendung möglicherweise perfekt funktioniert. Eine Million winziger Anwendungen zu haben, hat seine eigenen speziellen Probleme.

Eine monolithische Anwendung ist wirklich die Standardoption, die Sie erhalten, wenn Sie einer einzelnen Anwendung immer mehr Funktionen hinzufügen. Dies ist der einfachste Ansatz, wenn Sie jede Funktion für sich betrachten. Erst wenn es groß geworden ist, können Sie das Ganze betrachten und sagen: "Weißt du was? Das würde besser funktionieren, wenn wir X und Y trennen würden.".

94
Ewan

Verhindert die Aufteilung einer potenziell monolithischen Anwendung in mehrere kleinere Anwendungen Fehler?

In der Realität sind die Dinge selten so einfach.

Das Aufteilen hilft definitiv nicht, diese Fehler überhaupt zu verhindern. Es kann manchmal hilfreich sein, Fehler schneller zu finden. Eine Anwendung, die aus kleinen, isolierten Komponenten besteht, ermöglicht möglicherweise individuellere (Art von "Einheit" -) Tests für diese Komponenten, wodurch es manchmal einfacher wird, die Grundursache bestimmter Fehler zu erkennen und sie so schneller zu beheben.

Jedoch,

  • selbst eine Anwendung, die von außen monolithisch zu sein scheint, kann aus einer Vielzahl von Komponenten bestehen, die innerhalb der Einheit getestet werden können. Daher ist das Testen von Einheiten für eine monolithische App nicht unbedingt schwieriger

  • wie Ewan bereits erwähnte, bringt das Zusammenspiel mehrerer Komponenten zusätzliche Risiken und Fehler mit sich. Das Debuggen eines Anwendungssystems mit komplexer Interprozesskommunikation kann erheblich schwieriger sein als das Debuggen einer Einzelprozessanwendung

Dies hängt auch stark davon ab, wie gut sich eine größere App in Komponenten aufteilen lässt, wie breit die Schnittstellen zwischen den Komponenten sind und wie diese Schnittstellen verwendet werden.

Kurz gesagt, dies ist oft ein Kompromiss und nichts, wo eine "Ja" - oder "Nein" -Antwort im Allgemeinen richtig ist.

warum neigen Programme dazu, monolithisch zu sein?

Tun sie? Schauen Sie sich um, es gibt Unmengen von Web-Apps auf der Welt, die für mich nicht sehr monolithisch aussehen, ganz im Gegenteil. Es gibt auch viele Programme, die ein Plugin-Modell bereitstellen (AFAIK sogar die von Ihnen erwähnte Maya-Software).

wären sie nicht einfacher zu pflegen

"Einfachere Wartung" ergibt sich hier häufig aus der Tatsache, dass verschiedene Teile einer Anwendung von verschiedenen Teams einfacher entwickelt werden können, sodass die Arbeitslast besser verteilt ist, spezialisierte Teams mit klarerem Fokus und mehr.

51
Doc Brown

Ich werde der Mehrheit in diesem Punkt nicht zustimmen müssen. Das Aufteilen einer Anwendung in zwei separate Anwendungen macht den Code an sich nicht einfacher zu pflegen oder zu begründen.

Das Trennen von Code in zwei ausführbare Dateien ändert nur die physische Struktur des Codes, aber das ist nicht wichtig. Was entscheidet, wie komplex eine Anwendung ist, ist, wie die verschiedenen Teile, aus denen sie besteht, eng miteinander verbunden sind. Dies ist keine physikalische Eigenschaft, sondern eine logische Eins.

Sie können eine monolithische Anwendung verwenden, bei der verschiedene Belange und einfache Schnittstellen klar voneinander getrennt sind. Sie können eine Microservice-Architektur haben, die sich auf Implementierungsdetails anderer Microservices stützt und eng mit allen anderen verbunden ist.

Was stimmt, ist, dass der Prozess der Aufteilung einer großen Anwendung in kleinere sehr hilfreich ist, wenn versucht wird, klare Schnittstellen und Anforderungen für jedes Teil festzulegen. In DDD sprechen würde das mit Ihren begrenzten Kontexten kommen. Ob Sie dann viele kleine Anwendungen oder eine große Anwendung mit derselben logischen Struktur erstellen, ist eher eine technische Entscheidung.

38
Voo

Einfacher zu warten, wenn Sie sie aufgeteilt haben, ja. Aber das Aufteilen ist nicht immer einfach. Der Versuch, einen Teil eines Programms in eine wiederverwendbare Bibliothek aufzuteilen, zeigt, wo die ursprünglichen Entwickler nicht darüber nachgedacht haben, wo die Nähte sein sollten. Wenn ein Teil der Anwendung tief in einen anderen Teil der Anwendung hineinreicht, kann es schwierig sein, das Problem zu beheben. Das Rippen der Nähte zwingt Sie dazu, die internen APIs klarer zu definieren, und dies erleichtert letztendlich die Wartung der Codebasis. Wiederverwendbarkeit und Wartbarkeit sind Produkte gut definierter Nähte.

15
StackOverthrow

Es ist wichtig, sich daran zu erinnern, dass Korrelation keine Kausalität ist.

Das Bauen eines großen Monolithen und das anschließende Aufteilen in mehrere kleine Teile kann zu einem guten Design führen oder auch nicht. (Es kann das Design verbessern, aber es wird nicht garantiert.)

Ein gutes Design führt jedoch häufig dazu, dass ein System aus mehreren kleinen Teilen und nicht aus einem großen Monolithen besteht. (Ein Monolith kann das beste Design sein, es ist nur viel weniger wahrscheinlich.)

Warum sind kleine Teile besser? Weil sie leichter zu überlegen sind. Und wenn es leicht ist, über die Richtigkeit nachzudenken, erhalten Sie mit größerer Wahrscheinlichkeit ein korrektes Ergebnis.

Um C.A.R. Hoare:

Es gibt zwei Möglichkeiten, ein Software-Design zu erstellen: Eine Möglichkeit besteht darin, es so einfach zu gestalten, dass offensichtlich keine Mängel vorliegen, und die andere darin, es so kompliziert zu gestalten, dass es keine offensichtlich Mängel.

Wenn dies der Fall ist, warum sollte jemand eine unnötig komplizierte oder monolithische Lösung entwickeln? Hoare gibt die Antwort im nächsten Satz:

Die erste Methode ist weitaus schwieriger.

Und später in derselben Quelle (der Turing Award Lecture von 1980):

Der Preis für Zuverlässigkeit ist das Streben nach äußerster Einfachheit. Es ist ein Preis, den die Reichen am schwersten zu zahlen haben.

13
Daniel Pryden

Dies ist keine Frage mit Ja oder Nein. Die Frage ist nicht nur die Wartungsfreundlichkeit, sondern auch der effiziente Einsatz von Fähigkeiten.

Im Allgemeinen ist eine gut geschriebene monolithische Anwendung effizient. Die Kommunikation zwischen Prozessen und Geräten ist nicht billig. Das Aufbrechen eines einzelnen Prozesses verringert die Effizienz. Wenn Sie jedoch alles auf einem einzelnen Prozessor ausführen, kann dies den Prozessor überlasten und die Leistung beeinträchtigen. Dies ist das grundlegende Problem der Skalierbarkeit. Wenn das Netzwerk ins Bild kommt, wird das Problem komplizierter.

Eine gut geschriebene monolithische Anwendung, die als einzelner Prozess auf einem einzelnen Server effizient arbeiten kann, kann einfach gewartet und fehlerfrei gehalten werden, ist jedoch kein effizienter Einsatz von Codierungs- und Architekturfähigkeiten. Der erste Schritt besteht darin, den Prozess in Bibliotheken aufzuteilen, die immer noch als der gleiche Prozess ausgeführt werden, jedoch unabhängig voneinander codiert werden und den Disziplinen Kohäsion und lose Kopplung folgen. Ein guter Job auf diesem Niveau verbessert die Wartbarkeit und beeinträchtigt selten die Leistung.

Die nächste Stufe besteht darin, den Monolithen in separate Prozesse zu unterteilen. Dies ist schwieriger, weil Sie in schwieriges Gebiet eintreten. Es ist einfach, Fehler in den Rennbedingungen einzuführen. Der Kommunikationsaufwand steigt und Sie müssen auf "gesprächige Schnittstellen" achten. Die Belohnungen sind großartig, weil Sie eine Skalierbarkeitsbarriere durchbrechen, aber auch das Fehlerpotential steigt. Multiprozessanwendungen sind auf Modulebene einfacher zu warten, das Gesamtsystem ist jedoch komplizierter und schwieriger zu beheben. Korrekturen können teuflisch kompliziert sein.

Wenn die Prozesse auf separate Server oder auf eine Implementierung im Cloud-Stil verteilt werden, werden die Probleme schwieriger und die Belohnungen größer. Die Skalierbarkeit steigt. (Wenn Sie eine Cloud-Implementierung in Betracht ziehen, die keine Skalierbarkeit bietet, überlegen Sie genau.) Die Probleme, die in dieser Phase auftreten, können jedoch unglaublich schwer zu identifizieren und zu durchdenken sein.

6
MarvW

Nein. es macht es nicht einfacher zu warten. Wenn überhaupt, willkommen zu weiteren Problemen.

Warum?

  • Die Programme sind nicht orthogonal, sondern müssen sich gegenseitig unterstützen, soweit dies zumutbar ist, was ein gemeinsames Verständnis impliziert.
  • Viel Code beider Programme ist identisch. Verwalten Sie eine gemeinsame gemeinsam genutzte Bibliothek oder zwei separate Kopien?
  • Sie haben jetzt zwei Entwicklungsteams. Wie kommunizieren sie?
  • Sie haben jetzt zwei Produkte, die benötigen:

    • ein allgemeiner UI-Stil, Interaktionsmechanismen usw. Sie haben jetzt also Designprobleme. (Wie kommunizieren die Entwicklerteams wieder?)
    • abwärtskompatibilität (kann Modellierer v1 in Animator v3 importiert werden?)
    • die Cloud-/Netzwerkintegration (falls vorhanden) muss jetzt für doppelt so viele Produkte aktualisiert werden.
  • Sie haben jetzt drei Verbrauchermärkte: Modellierer, Animatoren und Modellierer-Animatoren

    • Sie werden widersprüchliche Prioritäten haben
    • Sie werden widersprüchliche Unterstützungsbedürfnisse haben
    • Sie haben widersprüchliche Verwendungsstile
  • Müssen die Modeller-Animatoren zwei separate Anwendungen öffnen, um an derselben Datei arbeiten zu können? Gibt es eine dritte Anwendung mit beiden Funktionen? Lädt eine Anwendung die Funktionen der anderen?
  • usw...

Abgesehen davon, dass kleinere Codebasen auf Anwendungsebene einfacher zu warten sind, erhalten Sie einfach kein kostenloses Mittagessen. Dies ist das gleiche Problem im Herzen von Micro-Service/Any-Modular-Architecture. Es ist kein Allheilmittel. Wartungsschwierigkeiten auf Anwendungsebene werden gegen Wartungsschwierigkeiten auf Orchestrierungsebene eingetauscht. Diese Probleme sind immer noch Probleme, sie befinden sich einfach nicht mehr in der Codebasis, sie müssen entweder vermieden oder gelöst werden.

Wenn das Lösen des Problems auf Orchestrierungsebene einfacher ist als das Lösen auf jeder Anwendungsebene, ist es sinnvoll, es in zwei Codebasen aufzuteilen und die Orchestrierungsprobleme zu lösen.

Andernfalls nein, tun Sie es einfach nicht. Sie sollten die interne Modularität der Anwendung selbst verbessern. Verschieben Sie Codeabschnitte in zusammenhängende und einfacher zu wartende Bibliotheken, für die die Anwendung als Plugin fungiert. Schließlich ist ein Monolith nur die Orchestrierungsschicht einer Bibliothekslandschaft.

4
Kain0_0

Es gab viele gute Antworten, aber da es fast eine tote Trennung gibt, werde ich auch meinen Hut in den Ring werfen.

In meiner Erfahrung als Softwareentwickler habe ich festgestellt, dass dies kein einfaches Problem ist. Es hängt wirklich von Größe, Maßstab und Zweck der Anwendung ab. Ältere Anwendungen sind aufgrund der Trägheit, die erforderlich ist, um sie zu ändern, im Allgemeinen monolithisch, da dies lange Zeit üblich war (Maya würde sich in diese Kategorie qualifizieren). Ich gehe davon aus, dass Sie allgemein über neuere Anwendungen sprechen.

Bei Anwendungen, die klein genug sind und mehr oder weniger einzelne Probleme betreffen, übersteigt der für die Wartung vieler separater Teile erforderliche Overhead im Allgemeinen die Nützlichkeit der Trennung. Wenn es von einer Person gewartet werden kann, kann es wahrscheinlich monolithisch gemacht werden, ohne zu viele Probleme zu verursachen. Die Ausnahme von dieser Regel ist, wenn Sie viele verschiedene Teile (ein Frontend, ein Backend, möglicherweise einige Datenebenen dazwischen) haben, die bequem (logisch) getrennt sind.

In sehr großen Anwendungen macht es meiner Erfahrung nach sogar Sinn, diese aufzuteilen. Sie haben den Vorteil, dass Sie eine Teilmenge der möglichen Fehlerklasse im Austausch gegen andere (manchmal einfacher zu lösende) Fehler reduzieren können. Im Allgemeinen können auch Teams von Personen isoliert arbeiten, was die Produktivität verbessert. Viele Anwendungen sind heutzutage jedoch ziemlich fein aufgeteilt, manchmal zu ihrem eigenen Nachteil. Ich war auch in Teams, in denen die Anwendung unnötig auf so viele Microservices aufgeteilt wurde, dass sie viel Aufwand verursachte, wenn die Dinge nicht mehr miteinander sprachen. Darüber hinaus wird es mit jeder Aufteilung viel schwieriger, das gesamte Wissen darüber zu besitzen, wie jedes Teil mit den anderen Teilen spricht. Es gibt ein Gleichgewicht, und wie Sie an den Antworten hier erkennen können, ist die Vorgehensweise nicht sehr klar, und es gibt wirklich keinen Standard.

3
CL40

Bei UI-Apps ist es unwahrscheinlich, dass die Gesamtzahl der Fehler verringert wird, aber das Gleichgewicht des Fehlermix wird in Richtung Kommunikationsprobleme verschoben.

Apropos UI-Anwendungen/Sites für Benutzer - Benutzer sind äußerst geduldig und benötigen eine geringe Antwortzeit. Dies führt zu Kommunikationsverzögerungen bei Fehlern. Infolgedessen wird eine potenzielle Verringerung von Fehlern aufgrund der verringerten Komplexität einer einzelnen Komponente mit sehr harten Fehlern und zeitlichen Anforderungen für die prozess-/maschinenübergreifende Kommunikation gehandelt.

Wenn die Einheiten der Daten, mit denen sich das Programm befasst, groß sind (dh Bilder), sind prozessübergreifende Verzögerungen länger und schwerer zu beseitigen - so etwas wie "Transformation auf 10-MB-Bild anwenden" gewinnt sofort + 20 MB Festplatte/Netzwerk IO zusätzlich zu 2 Konvertierungen vom In-Memory-Format in das Serializabe-Format und zurück. Sie können wirklich nicht viel tun, um die dafür erforderliche Zeit vor dem Benutzer zu verbergen.

Darüber hinaus unterliegt jede Kommunikation und insbesondere die Festplatte IO) AntiVirus-/Firewall-Überprüfungen - dies führt zwangsläufig zu einer weiteren Schicht schwer zu reproduzierender Fehler und noch mehr Verzögerungen.

Das Aufteilen eines monolithischen "Programms" scheint dort, wo Kommunikationsverzögerungen nicht kritisch oder bereits unvermeidbar sind

  • parallelisierbare Massenverarbeitung von Informationen, bei der Sie kleine zusätzliche Verzögerungen eintauschen können, um einzelne Schritte erheblich zu verbessern (manchmal müssen Sie keine benutzerdefinierten Komponenten mehr verwenden, indem Sie sie einmal von der Stange verwenden). Mit einem geringen Platzbedarf für einzelne Schritte können Sie möglicherweise mehrere billigere Maschinen anstelle einer einzigen teuren Maschine verwenden.
  • das Aufteilen monolithischer Dienste in weniger gekoppelte Mikrodienste - das parallele Aufrufen mehrerer Dienste anstelle eines Dienstes führt höchstwahrscheinlich nicht zu zusätzlichen Verzögerungen (kann sogar die Gesamtzeit verkürzen, wenn jeder einzelne schneller ist und keine Abhängigkeiten bestehen).
  • verschieben von Vorgängen, von denen Benutzer erwarten, dass sie lange dauern - Rendern komplizierter 3D-Szenen/-Filme, Berechnen komplexer Metriken für Daten, ...
  • alle Arten von "automatischer Vervollständigung", "Rechtschreibprüfung" und anderen optionalen Hilfsmitteln können und werden häufig als extern bezeichnet. Das offensichtlichste Beispiel sind die automatischen URL-Vorschläge des Browsers, bei denen Ihre Eingaben ständig an externe Dienste (Suchmaschinen) gesendet werden .

Beachten Sie, dass dies sowohl für Desktop-Apps als auch für Websites gilt. Benutzerbezogene Teile des Programms sind in der Regel "monolithisch". Der gesamte Benutzerinteraktionscode, der an einzelne Daten gebunden ist, wird normalerweise in einem einzigen Prozess ausgeführt (eine Aufteilung ist nicht ungewöhnlich Prozesse auf Datenbasis wie eine HTML-Seite oder ein Bild, aber es ist orthogonal zu dieser Frage. Selbst für die meisten einfachen Sites mit Benutzereingaben wird die Validierungslogik auf der Clientseite ausgeführt, selbst wenn die Serverseite modularer wäre und die Komplexität/Codeduplizierung verringert.

1
Alexei Levenkov

Hilft [es] dabei, Fehler zu verhindern?

Verhindern? Nein, nicht wirklich.

  • Es hilft Fehler zu erkennen .
    Nämlich all die Fehler, von denen Sie nicht einmal wussten, dass Sie sie hatten, die Sie erst entdeckt haben, als Sie versuchten, das ganze Durcheinander in kleinere Teile aufzuteilen. In gewisser Weise verhinderte dies, dass diese Fehler in der Produktion auftauchten - aber die Fehler waren bereits vorhanden.
  • Es hilft , die Auswirkungen von Fehlern zu verringern.
    Fehler in monolithischen Anwendungen können das gesamte System zum Erliegen bringen und den Benutzer davon abhalten, überhaupt mit Ihrer Anwendung zu interagieren. Wenn Sie diese Anwendung in Komponenten aufteilen, betreffen die meisten Fehler - vom Design her - nur eine der Komponenten.
  • Es wird ein Szenario für neue Fehler erstellt..
    Wenn Sie die Benutzererfahrung beibehalten möchten, müssen Sie ne Logik einschließen, damit alle diese Komponenten kommunizieren können (über REST Dienste, über OS-Systemaufrufe, was haben Sie), damit sie nahtlos vom POV des Benutzers aus interagieren können.
    Als einfaches Beispiel: Mit Ihrer monolithischen App können Benutzer ein Modell erstellen und animieren, ohne die App zu verlassen. Sie teilen die App in zwei Komponenten auf: Modellierung und Animation. Jetzt müssen Ihre Benutzer das Modell der Modellierungs-App in eine Datei exportieren, dann die Datei suchen und sie dann mit der Animations-App öffnen ... Seien wir ehrlich, einige Benutzer werden das nicht mögen, also müssen Sie eine neue Logik für die hinzufügen Modellierungs-App zum Exportieren der Datei und Starten Sie automatisch die Animations-App und make es öffnet die Datei. Und diese neue Logik, so einfach sie auch sein mag, kann eine Reihe von Fehlern in Bezug auf Datenserialisierung, Dateizugriff und Berechtigungen, Benutzer, die den Installationspfad der Apps ändern, usw. aufweisen.
  • Es ist die perfekte Ausrede, um dringend benötigtes Refactoring anzuwenden .
    Wenn Sie sich entscheiden, eine monolithische App in kleinere Komponenten aufzuteilen, tun Sie dies (hoffentlich) mit viel mehr Wissen und Erfahrung über das System als bei seiner ersten Entwicklung, und dank dessen können Sie eine Reihe von Anwendungen anwenden Refaktoren, um den Code sauberer, einfacher, effizienter, belastbarer und sicherer zu machen. Und dieses Refactoring kann in gewisser Weise dazu beitragen, Fehler zu vermeiden. Natürlich können Sie auch das gleiche Refactoring auf die monolithische App anwenden, um dieselben Fehler zu vermeiden, aber Sie tun dies nicht, weil es so monolithisch ist, dass Sie Angst haben, etwas in der Benutzeroberfläche zu berühren und die Geschäftslogik zu brechen ¯\_ (ツ) _/¯

Ich würde also nicht sagen, dass Sie Fehler verhindern, indem Sie eine monolithische App in kleinere Komponenten aufteilen, aber Sie machen es in der Tat einfacher, einen Punkt zu erreichen, an dem Fehler leichter verhindert werden können.

0
walen