Kopfloses CMS. Warum schreibe ich meine?

Hallo allerseits!

Dieser kürzlich erschienene Artikel hat mich dazu veranlasst , diese Veröffentlichung zu schreiben (ich habe sie gestern gesehen).

Erzählen Sie die Hauptfunktionen von Headless / content-first / api-first usw. nach. Ich werde kein CMS sein, das Material ist voll und wahrscheinlich sind viele bereits mit diesem Trend vertraut. Und ich möchte Ihnen sagen, warum und wie ich mein System schreibe, warum ich nicht aus den vorhandenen auswählen konnte, was ich über andere Systeme denke, denen ich zuvor begegnet bin, und welche Perspektiven ich für all dies sehe. Die Fiktion wird umfangreich sein (für das Material in zwei Jahren), aber ich werde versuchen, interessanter und nützlicher zu schreiben. Wen kümmert es bitte unter der Katze.

Im Allgemeinen ist die Geschichte sehr lang und ich werde versuchen, sie zuerst zu erzählen. Entweder um klarer zu machen, was die wahren Gründe für die Entwicklung dieser eigenen Engine sind, oder einfach, weil es ohne diese vor Ort schwierig sein wird zu erklären, warum ich es so mache und nicht auf irgendeine Weise.

Zunächst werde ich jedoch kurz die wichtigsten Auswahlkriterien für das moderne Headless-CMS für mich persönlich aufschreiben, warum ich mir noch keine fertige Lösung aussuchen konnte. Nur damit die Leute nicht abbrechen, um viele Buchen zu lesen, und nicht verstehen, was irgendwann erzählt wird.

Kurz gesagt: Ich wollte, dass sich alles an einem Ort befindet: sowohl hinten als auch vorne (und nicht dies oder das) und die GraphQL-API, und dass die Datenbank verwaltet wird und vieles mehr, einschließlich der Schaltfläche „Make Beautiful“. Ich habe das nicht gefunden. Ich selbst habe das auch noch nicht getan, aber im Großen und Ganzen hat es sich als ziemlich viel herausgestellt, und vor allem erlaubt es mir, echte Projekte zu machen.

Daher kann mein Ansatz kaum als wissenschaftlich und gerechtfertigt bezeichnet werden. Tatsache ist, dass ich im Allgemeinen sehr oft etwas Eigenes schreibe. Ich programmiere gerne hier. Und vor zwei Jahren (und davor weitere 8 Jahre) saß ich auf dem MODX CMF (unter dem ich auch viele meiner Krücken erfunden habe). Und drei Jahre lang haben wir ein ziemlich großes Projekt gestartet, bei dem ich MODX verwenden konnte. Aber wie sich herausstellte, konnte ich nicht ... Der Hauptgrund war, dass es ein Startup ohne technische Anforderungen war, mit einer Reihe von Ideen, die sich jeden Tag (und mehrmals am Tag) änderten und ergänzten. Und jetzt habe ich jedes Mal, wenn es unter einer neuen Idee notwendig war, eine neue Entität hinzuzufügen, Felder für vorhandene zu registrieren / zu ändern, Beziehungen zwischen diesen Entitäten zu erstellen / zu löschen / zu ändern (jeweils mit einer Änderung in der Datenbankstruktur), irgendwann Es dauerte mehrere Stunden, um diese Entitäten zu ändern. Neben der Tatsache, dass diese Änderungen im Schema registriert werden mussten, war es auch erforderlich, die Datenbank (fast manuell) zu ändern, die API zu aktualisieren, den Programmcode neu zu schreiben usw. usw. Dementsprechend musste die Front unter all dem aktualisiert werden. Infolgedessen habe ich beschlossen, dass wir nach etwas Neuem und Bequemerem suchen sollten, was das alles irgendwie vereinfachen würde. Ich werde noch einmal klarstellen, dass ich zu dieser Zeit ein PHP-Backend war. Seien Sie also nicht überrascht oder lachen Sie, dass ich verschiedene Front-End-Builder, weniger Prozessoren, npm usw. entdeckte. usw. In unserem Projekt tauchte jedoch nach und nach eine Front auf "react + less", eine API auf GraphQL und ein Server auf express auf.

