Warum verwende ich keine MVC-Frameworks mehr?

Ursprünglicher Autor: Jean-Jacques Dubray
  • Übersetzung


Lieber Habravchane.

Da die Diskussion um den Artikel sehr aktiv ist, beschloss Jean-Jacques Dubreux (er liest Kommentare), Chats in Gitter zu organisieren.

Sie können mit ihm persönlich in folgenden Chats chatten:
https://gitter.im/jdubray/sam
https://gitter.im/jdubray/sam-examples
https://gitter.im/jdubray/sam-architecture

Auch Autor des Artikels Hier veröffentlichte er Codebeispiele: https://bitbucket.org/snippets/jdubray/

In Bezug auf den Code hinterließ er den folgenden Kommentar:
Ich programmiere nicht für meinen Lebensunterhalt, deshalb bin ich nicht der beste Entwickler, aber die Leute können ein Gefühl dafür bekommen, wie das Muster funktioniert und dass Sie genau das Gleiche tun können wie React + Redux + Relay mit einfachen JavaScript-Funktionen, ohne es zu benötigen Für all diese aufgeblähten Bibliotheken (und natürlich brauchen Sie kein GraphQL).

Veröffentlichung mit freundlicher Genehmigung des Autors und Zustimmung des infoq.com- Portals . Ich hoffe, dass meine Sprachkenntnisse das Vertrauen des Autors rechtfertigen.

Das Schlimmste an meiner bisherigen Arbeit ist das Entwerfen einer API für Front-End-Entwickler. Der Dialog mit ihnen verläuft unweigerlich wie folgt:
Auf diesem Bildschirm werden die Daten x, y und z benötigt. Könnten Sie eine API erstellen, die Daten im Format {x:, y:, z:} zurückgibt

?

Ich diskutiere nicht einmal mehr mit ihnen. Die Projekte enden mit einer Reihe verschiedener APIs, die an Bildschirme gebunden sind, die sich sehr häufig ändern. Dies erfordert „konstruktionsbedingt“ Änderungen an der API. Sie haben keine Zeit, ein Auge zuzuwenden, aber Sie haben bereits eine Reihe von APIs und für jede müssen Sie viele Formate und Plattformen unterstützen. Sam Newman begann sogar, diesen Ansatz als BFF-Muster zu formalisieren , was darauf hindeutet, dass es normal ist, eine separate API für jeden Gerätetyp, jede Plattform und natürlich für jede Version Ihrer Anwendung zu entwickeln. Daniel Jacobson sagte, dass Netflix gezwungen sei, eine neue Funktion für seine „Experience API“ zu verwenden: Ephemerality. Oh ...

Vor ein paar Monaten begann ich meine Reise auf der Suche nach einer Antwort auf die Frage, warum alles passiert ist und was ich dagegen tun soll. Dieser Weg hat mich an den stärksten Dogmen der Entwicklung einer Anwendungsarchitektur - MVC - zweifeln lassen. Und als ich auf die wahre Kraft der reaktiven und funktionalen Programmierung stieß, konzentrierte ich mich auf Einfachheit und Vermeidung von Blähungen, in denen unsere Branche so erfolgreich war. Ich denke, Sie könnten an meinen Schlussfolgerungen interessiert sein.

Das Muster, das wir bei der Entwicklung jedes Bildschirms verwenden, ist MVC - Model-View-Controller. MVC wurde damals erfundenAls das Web noch nicht existierte und die Anwendungsarchitektur bestenfalls ein Fat Client war, der über ein primitives Netzwerk direkt auf eine einzelne Datenbank zugegriffen hat. Und doch wird MVC noch Jahrzehnte später zum Erstellen von Omni-Channel-Anwendungen verwendet .

In der kommenden Version von Angular2 ist es möglicherweise an der Zeit, die Verwendung des MVC-Musters und damit den Wert, den MVC-Frameworks für die Anwendungsarchitektur haben, erneut zu überdenken.

Ich habe MVC 1990 kennengelernt, als NeXT Interface Builder startete (es ist großartig zu erkennen, dass diese Software noch heute relevant ist). Zu dieser Zeit schienen Interface Builder und MVC ein großer Fortschritt zu sein. In den späten 90ern war das MVC-Musterangepasst, um über HTTP zu arbeiten (erinnern Sie sich an Struts ?) und jetzt ist MVC der Eckpfeiler in der Architektur jeder Anwendung für alle Gelegenheiten.

