Eine andere Art, dies zu fragen, ist: Warum sind Programme in der Regel monolithisch?
Ich denke an so etwas wie ein Animationspaket wie Maya, das die Leute für verschiedene Workflows verwenden.
Wären die Animations- und Modellierungsfunktionen nicht einfacher zu warten, wenn sie in eine separate Anwendung aufgeteilt und separat entwickelt würden, wobei Dateien zwischen ihnen ausgetauscht würden?
If the animation and modelling capabilities were split into their own separate application and developed separately, with files being passed between them, would they not be easier to maintain?
Mischen Sie nicht einfachere Erweiterungsmöglichkeiten mit einfacheren Wartungsmöglichkeiten, da dies nicht frei von Komplikationen oder zweifelhaften Designs ist. Maya kann die Hölle auf Erden sein, während seine Plugins es nicht sind. Oder umgekehrt.Antworten:
Ja. Im Allgemeinen sind zwei kleinere, weniger komplexe Anwendungen viel einfacher zu warten als eine einzige große.
Es tritt jedoch eine neue Art von Fehler auf, 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, auch wenn jede Anwendung möglicherweise einwandfrei funktioniert. Eine Million winziger Anwendungen zu haben, hat seine eigenen speziellen Probleme.
Eine monolithische Anwendung ist die Standardoption, mit der Sie am Ende immer mehr Funktionen zu einer einzelnen Anwendung hinzufügen. Dies ist der einfachste Ansatz, wenn Sie jede Funktion für sich betrachten. Erst wenn es groß geworden ist, können Sie sich das Ganze ansehen und sagen: "Weißt du was? Das würde besser funktionieren, wenn wir X und Y trennen."
quelle
In der Realität sind die Dinge selten so einfach.
Das Aufteilen hilft definitiv nicht, diese Fehler an erster Stelle zu verhindern. Es kann manchmal helfen, Fehler schneller zu finden. Eine Anwendung, die aus kleinen, isolierten Komponenten besteht, kann mehr individuelle (Art von "Einheit" -) Tests für diese Komponenten ermöglichen, wodurch es manchmal einfacher ist, die Ursache bestimmter Fehler zu lokalisieren und sie so schneller zu beheben.
Jedoch,
Sogar eine Anwendung, die von außen monolithisch zu sein scheint, kann aus einer Menge von Komponenten bestehen, die innerhalb der Einheit getestet werden können. Daher ist das Testen von Einheiten für eine monolithische Anwendung 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.
Tun sie? Schauen Sie sich um, es gibt Millionen 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).
Eine "einfachere Wartung" ergibt sich hier häufig aus der Tatsache, dass verschiedene Teile einer Anwendung von verschiedenen Teams einfacher entwickelt werden können, wodurch die Arbeitslast besser verteilt wird und spezialisierte Teams klarer fokussiert sind.
quelle
Ich muss mit der Mehrheit in dieser Frage nicht einverstanden sein. Das Aufteilen einer Anwendung in zwei separate macht den Code an sich nicht einfacher zu pflegen oder zu begründen.
Das Trennen von Code in zwei ausführbare Dateien ändert lediglich die physische Struktur des Codes, aber das ist nicht wichtig. Entscheidend für die Komplexität einer Anwendung ist, wie eng die einzelnen Komponenten miteinander verbunden sind. Dies ist keine physikalische Eigenschaft, sondern eine logische .
Sie können eine monolithische Anwendung haben, bei der verschiedene Anliegen und einfache Schnittstellen klar voneinander getrennt sind. Sie können über eine Microservice-Architektur verfügen, die sich auf Implementierungsdetails anderer Microservices stützt und eng mit allen anderen gekoppelt ist.
Die Aufteilung einer großen Anwendung in kleinere Anwendungen ist sehr hilfreich, wenn klare Schnittstellen und Anforderungen für jedes Teil festgelegt werden sollen. In DDD speak käme das auf Ihre begrenzten Kontexte. Ob Sie dann viele kleine Anwendungen oder eine große mit derselben logischen Struktur erstellen, ist eher eine technische Entscheidung.
quelle
Einfacher zu pflegen, wenn Sie fertig sind, ja. Aber sie aufzuteilen 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, dies zu beheben. Durch das Rippen der Nähte müssen Sie die internen APIs klarer definieren. Dies erleichtert letztendlich die Wartung der Codebasis. Wiederverwendbarkeit und Wartbarkeit sind beide Produkte mit genau definierten Nähten.
quelle
Es ist wichtig, sich daran zu erinnern, dass Korrelation keine Kausalität ist.
Der Bau eines großen Monolithen und dessen Aufteilung 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 Kleinteile besser? Weil es einfacher ist, über sie nachzudenken. Und wenn es einfach ist, über die Richtigkeit zu urteilen, erhalten Sie mit größerer Wahrscheinlichkeit ein korrektes Ergebnis.
Um CAR Hoare zu zitieren:
Wenn dies der Fall ist, warum sollte jemand eine unnötig komplizierte oder monolithische Lösung entwickeln? Hoare liefert die Antwort im nächsten Satz:
Und später in derselben Quelle (der 1980er Turing Award Lecture):
quelle
Dies ist keine Frage mit einer Ja- oder Nein-Antwort. Die Frage ist nicht nur die Wartungsfreundlichkeit, sondern auch die effiziente Nutzung 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 noch komplizierter.
Eine gut geschriebene monolithische Anwendung, die effizient als ein einzelner Prozess auf einem einzelnen Server ausgeführt werden kann, kann einfach zu warten und fehlerfrei sein, aber dennoch keine effiziente Verwendung von Codierungs- und Architekturfähigkeiten darstellen. Der erste Schritt besteht darin, den Prozess in Bibliotheken zu unterteilen, die immer noch als derselbe Prozess ausgeführt werden, jedoch unabhängig voneinander codiert werden, wobei den Disziplinen Kohäsion und lose Kopplung gefolgt wird. Ein guter Job auf diesem Niveau verbessert die Wartbarkeit und beeinträchtigt selten die Leistung.
Die nächste Stufe besteht darin, den Monolithen in getrennte Prozesse zu unterteilen. Dies ist schwieriger, weil Sie in schwieriges Gebiet eintreten. Es ist einfach, Fehler bei 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. Mehrprozessanwendungen sind auf Modulebene einfacher zu warten, das Gesamtsystem ist jedoch komplizierter und schwieriger zu beheben. Fixes 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. Skalierbarkeit steigt. (Wenn Sie über eine Cloud-Implementierung nachdenken, die keine Skalierbarkeit bietet, sollten Sie überlegen.) Die Probleme, die in dieser Phase auftreten, können jedoch unglaublich schwer zu identifizieren und zu überdenken sein.
quelle
Nein . Die Wartung wird dadurch nicht einfacher. Wenn irgendetwas zu mehr Problemen willkommen ist.
Warum?
Sie haben jetzt zwei Produkte, die benötigt werden:
Sie haben jetzt drei Verbrauchermärkte: Modellierer, Animatoren und Modellierer-Animatoren
Obwohl kleinere Codebasen auf Anwendungsebene einfacher zu warten sind, erhalten Sie 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. Bei diesen Problemen handelt es sich immer noch um Probleme. Sie befinden sich nicht mehr in der Codebasis. Sie müssen entweder vermieden oder behoben 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 behandeln.
Andernfalls, nein, tun Sie es einfach nicht. Sie werden besser bedient, wenn Sie die interne Modularität der Anwendung selbst verbessern. Verschieben Sie Codeabschnitte in zusammenhängende und leichter zu verwaltende Bibliotheken, für die die Anwendung als Plugin fungiert. Ein Monolith ist schließlich nur die Orchestrierungsschicht einer Bibliothekslandschaft.
quelle
Es gab viele gute Antworten, aber da es fast einen toten Spalt gibt, werfe ich auch meinen Hut in den Ring.
Aufgrund meiner Erfahrung als Softwareentwickler habe ich festgestellt, dass dies kein einfaches Problem ist. Es hängt wirklich von der Größe , dem Maßstab und dem Zweck der Anwendung ab. Ältere Anwendungen sind aufgrund der zum Ändern erforderlichen Trägheit im Allgemeinen monolithisch, da dies lange Zeit üblich war (Maya würde sich in dieser Kategorie qualifizieren). Ich gehe davon aus, dass Sie im Allgemeinen über neuere Anwendungen sprechen.
Bei Anwendungen, bei denen es sich mehr oder weniger um Einzelanwendungen handelt, übersteigt der Aufwand, der erforderlich ist, um viele separate Teile zu warten, 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) voneinander getrennt sind.
In sehr großen Einzelanwendungen macht es meiner Erfahrung nach Sinn, dies aufzuteilen. Sie haben den Vorteil, dass Sie eine Teilmenge der Fehlerklasse reduzieren, die im Austausch gegen andere (manchmal leichter zu lösende) Fehler möglich ist. Im Allgemeinen können auch Teams von Personen isoliert arbeiten, um die Produktivität zu verbessern. Viele Anwendungen sind heutzutage jedoch ziemlich fein aufgeteilt, manchmal zu ihrem eigenen Nachteil. Ich war auch in Teams, in denen die Anwendung unnötigerweise auf so viele Microservices aufgeteilt war, dass sie viel Overhead verursachte, wenn die Dinge aufhörten, miteinander zu reden. Darüber hinaus wird es mit jeder Aufteilung schwieriger, alle Kenntnisse darüber zu haben, wie jedes Teil mit den anderen Teilen spricht. Es gibt ein Gleichgewicht, und wie Sie anhand der Antworten hier sehen können, ist die Vorgehensweise nicht sehr klar.
quelle
Für UI-Apps ist es unwahrscheinlich, dass die Gesamtzahl der Fehler sinkt, aber das Gleichgewicht der Fehlermischung verschiebt sich zu Problemen, die durch die Kommunikation verursacht werden.
Apropos benutzerorientierte UI-Anwendungen / Sites - Benutzer sind äußerst unpatientiert und benötigen nur eine geringe Antwortzeit. Dies führt zu Kommunikationsverzögerungen bei Fehlern. Als Ergebnis wird man die potenzielle Abnahme von Fehlern aufgrund der verringerten Komplexität einer einzelnen Komponente mit sehr schweren Fehlern und Zeitanforderungen für die prozess- / maschinenübergreifende Kommunikation handeln.
Wenn die Dateneinheiten, 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" bringt sofort zusätzlich + 20 MB an Festplatten- / Netzwerk-E / A ein zu 2 Konvertierung von In-Memory-Format in Serializabe-Format und zurück. Sie können wirklich nicht viel tun, um die dafür benötigte Zeit vor dem Benutzer zu verbergen.
Darüber hinaus wird jede Kommunikation und insbesondere Festplatten-E / A-Vorgänge einer Viren- / Firewall-Überprüfung unterzogen. Dies führt zwangsläufig zu einer weiteren Schicht schwer reproduzierbarer Fehler und zu noch mehr Verzögerungen.
Das Aufteilen eines monolithischen "Programms" scheint dort, wo Kommunikationsverzögerungen nicht kritisch oder bereits unvermeidbar sind
Beachten Sie, dass dies sowohl für Desktop-Apps als auch für Websites gilt. Der dem Benutzer zugewandte Teil des Programms ist in der Regel "monolithisch". Der gesamte Benutzerinteraktionscode, der an einzelne Daten gebunden ist, wird normalerweise in einem einzigen Prozess ausgeführt (das Aufteilen 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 Basiswebsites mit Benutzereingaben wird auf der Clientseite eine Überprüfungslogik ausgeführt, auch wenn die Serverseite modularer wäre und die Komplexität / Codeduplizierung verringert.
quelle
Verhindern? Nun, nein, nicht wirklich.
Und zwar all die Fehler, von denen Sie nicht einmal wussten, dass Sie sie hatten, und die Sie erst entdeckten, als Sie versuchten, das ganze Durcheinander in kleinere Teile aufzuteilen. In gewisser Weise verhinderte dies, dass diese Bugs in der Produktion auftauchten - aber die Bugs waren bereits da.
Fehler in monolithischen Anwendungen können das gesamte System zum Absturz bringen und den Benutzer davon abhalten, überhaupt mit Ihrer Anwendung zu interagieren. Wenn Sie diese Anwendung in Komponenten aufteilen, wirken sich die meisten Fehler - von Natur aus - nur auf eine der Komponenten aus.
Wenn Sie die Benutzererfahrung beibehalten möchten, müssen Sie eine neue Logik für die Kommunikation all dieser Komponenten einbinden (über REST-Dienste, über Betriebssystemaufrufe, wie Sie möchten ), damit sie nahtlos vom POV des Benutzers aus interagieren können.
Ein einfaches Beispiel: Mit Ihrer monolithischen App können Benutzer ein Modell erstellen und es 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, die Datei suchen und sie dann mit der Animations-App öffnen Modellierungs-App zum Exportieren der Datei undStarte automatisch die Animations-App und öffne 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.
Wenn Sie sich dazu entschließen, eine monolithische App in kleinere Komponenten aufzuteilen, verfügen Sie (hoffentlich) über viel mehr Wissen und Erfahrung über das System als beim ersten Entwurf, und dank dessen können Sie eine Reihe von Refaktoren anwenden, um den Code zu erstellen Sauberer, einfacher, effizienter, belastbarer, sicherer. Und dieses Refactoring kann in gewisser Weise dazu beitragen, Fehler zu vermeiden. Natürlich können Sie das gleiche Refactoring auch auf die monolithische App anwenden, um die gleichen Fehler zu vermeiden, aber 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.
quelle