Aber nicht alles war so rosig, wie es vielen jetzt erscheinen würde. Ich möchte Sie daran erinnern, dass dies vor mehr als zwei Jahren war. Wenn Sie weniger als zwei Jahre im modernen JS-Web sind, empfehle ich Ihnen, diesen Artikel zu lesen: N Gründe für die Verwendung der Create React App (habr). Kurz gesagt, zu faul: Mit dem Aufkommen von React-Skripten können Sie sich nicht mehr mit der Konfiguration von Webpacks usw. beschäftigen. All dies tritt in den Hintergrund. Freundliche Leute haben das Webpack bereits so konfiguriert, dass die meisten Reaktionsprojekte fast garantiert funktionieren, und der Endentwickler konzentrierte sich direkt auf die Programmierung des Endprodukts, anstatt eine Reihe von Abhängigkeiten, Ladern usw. zu konfigurieren. Aber das ist später. Und vorher musste ich nur dieses Webpack konfigurieren, dem Update des Haufens von allem folgen, was mit ihm geflogen ist, um aufzuholen usw. usw. Dies ist aber nur ein Teil der Arbeit, nur im Wesentlichen die Front. Und du brauchst auch einen Server. Und Sie benötigen auch eine API. Außerdem benötigen Sie SSR (Server-Side Rendering), das React-Script meines Wissens nach immer noch nicht bietet. Im Allgemeinen war damals alles viel komplizierter als heute, es gab nicht viel und jeder krümmte sich so gut er konnte. Und wie habe ich dann Krücke ...

Stellen Sie sich vor:

  • Native Webpack-Konfiguration getrennt für Front und Server.
  • Eigene Implementierung von SSR, sodass Async normal mit React-Server funktioniert und Stile sofort eintreffen und normal indiziert werden und Serverstatus für nicht gefundene Seiten angegeben wurden.
  • No-Redux. Nun, ich mochte Redux nicht sofort. Ich mochte die Idee, meinen nativen Reaktionsfluss zu verwenden (obwohl ich ihn ein wenig für mich selbst umschreiben musste).
  • Manuell vorgeschriebene GraphQL-Schemata und Resolver ohne automatische Bereitstellung der Datenbank (der API-Server wurde als Mitte für eine MODX-Site verwendet).
  • Kein React-Apollo / Apollo-Client usw. Alles wird unabhängig mit Anforderungen über Abruf- und Repositorys in einem Browser geschrieben, der auf benutzerdefiniertem Fluss basiert.

Infolgedessen: Bis jetzt hat eine der ersten Versionen dieses Projekts ein Projekt mit mehr als 500 Teilnehmern und in der Saison (Winter) 1000 bis 1700 einzigartige Studenten pro Tag. Betriebszeit 2 Monate. Dies liegt daran, dass ich den Server nach einem vorbeugenden Software-Update manuell neu gestartet habe. Vor diesem Neustart betrug die Betriebszeit weitere 6+ Monate. Am interessantesten ist jedoch der Speicherverbrauch. Derzeit gibt es fast 700 Megabyte js Prozess. Ja, ja, ich lache auch hier mit dir :) Natürlich ist das viel. Und vorher habe ich ein wenig vorbeugt und diesen Indikator verbessert. Bisher gab es insgesamt 1000M + pro Prozess ... Trotzdem funktionierte es und war ziemlich erträglich. Bevor Google im November die PageSpeed ​​Insights-Algorithmen änderte , hatte die Website eine Leistungsmetrik von 97/100. Beweis

Eine Zwischenschlussfolgerung basierend auf diesem Projekt basierend auf einem System, das sich ohne dieses Projekt weiterentwickelt hat (das Projekt wurde zurückgelassen):