Sogar React.js, die scheinbar weit vom MVC-Dogma entfernt sind, nutzten diesen Euphemismus, als sie ihr Framework präsentierten: „ React is just View in MVC “.

Letztes Jahr, als ich anfing, React zu verwenden, hatte ich das Gefühl, dass es etwas völlig anderes war - Sie ändern einen Teil der Daten irgendwo und ohne die explizite Interaktion von Ansicht und Modell ändert sich gleichzeitig die gesamte Benutzeroberfläche (nicht nur die Werte in den Feldern) und Tabellen). Aus diesem Grund war ich schnell desillusioniert vom React-Entwicklungsmodell und war offensichtlich nicht allein. Ich werde die Meinung von Andre Medeiros teilen :
Reagieren Sie enttäuscht mich aus mehreren Gründen. Dies ist hauptsächlich auf eine schlecht gestaltete API zurückzuführen, die den Benutzer [..] dazu zwingt, mehrere Anliegen in einer Komponente zu mischen.

Als Server-API-Designer bin ich zu dem Schluss gekommen, dass es keine gute Möglichkeit gibt, API-Aufrufe zum in React implementierten Front-End hinzuzufügen, da React nur auf View ausgerichtet ist und das Entwicklungsmodell kein Controller-Konzept enthält.

Facebook hat sich bisher geweigert, diese Lücke auf Framework-Ebene zu schließen. Das React-Team hat das ebenfalls enttäuschende Flux-Muster entwickelt , und jetzt wirbt Dan Abramov für ein weiteres Muster, Redux , das sich in die richtige Richtung bewegt, aber - ich werde dies weiter demonstrieren - keine geeignete Möglichkeit bietet, mit der Front-End-API zu arbeiten .

Wenn Sie GWT, Android SDK und Angular entwickeln, haben Sie vielleicht eine klare Vorstellung (auf Englisch klingt es wie ein Wortspiel) - was ist die beste Architektur für das Front-End? Wenn Sie jedoch einige Überlegungen zur Architektur von Angular2 lesen , werden Sie nicht unbedingt das warme Gefühl spüren , dass die Leute zumindest bei Google verstehen, was sie tun:
Winkel 1 basiert nicht auf der Idee von Komponenten. Stattdessen fügen wir nach eigenem Ermessen Controller zu den verschiedenen [Elementen] der Seite hinzu. Angehängte oder durchflossene Bereiche, je nachdem, wie sich unsere Richtlinien selbst kapseln (isolierte Bereiche, jemand?)

Sieht Angular2-Komponenten viel einfacher aus? Nicht wirklich. Das Angular2-Kernpaket selbst enthält 180 Definitionen, und das gesamte Framework umfasst fast 500 Definitionen, die auf HTML5 und CSS3 aufbauen. Wer hat Zeit, diese Art von Framework zum Erstellen von Webanwendungen zu erlernen und zu verbessern? Was passiert, wenn Angular3 herauskommt?

Nachdem ich mit React gearbeitet hatte und herausgefunden hatte, worum es in Angular2 geht, wurde ich entmutigt - diese Frameworks zwingen mich methodisch, das BFF- Screen-Scraping- Muster zu verwenden , bei dem jede Server-API auf das kleinste Detail für den auf dem Bildschirm benötigten Datensatz angepasst wird.

Das war mein Moment "Scheiß drauf." Ich werde nur Anwendungen erstellen, ohne Reagieren, ohne Angular, ohne MVC-Frameworks, und sehen, ob ich einen besseren Weg finden kann, die Ansicht und die zugrunde liegende API zu verbinden.

Was mir an React sehr gut gefallen hat, war die Interaktion zwischen Model und View. Die Tatsache, dass React nicht auf Vorlagen basiert und View alleine nicht auf Daten zugreifen kann, wurde als sinnvolle Lernrichtung angesehen (Sie können nur Daten an View übergeben).

Nachdem Sie viel mit React gearbeitet haben, wissen Sie, dass es nur dazu dient, View in eine Reihe von reinen Funktionen und JSX-Markups zu zerlegen :


