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 Datei child/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.goat

beschreibt 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
      ↓
Anwendungscode

Das 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 re

Der 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
      ↓
Tests

Nach der Regenerierung solltest du immer Folgendes prüfen:

git diff

Dadurch 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.goat

Beispiele:

  • 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:persist

mü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.goat

definiert sind, können zur Generierung von Elementen verschiedener Anwendungsschichten verwendet werden.

Beispielhafter Ablauf:

Entität
  ↓
Backend-Modell
  ↓
DAO / Repository
  ↓
DTO
  ↓
API
  ↓
Formular
  ↓
Listenansicht

Dadurch 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.sh

kann 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
Frontend

In 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 Schichten

Die 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 status

Bei 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.