Vorteile

  1. Die Projekt-API wurde durch die Verwendung von GraphQL flexibler und die Anzahl der Serveranforderungen wurde erheblich reduziert.
  2. Das Projekt hat Zugriff auf eine Vielzahl von Komponenten auf npm.
  3. Das Projektmanagement ist durch die Verwendung von Abhängigkeiten, Git usw. transparenter geworden.
  4. Verstreute Skripte und Stile sind sicherlich ansprechender als eine Reihe separater Skripte auf alten Websites, wenn Sie nicht wissen, was Sie ohne Konsequenzen aus diesem Zoo entfernen können (und Sie häufig mehrere Versionen eines Fehlers auf einer Website sehen).
  5. Die Site ist interaktiver geworden, Seiten funktionieren ohne Neustart. Die Rückkehr zu zuvor angezeigten Seiten erfordert keine wiederholten Aufrufe des Servers.
  6. Die Datenbearbeitung erfolgt direkt auf der Seite nach dem Prinzip "Bearbeiten, was Sie sehen und wo Sie sehen", ohne separates Admin-Panel.

Nachteile (hauptsächlich für den Entwickler)

  1. Alles ist sehr kompliziert. Wirklich. Es ist einfach unrealistisch, einen Drittanbieter mit dem Projekt zu verbinden. Ich selbst konnte kaum herausfinden, was und wie es funktioniert und woher meine Beine wachsen. Wenn Sie sich S. 3 der Pluspunkte ansehen, in denen es um Transparenz geht, dann besteht Transparenz nur darin, dass Sie sofort sehen können, was kaputt ist (Skripte werden nicht erstellt usw.), aber durch Commits und Diffs Sie können finden, wo das süchtig. Nun, wenn Sie es geschafft haben, etwas Neues hinzuzufügen und es funktioniert, verstehen Sie zumindest klar, dass alles gut gelaufen ist. Aber insgesamt ist es immer noch die Hölle.
  2. Schwierigkeiten beim Zwischenspeichern. Später entdeckte ich Apollo-Client für mich. Und davor habe ich, wie gesagt, meine auf Flussmitteln basierenden Speicher geschrieben. Aufgrund dieser Speicher war es möglich, die erforderlichen Daten zum Rendern von verschiedenen Komponenten abzurufen, aber das Cache-Volumen auf der Clientseite war sehr groß (jeder Satz typischer Entitäten hatte ein eigenes Repository). Infolgedessen war es schwierig zu überprüfen, ob das Objekt früher angefordert wurde oder nicht (das heißt, es lohnt sich, eine Anfrage an den Server zu richten, um es zu finden), ob alle zugehörigen Daten verfügbar sind usw.
  3. Schwierigkeiten mit Schemata, Datenbankstruktur und Resolvern (API-Funktionen zum Empfangen / Ändern von Daten). Wie gesagt, ich habe Schemata manuell geschrieben und auch Resolver. Bei Resolvern habe ich versucht, das Caching und die Verarbeitung verschachtelter Anforderungen und anderer Feinheiten bereitzustellen. In diesem Moment musste ich mich sehr tief mit der Essenz und dem GraphQL-Programmcode befassen. Der Vorteil ist, dass ich im Allgemeinen ziemlich gut verstehe, wie GraphQL funktioniert, welche Vor- und Nachteile es hat und wie man es besser kocht. Der Nachteil ist, dass Sie natürlich nicht alle Annehmlichkeiten und Brötchen, die von Befehlen wie Apollo geschrieben wurden, in einem schreiben können. Als ich Apollo entdeckte, begann ich natürlich, ihre Komponenten mit großer Freude zu verwenden (aber hauptsächlich vorne werde ich Ihnen unten erklären, warum).

Im Allgemeinen gehört dieses Projekt mit veralteten Technologien zu 100% mir, sodass ich es mir leisten kann, es bis zu besseren Zeiten aufzugeben. Es gibt aber auch andere Projekte, für die ich die Plattform weiterentwickeln musste. Und mehrmals musste ich alles von Grund auf neu schreiben. Außerdem werde ich detaillierter auf die einzelnen Aufgaben eingehen, auf die ich gestoßen bin, und auf die Lösungen, die ich als Ergebnis entwickelt und angewendet habe.

Schema zuerst. Erst die Schaltung und dann alles andere