Dies ist nichts anderes als:

V = f(M)

Zum Beispiel wird die Website eines der Projekte, an denen ich gerade arbeite, Gliiph , mit folgenden Funktionen erstellt:


Abb . 1 Funktion, die für die Erzeugung von HTML für die Schiebereglerkomponente verantwortlich ist.

Diese Funktion wird vom Modell aus ausgefüllt:


Abb. 2. Ein Modell, das mit Schiebereglern arbeitet.

Wenn Sie feststellen, dass gewöhnliche JavaScript-Funktionen problemlos funktionieren, lautet Ihre nächste Frage: "Warum sollte ich React überhaupt verwenden?"

Virtueller dom ? Wenn es Ihnen so vorkommt, als ob Sie es brauchen (und ich bin mir nicht sicher, ob dies für viele so ist), dann gibt es Optionen , und ich denke, andere werden entwickelt.

GraphQL? Nein eigentlich. Lassen Sie sich nicht von dem Argument täuschen, dass es Ihnen passt, wenn Facebook es überall verwendet. GraphQL ist nichts anderes als eine deklarative Weise eine erstellen Ansicht Nehmen Modell . Wenn Sie gezwungen sind, ein Modell gemäß View zu erstellen, ist dies ein Problem und keine Lösung. Generell kann der Befehl React (in dem Sinne, dass er reaktiv ist) davon ausgehen, dass es normal ist, Daten mit den vom Client angegebenen Abfragen abzufragen :
GraphQL wird vollständig von den Anforderungen von View- und Front-End-Entwicklern gesteuert, [...] die sie geschrieben haben. Andererseits gibt eine GraphQL-Abfrage genau das zurück, was vom Client angefordert wird, und nicht mehr

Was dem GraphQL-Befehl zu fehlen scheint - eine kleine Änderung, die durch die JSX-Syntax verborgen wird - ist, dass die Funktionen das Modell von der Ansicht isolieren. Im Gegensatz zu Vorlagen oder „Anforderungen, die von Front-End-Entwicklern geschrieben wurden“, ist für Funktionen keine Anpassung von Model an View erforderlich.

Wenn eine Ansicht mithilfe einer Funktion erstellt wird (im Gegensatz zu einer Vorlage oder Abfrage), können Sie das Modell nach Bedarf transformieren und die Ansicht am besten implementieren, ohne dem Modell künstliche Einschränkungen hinzuzufügen.

Wenn View beispielsweise den Wert von v und einen grafischen Indikator anzeigt, der anzeigt, ob dieser Wert ausgezeichnet, gut oder schlecht ist, müssen Sie den Indikatorwert nicht zum Modell hinzufügen - die Funktion sollte einfach den Indikatorwert aus dem Wert von v berechnen, den das Modell bereitgestellt hat.

Derzeit ist es keine gute Idee, solche Berechnungen direkt in die Ansicht einzubetten. Es ist jedoch überhaupt nicht schwierig, das Ansichtsmodell zu einer reinen Funktion zu machen. Daher ist es nicht erforderlich, GraphQL zu verwenden, wenn Sie ein klar formuliertes Ansichtsmodell benötigen:

V = f( vm(M) )

Als ein Veteran von MDE ( Model-driven Engineering ) kann ich Ihnen versichern, dass das Schreiben von Code unendlich besser ist als Metadaten, sei es eine Vorlage oder eine komplexe Abfragesprache wie GraphQL.

Ein solcher funktionaler Ansatz hat mehrere wichtige Vorteile. Erstens ermöglicht es Ihnen, genau wie React, Ihre Ansicht in Komponenten zu zerlegen. Über die von ihnen erstellte natürliche Oberfläche können Sie Themen für Ihre Webanwendung oder Website implementieren oder View mithilfe anderer Technologien (z. B. native) rendern. Die Implementierung mithilfe von Funktionen kann auch die Methoden zur Implementierung des von uns verwendeten Responsive-Designs verbessern.

