Projektarchitektur
Ein Überblick über Anwendungsschichten, Quellverzeichnisse und generierten Code.
Projektarchitektur
Goat kombiniert eine generierte Grundlage mit Erweiterungen der Anwendung. In goatcms.com sind Definitionen und Vorlagen unter herd/ die Quelle; goatapp/ wird generiert, während anwendungsspezifische Implementierungen unter child/ liegen. goatapp/ niemals von Hand ändern oder seinen Code in child kopieren.
Struktur und Abhängigkeiten
herd/_model.goat: Entitäten, Rollen, Module und Constraints.herd/_modules.goat: festgelegte Commit-Versionen von goatcore und goatmodule; lokale Checkouts unter.goat/modules/.herd/gen/templates/: Projektvorlagen, einschließlich des gemeinsamen Service-Containers.child/app/: Layout, Bootstrap, Routen, Befehle, Produktverhalten und SQL-Migrationen.child/static/raw/: Projektdateien, eingebettet durch die generierte Dateichild/static/main.go.child/web/: separate Angular-Anwendung unter/child/; die Grundlage liefert/app/und elements.
Service-Komposition
Der Einstiegspunkt erstellt services.GenInstances und übergibt Layout, Mailer und Shops an appcore.RunApp(instances, bootstrap.Body). Ein Container führt zuerst Init für alle Services aus und danach AfterInit. Service-Slots werden generiert; Implementierungen bleiben unter child. Produktverhalten gehört zur jeweiligen Shop-Instanz, ohne globalen Zustand.
Bootstrap registriert Routen, Befehle und die Aktion serve. Nur diese Aktion startet Worker, einmal pro Instanz. Beim Beenden werden Worker abgebrochen und abgewartet, bevor Datenbankressourcen geschlossen werden. CLI-Befehle und Migrationen starten keine Hintergrundarbeit.
Seiten und Migrationen
Seiten liefern Inhalte als layoutcontract.Document und Metadaten als PageMeta; der Renderer liefert die gemeinsame HTML-Hülle und SEO. Dokumente betten BaseDocument für optionale Slots ein. Das child-Layout verwendet das öffentliche GOAT-Theme und ergänzt Projektstyles, ohne dessen Implementierung zu kopieren.
Grundlage und child sind separate Go-Module, verbunden durch replace in go.mod. Beide Module separat testen. Migrationsnamen und relative Pfade bleiben unverändert: Modellmigrationen unter 0000_goatmigrations/, Anwendungsmigrationen unter 0001_childigrations/. Jede child-Datei läuft in einer Transaktion.
Anwendungsmodell
Die Datei:
herd/_model.goatbeschreibt die Domänenstruktur der Anwendung.
Sie kann unter anderem enthalten:
- Entitäten,
- Felder,
- Datentypen,
- Beziehungen,
- Rollen,
- gemeinsame Eigenschaften,
- Informationen, die vom Backend und Frontend verwendet werden.
Das Modell sollte vor allem die Intention und Struktur der Anwendung beschreiben, anstatt Implementierungsdetails jeder einzelnen Datei.
Auf dieser Grundlage kann Goat konsistente Elemente mehrerer Systemschichten generieren, beispielsweise:
- Modelle,
- DAOs und Repositories,
- DTOs,
- API-Endpunkte,
- CRUD-Befehle,
- SQL-Migrationen,
- Formulare,
- Tabellen,
- Administrationsansichten,
- Elemente der Benutzeroberfläche.
Der größte Vorteil liegt nicht allein im Erstellen von Dateien, sondern in der Möglichkeit, die Konsistenz zwischen mehreren Repräsentationen derselben Entität aufrechtzuerhalten.
Eine Modelländerung kann gleichzeitig auf Backend, Datenbank und Frontend übertragen werden.
Codegenerator
Der Generator kombiniert mehrere Quellen:
Anwendungsmodell
+
Vorlagen
+
Quellcode
↓
AnwendungscodeDas Ergebnis ist eine vollständige Anwendung im Hauptprojektverzeichnis.
Der generierte Code wird jedoch nicht als schreibgeschützter Code behandelt.
Du kannst ihn:
- bearbeiten,
- erweitern,
- refaktorieren,
- an spezifische Anforderungen anpassen,
- um eigene Logik ergänzen,
- zusammen mit den übrigen Projektänderungen committen.
Goat soll die Arbeit dort automatisieren, wo die Generierung einen echten Nutzen bringt. Es erzwingt jedoch nicht, dass jede spätere Änderung ausschließlich über das Modell oder die Vorlagen vorgenommen wird.
Bearbeitung des generierten Codes
Anwendungsspezifische Änderungen gehören nach child/, Generatorvorlagen nach herd/gen/templates/. Änderungen der Grundlage werden generiert; goatapp/ nicht von Hand bearbeiten.
Regenerierung der Anwendung
Nach einer Änderung des Modells kannst du Folgendes ausführen:
goat reDer Generator analysiert die aktuelle Definition der Anwendung und führt die daraus resultierenden Änderungen durch.
Ein typischer Workflow kann so aussehen:
Änderung des Modells
↓
goat re
↓
Aktualisierung des Codes
↓
manuelle Nachbearbeitung
↓
git diff
↓
TestsNach der Regenerierung solltest du immer Folgendes prüfen:
git diffDadurch siehst du genau, welche Teile des Projekts vom Generator geändert wurden, und kannst entscheiden, welche Änderungen in den Commit aufgenommen werden sollen.
Git bleibt ein wichtiger Bestandteil der Arbeit mit Goat: Der Generator schlägt konkrete Änderungen am Code vor, während der Entwickler deren endgültigen Umfang kontrolliert.
Modell, Vorlage oder direkte Bearbeitung?
Goat schreibt keine einzige Methode zum Vornehmen von Änderungen vor.
Je nach Art der Aufgabe kannst du auf unterschiedlichen Ebenen arbeiten.
Änderung des Modells
Wenn die Änderung die Struktur der Domäne betrifft, beginne mit:
herd/_model.goatBeispiele:
- Hinzufügen einer Entität,
- Hinzufügen eines Feldes,
- Ändern eines Datentyps,
- Ändern einer Beziehung,
- Hinzufügen einer Rolle,
- Ändern einer gemeinsamen Eigenschaft.
In diesem Fall ist das Modell der beste Ort, da es dem Generator ermöglicht, die Konsistenz zwischen den verschiedenen Schichten der Anwendung zu bewahren.
Änderung der Vorlage
Wenn du die Art ändern möchtest, wie eine ganze Klasse ähnlicher Elemente generiert wird, kann der passende Ort sein:
herd/gen/templates/Beispiele:
- Ändern der Struktur aller Endpoints,
- Hinzufügen einer gemeinsamen Annotation,
- Ändern der Art, wie Formulare generiert werden,
- Ändern der Standard-CRUD-Komponente,
- Einführen einer neuen Konvention im generierten Code.
Das Modell bestimmt dann, was erstellt werden soll, und die Vorlage definiert, wie die Standardimplementierung aussehen soll.
Direkte Bearbeitung des Codes
Anwendungsspezifische Änderungen gehören nach child/, Generatorvorlagen nach herd/gen/templates/. Änderungen der Grundlage werden generiert; goatapp/ nicht von Hand bearbeiten.
Anwendungsschichten
child/cmd/goatcms/main.go startet den Kern der Anwendung.
Der Kern ist unter anderem verantwortlich für:
- Laden der Konfiguration,
- Initialisierung von Abhängigkeiten,
- Registrierung von Diensten,
- Vorbereitung der Anwendungsinfrastruktur,
- Verarbeitung des Befehlszeilenterminals.
Dadurch nutzen verschiedene Operationen dieselbe Anwendungsumgebung.
Beispielhafte Befehle:
serve
db:migrate
crud:doc:persistmüssen nicht als separate Werkzeuge implementiert werden.
Sie können Folgendes gemeinsam nutzen:
- Konfiguration,
- Datenbankzugriff,
- Dienste,
- Protokollierung,
- Autorisierungsmechanismen,
- die übrige Projektinfrastruktur.
Dies erleichtert sowohl die Entwicklung der Anwendung als auch die Erstellung administrativer Werkzeuge und Automatisierungsprozesse.
Domänenmodell und generierter Code
Entitäten, die in:
herd/_model.goatdefiniert sind, können zur Generierung von Elementen verschiedener Anwendungsschichten verwendet werden.
Beispielhafter Ablauf:
Entität
↓
Backend-Modell
↓
DAO / Repository
↓
DTO
↓
API
↓
Formular
↓
ListenansichtDadurch lässt sich die Notwendigkeit verringern, dieselbe Datenstruktur mehrfach zu beschreiben.
Anstatt mehrere Anwendungsschichten manuell zu synchronisieren, können grundlegende Informationen aus einer gemeinsamen Definition stammen.
Dies bedeutet jedoch nicht, dass alle Schichten identisch bleiben oder vollständig generiert werden müssen. Jedes der generierten Elemente kann weiter an die Anforderungen der Anwendung angepasst werden.
.goat-Skripte
Das Verzeichnis herd/ dient auch als Automatisierungsschicht des Projekts.
.goat-Skripte können vollständige Entwicklungsprozesse beschreiben, unter anderem:
- Starten der Umgebung,
- Generieren der Anwendung,
- Migrationen,
- Initialisierung der Datenbank,
- Laden von Fixture-Daten,
- Erstellen des Frontends,
- Synchronisierung von Übersetzungen,
- Ausführen von Tests.
Beispielsweise:
bash child/dev.shkann den vollständigen Workflow starten, der erforderlich ist, um mit der Arbeit am Projekt zu beginnen.
Dadurch bleibt die Art und Weise, wie die Anwendung gebaut, getestet und gestartet wird, Teil des Repositorys.
Es ist nicht erforderlich, für jedes Betriebssystem oder jede Entwicklungsumgebung separate manuelle Anweisungen zu pflegen.
Isolierung der Skriptausführung
Eine der wichtigen Annahmen von Goat besteht darin, die Auswirkungen ausgeführter Skripte auf das Hostsystem zu begrenzen.
Die beim Erstellen und Testen verwendeten Werkzeuge können in einer isolierten, auf Docker basierenden Umgebung ausgeführt werden.
Dies betrifft unter anderem:
- Go,
- Node.js,
- npm,
- PostgreSQL,
- Playwright,
- Build-Werkzeuge,
- zusätzliche Abhängigkeiten, die vom Projekt benötigt werden.
Dadurch lässt sich die Abhängigkeit von der lokalen Konfiguration des Entwicklerrechners reduzieren und die Umgebung zwischen dem Team und CI vereinheitlichen.
Die Isolierung begrenzt auch den Umfang der Ressourcen, die den ausgeführten Skripten zur Verfügung stehen.
Im typischen Fall können sie mit dem aktuellen Projekt arbeiten, ohne freien Zugriff auf die übrigen Daten des Hosts zu haben.
Dies ist besonders wichtig bei der Ausführung von Code aus externen Abhängigkeiten, beispielsweise von Installationsskripten, die durch npm install gestartet werden.
Architektur und Arbeit mit AI
Die Architektur von Goat wurde so konzipiert, dass sie auch gut mit AI-Werkzeugen zusammenarbeitet.
In einem traditionellen Projekt kann selbst eine kleine Anforderungsänderung die Analyse vieler Dateien bedeuten:
Modell
DTO
DAO
API
Migration
Formular
Ansicht
FrontendIn einem auf Goat basierenden Projekt kann ein Teil solcher Änderungen deutlich höher beschrieben werden:
Änderung der Anforderung
↓
Änderung des Modells
↓
Goat re
↓
Aktualisierung vieler SchichtenDie KI kann dann mit einem kleineren Kontext arbeiten und sich auf die Absicht der Änderung konzentrieren, anstatt die gesamte generierte Implementierung zu analysieren.
Dies ermöglicht:
- die Menge des an das Modell übergebenen Codes zu begrenzen,
- die Analysekosten zu senken,
- die Zeit zum Verständnis der Änderung zu verkürzen,
- die Anzahl der geänderten Dateien zu begrenzen,
- das Risiko von Inkonsistenzen zwischen den Schichten zu verringern.
Gleichzeitig erzwingt Goat keine Arbeit ausschließlich auf hoher Ebene.
Wenn die Änderung einen konkreten Codeabschnitt betrifft, kann die KI oder der Entwickler ihn direkt ändern.
Dadurch ergeben sich zwei einander ergänzende Arbeitsebenen:
hohe Ebene — Änderung des Modells, der Konfiguration oder des Templates und Regenerierung,
niedrige Ebene — direkte Bearbeitung des Anwendungscodes.
Die Wahl der Ebene hängt davon ab, welcher Ansatz einfacher, lesbarer und kostengünstiger in der Wartung ist.
Typischer Workflow
Die Arbeit an einer Modelländerung kann wie folgt aussehen:
# ändere das Anwendungsmodell
vim herd/_model.goat
# generiere die daraus resultierenden Änderungen
goat re
# prüfe das Ergebnis
git diff
# bei Bedarf den Code manuell nachbearbeiten
# führe die Tests aus
goat run:script --path=herd/test.goat
# prüfe den finalen Änderungsumfang
git statusBei einer rein geschäftlichen oder implementierungsbezogenen Änderung ist möglicherweise keine Regenerierung erforderlich. Du kannst den entsprechenden Codeabschnitt direkt ändern und die Tests ausführen.
Wichtigste Regel
Anwendungsspezifische Änderungen gehören nach child/, Generatorvorlagen nach herd/gen/templates/. Änderungen der Grundlage werden generiert; goatapp/ nicht von Hand bearbeiten.