Eine Site (Webschnittstelle, Thin Client usw.) ist die gesamte Anzeige von Informationen (also Informationsmanagement, sofern zulässig und Funktionalität zulässig). Aber zuerst eine Datenbank (Tabellen, Spalten usw.). Nachdem ich auf meinem Weg verschiedene Ansätze für die Arbeit mit der Datenbank kennengelernt hatte, gefiel mir der Schema-first-Ansatz am besten. Das heißt, Sie beschreiben das Schema von Entitäten und Datentypen (manuell oder über die Schnittstelle), stellen das Schema bereit und haben sofort die in der Datenbank beschriebenen Änderungen (Tabellen / Spalten werden erstellt / gelöscht sowie Beziehungen zwischen ihnen). Abhängig von der Implementierung generieren Sie auch alle erforderlichen Resolver-Funktionen zur Verwaltung dieser Daten. Vor allem in dieser Richtung hat mir das Projekt prisma.io gefallen.

Mit Ihrer Erlaubnis werde ich, da ich selbst auf dem Hub keinen einzigen Artikel über das Prisma gesehen habe, leicht auf sie aufmerksam machen, da das Projekt wirklich sehr interessant ist und ich ohne sie jetzt keine solche Plattform hätte, die mich so glücklich gemacht hätte . Deshalb habe ich meine Plattform prisma-cms genannt, weil prisma.io dabei eine sehr große Rolle spielt.

Eigentlich ist prisma.io ein SaaS-Projekt, aber mit einer großen Einschränkung: Sie stellen fast alles, was sie tun, auf einen Github. Das heißt, Sie können ihre Server gegen eine sehr angemessene Gebühr nutzen (und Ihre eigene Datenbank und API in wenigen Minuten für sich selbst konfigurieren) oder alles vollständig zu Hause bereitstellen. In diesem Fall sollte das Prisma logisch in zwei wichtige separate Teile unterteilt werden:

  1. Prisma-Server, dh der Server, auf dem sich auch die Datenbank dreht.
  2. Prisma-Client. Es ist im Wesentlichen auch ein Server, aber in Bezug auf die Datenquelle (Prisma-Server) ist es ein Client.

Jetzt werde ich versuchen, diese verwirrende Situation zu erklären. Im Allgemeinen besteht das Wesentliche des Prismas darin, dass Sie mit einem einzelnen API-Endpunkt mit verschiedenen Datenquellen arbeiten können. Ja, hier wird jeder sagen, dass alle auf GraphQL gekommen sind und Prisma hier nicht benötigt wird. Im Allgemeinen wird jeder Recht haben, aber es gibt einen ernsten Punkt: GraphQL definiert nur die Prinzipien und die Gesamtarbeit, bietet jedoch keine sofort einsatzbereite Arbeit mit den endgültigen Datenquellen. Er sagt: "Sie können eine API erstellen, um zu beschreiben, welche Anforderungen Benutzer senden können, aber wie Sie mit diesen Anforderungen umgehen, liegt bei Ihnen." Und das Prisma verwendet natürlich auch GraphQL (übrigens und viele andere Dinge, einschließlich verschiedener Apollo-Produkte). Das Prisma plus bietet jedoch nur Arbeit mit der Datenbank. Das heißt, wenn das Schema und seine Bereitstellung beschrieben werden, werden die erforderlichen Tabellen und Spalten (sowie die Beziehungen zwischen ihnen) sofort in der angegebenen Datenbank erstellt und generieren sogar sofort alle erforderlichen CRUD-Funktionen. Das heißt, mit einem Prisma erhalten Sie nicht nur einen GraphQL-Server, sondern eine vollwertige Arbeits-API, mit der Sie sofort mit der Datenbank arbeiten können. Prisma-Server bietet also eine Datenbank und Interaktion mit ihr, und Prisma-Client ermöglicht es Ihnen, Ihre Resolver zu schreiben und Anforderungen an Prisma-Server (oder an einen anderen Ort, sogar für einige Prisma-Server) zu senden. Es stellt sich also heraus, dass Sie den Prisma-Client nur selbst bereitstellen können (und SaaS prisma.io wird als Prisma-Server verwendet), und dass Sie den Prisma-Server selbst bereitstellen können und im Allgemeinen in keiner Weise von einem Prisma abhängig sind deine.