Ich werde zum Beispiel nicht überrascht sein, wenn Entwickler in den nächsten Monaten damit beginnen, Designs für HTML5 zu erstellen, die als Komponenten auf der Basis von JavaScript-Funktionen implementiert sind. Im Moment mache ich alle meine Websites so. Ich nehme die Vorlage und verpacke sie sofort mit JavaScript-Funktionen. Ich benutze Wordpress nicht mehr - ich kann das Beste aus den HTML5- und CSS3-Technologien mit den gleichen Kosten (oder weniger) herausholen.

Dieser Ansatz erfordert auch eine neue Art der Interaktion zwischen Designern und Entwicklern. Jeder kann solche JavaScript-Funktionen schreiben, insbesondere ein Template-Designer. Es gibt keine "Bindungs" -Syntax zu lernen, kein JSX, keine Angular-Templates, nur einfache JavaScript-Funktionen .

Interessanterweise können diese Funktionen aus Sicht des reaktiven Datenflusses dort arbeiten, wo es sinnvoller ist - auf dem Server oder auf dem Client.

Am wichtigsten ist jedoch, dass View bei diesem Ansatz einen Mindestvertrag für die Interaktion mit Model deklarieren kann und das Model entscheiden muss, wie die erforderlichen View-Daten am besten bereitgestellt werden. Probleme wie Caching, verzögertes Laden, Orchestrierung und Konsistenz unterliegen der vollständigen Kontrolle des Modells. Anders als bei Vorlagen oder GraphQL ist es nie erforderlich, eine direkte Abfrage zu erstellen, die in Bezug auf die Ansicht erstellt wird.

Nachdem wir nun die Möglichkeit haben, Modell und Ansicht voneinander zu trennen, lautet die nächste Frage, wie daraus ein vollständiges Anwendungsmodell erstellt werden kann. Wie soll „Controller“ aussehen? Um diese Frage zu beantworten, kehren wir zu MVC zurück.

Apple weiß etwas über MVC, da sie dieses Muster in den frühen 80er Jahren von Xerox SPARC "gestohlen" haben und es seitdem religiös befolgt haben :


Abb. 3. Das MVC-Muster.

Die Schlüsselfrage hier ist, und Andre Medeiros erklärte sehr eloquent, dass das MVC-Muster „interaktiv“ ist.(Im Gegensatz zu reaktiven). In einer herkömmlichen MVC ruft die Aktion (Controller) die Aktualisierungsmethode von Model auf und entscheidet bei Erfolg (oder Fehler), wie die Ansicht aktualisiert werden soll. Wie er (Andre Medeiros - ca. Übersetzer) feststellt, ist der Controller nicht verpflichtet, auf diese Weise zu handeln. Es gibt einen anderen, nicht weniger vernünftigen, reaktiven Weg, bei dem Action die Werte unabhängig vom Ergebnis einfach an das Modell weitergibt, anstatt zu entscheiden, wie das Modell aktualisiert werden soll.

Die Schlüsselfrage lautet dann: „Wie können Sie Aktionen in reaktiven Ablauf integrieren?“ Wenn Sie verstehen möchten, was Aktionen sind, sollten Sie sich TLA + ansehen . TLA steht für "Temporal Logic of Actions" (Zeitliche Logik von Handlungen), eine erfundene FormulierungDr. Lamport , der dafür den Turing Award erhalten hat. Laut TLA + Actions sind dies reine Funktionen :

 data’ = A (data)

Ich mag die Grundidee von TLA + sehr, weil sie die Tatsache bestätigt, dass Funktionen nur Transformationen eines bestimmten Datensatzes sind.

In diesem Sinne könnte reaktiver MVC ungefähr so ​​aussehen:

V = f( M.present( A(data) ) ) 

Dieser Ausdruck legt fest, dass die Aktion beim Auslösen den Datensatz aus dem Satz von Eingabeparametern (z. B. Benutzereingaben) berechnet, die an das Modell übergeben werden, das wiederum entscheidet, wann und wie es sich selbst aktualisiert. Nach Abschluss der Aktualisierung wird die Ansicht aus dem neuen Modellstatus gerendert. Die Reaktivschleife schließt sich. Die Art und Weise, in der Model Daten speichert und abruft, hängt nicht mit dem reaktiven Datenfluss zusammen und sollte auf keinen Fall von Front-End-Entwicklern geschrieben werden. Keine Ausreden.