Hier habe ich mir ein Prisma als Basis für meine Plattform ausgesucht. Aber dann musste ich es selbst drehen, um eine vollständige Plattform zu erhalten.

1. Schemata zusammenführen


Zu diesem Zeitpunkt war das Prisma nicht in der Lage, Schaltkreise zu kombinieren. Das heißt, die Aufgabe ist wie folgt:

Sie haben ein Benutzermodell in einem Modul beschrieben

type User { id: ID! @unique username: String! @unique email: String @unique } 

und in einem anderen Modul

 type User { id: ID! @unique username: String! @unique firstname: String lastname: String } 

Als Teil eines Projekts möchten Sie diese beiden Schemata automatisch kombinieren, um die Ausgabe zu erhalten

 type User { id: ID! @unique username: String! @unique email: String @unique firstname: String lastname: String } 

Aber dann konnte dieses Prisma nicht. Es stellte sich heraus, dass dies mithilfe der Bibliothek merge-graphql-schemas implementiert wurde.

Arbeiten Sie mit einem beliebigen Prisma-Server.


Im Prisma wird die Konfiguration in eine spezielle Konfigurationsdatei geschrieben. Wenn Sie die Adresse des verwendeten Prismaservers ändern möchten, müssen Sie die Datei bearbeiten. Eine Kleinigkeit, nicht angenehm. Ich wollte die URL ermöglichen, die im Befehl angegeben werden kann, zum Beispiel endpoint = http: // Endpunktadresse Garn bereitstellen (Garnstart). Das wurde mehrere Tage lang getötet ... Aber jetzt können Sie ein Prismenprojekt für eine beliebige Anzahl von Endpunkten verwenden. Übrigens funktioniert prisma-cms bisher auch mit einer lokalen Datenbank problemlos, selbst mit SaaS-Prismaservern.

Module / Plugins


Dies war im Allgemeinen nicht genug. Wie gesagt, die Hauptaufgabe des Prismas besteht darin, die Arbeit mit verschiedenen Datenbanken bereitzustellen. Und sie leisten hervorragende Arbeit. Sie unterstützen bereits die Arbeit mit MySQL, PostgreSQL, Amazon RDS und MongoDB, mehrere weitere Arten von Quellen auf dem Weg. Sie bieten jedoch keine modulare Infrastruktur. Es gibt bisher keinen Marktplatz oder ähnliches. Es gibt nur wenige typische Leerzeichen. Sie können jedoch nicht zwei oder drei aus mehreren Leerzeichen auswählen und in einem Projekt installieren. Wir müssen einen auswählen. Ich wollte, dass es möglich ist, eine andere Anzahl von Modulen im endgültigen Projekt zu installieren, und dass beim Bereitstellen der Schaltkreise und Resolver ein solches einzelnes Projekt mit der Gesamtfunktionalität fröhlich wird. Und obwohl es noch keine grafische Oberfläche gibt, gibt es bereits mehr als zwei Dutzend Arbeitsmodule und Komponenten, die im endgültigen Projekt kombiniert werden können. Hier werde ich sofort ein wenig über persönliche Definitionen entscheiden: Ein Modul wird auf der Rückseite installiert (Erweiterung der Datenbank und API) und eine Komponente wird auf der Vorderseite installiert (um verschiedene Schnittstellenelemente hinzuzufügen). Bisher gibt es keine grafische Oberfläche zum Anschließen von Modulen, aber es fällt mir nicht schwer, auf diese Weise zu schreiben (dies wird nicht oft gemacht):

  constructor(options = {}) { super(options); this.mergeModules([ LogModule, MailModule, UploadModule, SocietyModule, EthereumModule, WebrtcModule, UserModule, RouterModule, ]); } 

Nach dem Hinzufügen neuer Module reicht es aus, eine Bereitstellung einfach mit einem Befehl erneut durchzuführen, und das war's. Hier haben wir bereits neue Tabellen / Spalten und erweiterte Funktionen.

5 vorne, reagiert auf Änderungen im Backend


Das war überhaupt nicht genug. Darauf folgt ein Exkurs. Tatsache ist, dass alle API-ersten CMS, die ich gesehen habe, sagen: "Wir sind großartig, die API bereitzustellen, und Sie schrauben die Front, die Sie wollen." Dies ist, was sie "schrauben, was immer Sie wollen" bedeutet eigentlich "stören, wie Sie wollen". Genau so, wie UI-Frameworks sagen: "Sehen Sie sich an, welche coolen Schaltflächen wir sind, und machen Sie das alles, und verwechseln Sie sich selbst mit dem Backend." Das hat immer getötet. Ich wollte nur ein umfassendes CMS finden, das in Javascript geschrieben ist, GraphQL verwendet und sowohl hinten als auch vorne bereitstellt. Aber so einen habe ich nicht gefunden. Ich wollte wirklich, dass die API-Änderungen sofort im Vordergrund stehen. Und dafür wurden mehrere Teilschritte abgeschlossen:

5.1 API-Fragmente generieren


Auf der Vorderseite werden Fragmente aus der Schemadatei in den Anforderungen registriert. Wenn die API auf dem Server neu erstellt wird, wird auch eine neue JS-Datei mit API-Fragmenten generiert. Und bei Anfragen ist es so geschrieben:

 const { UserNoNestingFragment, EthAccountNoNestingFragment, NotificationTypeNoNestingFragment, BatchPayloadNoNestingFragment, } = queryFragments; const userFragment = ` fragment user on User { ...UserNoNesting EthAccounts{ ...EthAccountNoNesting } NotificationTypes{ ...NotificationTypeNoNesting } } ${UserNoNestingFragment} ${EthAccountNoNestingFragment} ${NotificationTypeNoNestingFragment} `; const usersConnection = ` query usersConnection ( $where: UserWhereInput $orderBy: UserOrderByInput $skip: Int $after: String $before: String $first: Int $last: Int ){ objectsConnection: usersConnection ( where: $where orderBy: $orderBy skip: $skip after: $after before: $before first: $first last: $last ){ aggregate{ count } edges{ node{ ...user } } } } ${userFragment} `; 

5.2 Ein Kontext für alle Komponenten


In React 16.3 wird eine neue Kontext-API eingeführt . Ich habe es so gemacht, dass Sie in untergeordneten Komponenten auf jeder Ebene auf einen einzelnen Kontext zugreifen können, ohne die gewünschten Typen aus dem Kontext aufzulisten, aber einfach statischen contextType = PrismaCmsContext angeben und alle Reize über diesen-> Kontext (einschließlich des API-Clients, des Schemas) abrufen , Anfragen usw.).

5.3 dynamische Filter


Ich wollte auch wirklich. Mit GraphQL können Sie komplexe Abfragen mit einer verschachtelten Struktur erstellen. Ich wollte, dass die Filter auch dynamisch sind, aus dem API-Schema gebildet werden und es uns ermöglichen, verschachtelte Bedingungen zu erstellen. Folgendes ist passiert:


5.4 Website Builder


Und schließlich fehlte mir ein externer Site-Editor, also ein Designer. Ich wollte, dass der Server nur ein Minimum an Aktionen ausführt, und das gesamte endgültige Design sollte an der Vorderseite erfolgen (einschließlich Einrichten des Routings, Generieren von Auswahlen usw.). Dies ist ein Thema für einen separaten Artikel, da ich unter anderem auch meinen krückenhaften Wysiwyg-Editor dafür auf pure contentEditable geschrieben habe und es viele Feinheiten gibt. Wenn ich meine Rechte wiedererlangt habe und wer interessiert sein wird, schreibe ich einen separaten Artikel.

Nun, endlich ein kurzes Demo-Video des Designers in Aktion. Immer noch ziemlich roh, aber ich mag es.


Damit bin ich fertig. Ich habe noch nicht viel geschrieben, was ich gerne schreiben würde, aber es ist so viel passiert. Ich werde gerne einen Kommentar abgeben.

PS: Alle Quellcodes, einschließlich der Quellcodes der Site selbst, sind hier .

Source: https://habr.com/ru/post/de448982/


All Articles