Auch hier sind Aktionen reine Funktionen, ohne Status und ohne Nebenwirkungen (aus Respekt vor dem Modell schließen wir beispielsweise die Protokollierung aus).

Das reaktive MVC-Muster ist interessant, da mit Ausnahme von Model (natürlich) alles andere reine Funktionen sind. Um ehrlich zu sein, setzt Redux genau dieses Muster um, jedoch mit unnötigen Zeremonien in Bezug auf React und einer etwas redundanten Verbindung zwischen Model und Actions in Reducer. Die Schnittstelle zwischen Aktionen und Modell ist nur Messaging.

Auf der Grundlage des Vorstehenden ist das reaktive MVC-Muster, wie es ist, nicht vollständig. Es lässt sich nicht auf echte Anwendungen skalieren, wie Dan gerne sagt . Schauen wir uns ein einfaches Beispiel an, das erklärt, warum.

Angenommen, wir müssen eine Anwendung implementieren, die einen Raketenwerfer steuert: Sobald wir den Countdown starten, verringert das System den Zähler, und wenn er Null erreicht, wird ein Raketenstart eingeleitet, wenn alle Modelleigenschaften die richtigen Werte haben.



Diese Anwendung hat eine einfache Zustandsmaschine:


Abb. 4. Anwendungen zum Starten von Raketen mit Maschinenzustand.

Sowohl Dekrementierung als auch Start sind "automatische" Aktionen. Das heißt, jedes Mal, wenn wir in den Referenzzustand eintreten (oder ihn wieder einführen), werden die Übergangsbedingungen ausgewertet, und wenn der Zählerzustand größer als Null ist, wird die Dekrementierungsaktion ausgeführt . Und wenn der Wert Null erreicht, wird die Startaktion aufgerufen. Die Abbruchaktion kann jederzeit aufgerufen werden, wodurch das System in einen abgebrochenen Zustand versetzt wird.

In MVC wird diese Logik im Controller implementiert und wahrscheinlich vom Timer von View aus aufgerufen.

Dieser Absatz ist sehr wichtig, lesen Sie ihn daher sorgfältig durch. Wir haben festgestellt, dass TLA + Action keine Nebenwirkungen hat und der resultierende Status berechnet wird, sobald das Modell das Ergebnis des Aktionsaufrufs verarbeitet und sich selbst aktualisiert. Dies ist eine signifikante Abweichung von der traditionellen Semantik von Zustandsautomaten, bei der Action den resultierenden Zustand definiert. Mit anderen Worten ist der resultierende Zustand unabhängig vom Modell. In TLA + beziehen sich Aktionen, die zulässig und daher für den Aufruf in der Statusdarstellung (d. H. In Ansicht) verfügbar sind, nicht direkt auf Aktionen, die Statusänderungen verursachen. Mit anderen Worten, Zustandsautomaten müssen keine Tupel sein, die zwei Zustände (S1, A, S2) verbinden, die sie normalerweise sind. Sie sind vielmehr Tupel von Formen (Sk, Ak1, Ak2, ...

Die Semantik von TLA + bietet eine hervorragende Möglichkeit, ein System zu konzipieren, in dem Sie ein "Status" -Objekt darstellen, das von "Aktion" und "Ansicht" (bei denen es sich lediglich um eine Statuszuordnung handelt ) getrennt ist.

Modell in unserer Anwendung ist wie folgt:

model = {
counter:,
started:,
aborted:,
launched:
}

Die vier (Steuer-) Zustände des Systems sind in Model den folgenden Werten zugeordnet:

ready = {counter: 10, started: false, aborted: false, launched: false }
counting = {counter: [0..10], started: true, aborted: false, launched: false }
launched = {counter: 0, started: true, aborted: false, launched: true}
aborted = {counter: [0..10], started: true, aborted: true, launched: false}


Das Modell wird durch die Eigenschaften des Systems und ihre möglichen Werte beschrieben, während der Status die für einen bestimmten Wertesatz verfügbaren Aktionen bestimmt. Eine solche Geschäftslogik muss irgendwo implementiert werden. Wir können nicht erwarten, dass dem Benutzer vertraut werden kann, welche Aktionen möglich sind und welche nicht. Dies ist eine Bedingung, die nicht umgangen werden kann. Dennoch ist es schwierig, eine solche Geschäftslogik zu schreiben, zu debuggen und zu warten, insbesondere wenn Ihnen keine Semantik zur Beschreibung, beispielsweise in MVC, zur Verfügung steht.

Schreiben wir einen Code für unser Raketenstartbeispiel. Aus Sicht von TLA + wird das Prädikat der nächsten Aktion logisch aus dem angezeigten Status bestimmt. Sobald der aktuelle Status angezeigt wird, besteht der nächste Schritt darin, das Prädikat für die nächste Aktion auszuführen, das die nächste Aktion auswertet und gegebenenfalls ausführt. Sie liefert ihrerseits Modelldaten, die das Rendern einer neuen Zustandsdarstellung einleiten, und so weiter.


Abb. 5. Implementierung der Raketenstartanwendung.

Beachten Sie, dass bei einer Client-Server-Architektur ein Protokoll wie WebSocket verwendet werden muss (oder eine Abfrage, wenn WebSocket nicht verfügbar ist), um den Status nach Abschluss der automatischen Aktion korrekt anzuzeigen.

Ich habe eine sehr kleine Open Source Bibliothek in geschriebenJava und JavaScript , die den Status des Objekts aus Sicht von TLA + mit der richtigen Semantik aufbauen, und Beispiele, die WebSocket, Polling und Queuing zum Implementieren der Client-Server-Interaktion verwenden. Wie Sie dem Beispiel mit der Anwendung zum Raketenstart entnehmen können, müssen Sie diese Bibliothek nicht verwenden. Das Implementieren eines Status ist ziemlich einfach zu schreiben, wenn Sie verstehen, was Sie schreiben müssen.

Ich glaube, wir haben jetzt alle Elemente, um das neue Template als Alternative zum MVC-SAM-Muster (State-Action-Model), einem reaktiven, funktionalen Muster, das in React.js und TLA + verwurzelt ist, formell zu beschreiben.

Das SAM-Muster kann wie folgt beschrieben werden:

 V = S( vm( M.present( A(data) ) ), nap(M))



Dies legt fest, dass View V-Systeme nach Anwendung von Aktion A als reine Funktion von Model berechnet werden können. In SAM sind A (Actions), vm (ViewModel), nap (Prädikat der nächsten Aktion) und S (Zustandsdarstellung) sind und sollen reine Funktionen sein. Im Fall von SAM schränkt das, was wir normalerweise als "Status" (Werte von Systemeigenschaften) bezeichnen, das Modell vollständig ein, und die Logik, die diese Werte ändert, ist außerhalb des Modells nicht verfügbar.

Als Hinweis ist das Prädikat für die nächste Aktion (nap ()) ein Rückruf, der aufgerufen wird, sobald die Zustandsdarstellung erstellt wurde und dem Benutzer wiedergegeben wird.


Abb. 6. Musterzustands-Aktionsmodell (SAM).

Das Muster als solches hängt nicht von Protokollen ab (und kann einfach über HTTP implementiert werden) und von Client / Server-Technologien.

SAM bedeutet nicht, dass Sie immer die Semantik einer Zustandsmaschine verwenden sollten, um den Inhalt einer Ansicht abzurufen. Wenn ein Aktionsaufruf nur über Ansicht erfolgt, ist das Prädikat für die nächste Aktion eine Nullfunktion. Es kann jedoch eine gute Angewohnheit sein, die Steuerungszustände der zugrunde liegenden Zustandsmaschine klar zu unterscheiden, da die Ansicht je nach dem einen oder anderen (Steuerungs-) Zustand unterschiedlich aussehen kann.

Wenn Ihre Statusmaschine dagegen automatische Aktionen enthält, sind weder Ihre Aktionen noch Ihr Modell ohne das Prädikat der nächsten Aktion bereinigt: Entweder werden einige Aktionen in den Status versetzt, oder Model ruft Aktionen auf, die in ihre Aufgaben einbezogen. Übrigens, und das ist unerwartet, das Statusobjekt enthält keinen „Status“. Es ist auch eine reine Funktion, die die Ansicht rendert und das Prädikat für die nächste Aktion berechnet, wobei beide auf den Werten der Modelleigenschaften basieren.

Der Hauptvorteil dieses neuen Musters besteht darin, dass CRUD-Operationen klar von Aktionen getrennt werden. Das Modell ist für seine Persistenz verantwortlich, die mithilfe von CRUD-Operationen implementiert wird, die in View nicht verfügbar sind. Insbesondere ist die Ansicht niemals in der Lage, Daten abzurufen. Die Ansicht kann nur die Darstellung des aktuellen Status des Systems anfordern und einen reaktiven Datenfluss durch Aufrufen von Aktionen initiieren.

Aktionen sind nur ein Kanal zum Ändern des Modells. Sie selbst haben keine Nebenwirkung (für Model). Bei Bedarf können Aktionen APIs von Drittanbietern aufrufen (wiederum ohne Nebeneffekte für Model). Beispielsweise kann die Aktion des Änderns der Adresse den Dienst aufrufen, um ihn zu überprüfen und die von diesem Dienst zurückgegebene Adresse an das Modell zu übergeben.

Hier ist die Aktion „Adressänderung“, so dass der API die Adresse zu überprüfen, umgesetzt werden:


Abb. 7. Umsetzung der Aktion „Adressen ändern“

Die Bestandteile eines Musters, Aktionen und Modelle, können frei zusammengestellt werden:

Zusammensetzung nach Funktionen:

data’ = A(B(data))

Einstufige Komposition (derselbe Datensatz wird an zwei Modelle übertragen):

M1.present(data’)
M2.present(data’)

"Parent-Child" -Zusammensetzung (das übergeordnete Modell steuert den an das untergeordnete Modell übergebenen Datensatz):

M1.present(data’,M2)
function present(data, child) {
        	// perform updates
        	…
        	// synch models
        	child.present(c(data))
}

Komposition über Publish / Subscribe:

M1.on(“topic”, present )
M2.on(“topic”, present )

Oder

M1.on(“data”, present )
M2.on(“data”, present )


Für Architekten, die in Bezug auf Aufzeichnungssysteme und Partizipationssysteme denken (lesen Sie, was hier steht - Anmerkung des Übersetzers), hilft dieses Muster bei der Bestimmung der Schnittstelle zwischen zwei Ebenen (Abb. 8) mit Model, die für alle Interaktionen mit dem Aufzeichnungssystem verantwortlich ist.


Abb. 8. SAM-Zusammensetzungsmodell.

Zusammensetzbare Muster selbst, und Sie können in einem Browser - Unterstützung Verhalten im Stil des Assistenten-s (zB SAM - Instanz laufen implementieren die ToDo - Anwendung ), die in Wechselwirkung mit einer Kopie des SAM auf dem Server:


Abb. 9. Zusammensetzung der SAM-Instanzen.

Beachten Sie, dass die interne SAM-Instanz als Teil der von der externen SAM-Instanz generierten Statusdarstellung dargestellt wird.
Der Sitzungswiederherstellung sollte eine Aktion-a vorausgehen (Abb. 10). SAM ermöglicht eine interessante Komposition, wenn View eine Aktion eines Drittanbieters aufrufen kann, die ein Token und einen Rückruf bereitstellt, die auf die Aktion des Systems verweisen, die den Anruf autorisiert und überprüft, bevor Daten an das Modell gesendet werden.


Abb. 10. Sitzungsverwaltung in SAM.

Aus Sicht von CQRS unterscheidet das Muster nicht zwischen Anforderungen und Befehlen, obwohl die zugrunde liegende Implementierung diese Unterscheidung erfordert. Beim Suchen oder Abfragen von Daten wird der Parametersatz einfach an Model übergeben. Wir können die Konvention (z. B. ein Präfix in Form von Unterstrichen) verwenden, um Anforderungen von Befehlen zu trennen, oder wir können zwei verschiedene Arten der Darstellung in Model verwenden.

{ _name : ‘/^[a]$/i’ } // Names that start with A or a
{ _customerId: ‘123’ } // customer with id = 123


Das Modell kann die erforderlichen Vorgänge ausführen, um die Anforderung abzugleichen, den Inhalt zu aktualisieren und das Rendern der Ansicht zu initiieren. Mit ähnlichen Konventionen können Modellelemente erstellt, aktualisiert oder gelöscht werden. Es gibt eine Reihe von Möglichkeiten, die implementiert werden können, um das Ergebnis der Arbeit von Aktionen in Model zu übermitteln (Datensätze, Ereignisse, Aktionen). Jeder Ansatz hat seine eigenen Vor- und Nachteile. Letztendlich kann der Ansatz anhand von Präferenzen festgelegt werden. Ich bevorzuge den Ansatz mit Datensätzen.

Aus Sicht von Ausnahmen wird erwartet, dass Model genau wie in React Informationen zu der entsprechenden Ausnahme als Eigenschaftswerte enthält (unabhängig davon, ob es sich um eine Ausnahme von Action handelt oder von einer CRUD-Operation zurückgegeben wird). Diese Werte werden beim Rendern der Statusdarstellung verwendet, um eine Ausnahme anzuzeigen.

In Bezug auf die Zwischenspeicherung können Sie SAM auf der Ebene der Zustandsdarstellung ausführen. Es wird erwartet, dass das Zwischenspeichern der Ergebnisse dieser Statusrepräsentationsfunktionen zu einer höheren Trefferquote führt, da das Zwischenspeichern jetzt auf Komponenten- / Statusebene anstelle der Aktion- / Antwortebene eingeleitet wird.

Die reaktive und funktionale Struktur des Musters erleichtert die Reproduktion und das Testen von Einheiten.

Das SAM-Muster ändert das Paradigma der Front-End-Architektur vollständig, da die Geschäftslogik auf der Grundlage von TLA + klar unterteilt werden kann in:
  • Aktionen als reine Funktionen
  • CRUD-Operationen in Model
  • Zustände, die automatische Aktionen steuern

Aus meiner Sicht verlagert das Muster als API-Entwickler die Verantwortung für das Entwerfen der API zurück auf den Server, mit dem geringstmöglichen Vertrag zwischen View und Model.

Aktionen können als reine Funktionen zwischen Modellen wiederverwendet werden, wenn Model das Rückgabeergebnis von Action akzeptiert. Wir können davon ausgehen, dass die Bibliotheken von Aktionen, Themen (Zustandsdarstellung) und möglicherweise die Bibliotheken von Modellen aufblühen werden, da sie jetzt unabhängig voneinander zusammengesetzt sind.

Mit SAM werden Microservices natürlich hinter Model platziert. Frameworks wie Hivepod.io können auf dieser Ebene im Großen und Ganzen so wie sie sind verbunden werden.

Vor allem erfordert dieses Muster wie "Reagieren" keine Datenbindung oder Vorlagen.

Ich gehe davon aus, dass SAM im Laufe der Zeit dazu beitragen wird, dass virtual-dom universell in Browsern implementiert wird und neue Statusdarstellungen direkt über die entsprechende API verarbeitet werden.

Ich finde diese Studie transformativ: Die Zeiten des jahrzehntelangen Ansatzes der Objektorientierung scheinen fast vorbei zu sein. Ich kann nicht mehr anders als reaktiv oder funktional denken. Die Dinge, die ich mit SAM implementiert habe, und die Geschwindigkeit, mit der ich sie implementieren kann, sind beispiellos. Und noch eine Sache. Ich kann mich jetzt auf das Entwerfen von APIs und Diensten konzentrieren, die nicht dem Muster der Bildschirmverschrottung folgen.

Ich möchte mich bei allen bedanken, die freundlicherweise zugestimmt haben, diesen Artikel zu lesen: Prof. Jean Bezivin, Prof. Joëlle Coutaz, Braulio Diez, Adron Hall, Edwin Khodabackchian, Guillaume Laforge, Pedro Molina und Arnon Rotem-Gal-Oz.

Über den Autor:
Jean-Jacques Dubreux ist der Gründer von xgen.io und gliiph . Seit 15 Jahren erstellt er serviceorientierte Architekturen und API-Plattformen. Er war ehemaliges Mitglied des Forschungsteams am HRL und promovierte an der Universität der Provence (Luminy Campus) (heute Universität Aix-Marseille - ca. Übersetzer), dem Sprachhaus von Prolog. Erfinder der BOLT- Methodik .

Jetzt auch beliebt: