Architektur · v0.8.1

Architekturüberblick

Diese Seite erklärt, wie die Agentic Software Factory technisch aufgebaut ist — von der UI-Schicht bis zu den Adaptern, die mit den Coding-CLIs sprechen. Sie richtet sich an Architektinnen und Entwickler, die einen Überblick über Schichten, Module, Datenflüsse und die wichtigsten Designentscheidungen wollen, ohne sofort in den Code einzutauchen. Wer Schritt-für-Schritt durch die Bedienung will, ist in der Einführung besser aufgehoben.

Was ist das Ziel der Architektur?

Die Plattform ist eine Control Plane für AI-gestützte Softwareentwicklung. Sie kapselt vier Aufgaben in einer einzigen Webanwendung: Projekt-Erfassung, Artefakt-Generierung, Run-Orchestrierung und Quality-Bewertung. Du arbeitest nicht direkt mit der Shell-CLI von Claude Code, Codex, Gemini oder Aider — die Plattform ruft diese Tools als Subprozesse auf, sammelt deren Output, schreibt ihn strukturiert in die Datenbank und macht ihn live in der UI sichtbar.

Die Architektur folgt zwei sehr klassischen Prinzipien, die in v1 bewusst nicht mit Buzzwords überfrachtet werden:

Browser → Spring MVC + Thymeleaf → Application Services → Domain Model
                                                               ↓
                       PostgreSQL ←─ Repositories ←─ Adapter (Claude / Codex / Gemini / Aider / Mock / Git / Filesystem)
Merksatz: Die Plattform ist klein und lesbar. Wenn du die Modulgrenzen verstehst, verstehst du die Plattform.

Schichten und Module

Innerhalb des Maven-Moduls app liegen alle fachlichen Module unter io.softwarefabrik.app.<modul>. Pro Modul gibt es maximal vier Unterpakete:

Schicht-Layout pro Modul

  • domain/ — Entities, Value Objects, Repositories als Interface, fachliche Services. Keine Spring-, JPA- oder Web-Annotationen.
  • application/ — Use-Case-Services, die Domain-Methoden orchestrieren. Hier liegen Transaktionsgrenzen (@Transactional).
  • web/ — Spring-MVC-Controller, Thymeleaf-Bindings, DTOs für die UI, REST-Endpunkte für SSE.
  • infrastructure/ — JPA-Implementierung der Repositories, externe Adapter, Filesystem-Zugriffe, Scheduled Jobs.

Abhängigkeitsregeln

  • domain hängt von nichts ab.
  • application hängt nur von domain ab.
  • web hängt von application ab, nicht direkt von domain.
  • infrastructure implementiert domain-Ports und kann Spring-Magie nutzen.
  • Kein Modul greift quer in das domain-Paket eines anderen Moduls, sondern nur über dessen application-Service.

ArchUnit-Tests im Modul app/src/test/java/.../architecture/ pinnen genau diese Regeln. Wer eine falsche Abhängigkeit reinzieht, kriegt einen roten CI-Lauf — die Architektur wird also nicht durch gute Absicht, sondern durch automatisierte Tests garantiert.

Die Module der v0.8.1-Codebasis im Überblick:

agentauditcommonconductorexecutiongitlicensepolicyprojectdefinitionpromptqualitygatereviewrunsecretssecuritysettingsteamvalidationwebwizard

Module im Detail

Hier ein kurzer Steckbrief pro Modul, damit du beim Lesen des Codes oder beim Suchen einer Funktion schnell weißt, wo du nachschauen musst:

agent

Verwaltet die Agentenrollen (Architect, Developer, Reviewer, QA, Security, Documentation, Merge/Release) inkl. preferredModel pro Rolle. Hauptklassen: AgentDefinition, AgentRoleService. Routen: /agents.

audit

Append-only Audit-Log für sicherheitsrelevante Aktionen — Login-Versuche, Setting-Änderungen, Run-Starts, Approval-Entscheidungen. Hauptklasse: AuditEvent. Tabelle audit_event.

common

Shared utilities ohne fachlichen Bezug: ID-Generator, Clock-Abstraktion, gemeinsame Validatoren. Bewusst klein gehalten.

conductor

Neu seit v0.6.0: schreibt vor jedem Run .claude/settings.local.json (Plugin-Whitelist + Skills-Pfad) und .claude/agents/<role>.md pro aktivem Team-Mitglied ins Workspace. Hauptklassen: PluginCatalog, SkillsCatalog, ConductorWorkspaceWriter, ConductorRunPreparation.

execution

Das Herz der Run-Ausführung: ExecutionAdapterRegistry, Sandbox-Implementierungen LocalProcessSandbox + ContainerProcessSandbox mit ExecutionSandboxFactory (Auswahl per Setting execution.sandbox.variant), Adapter für Claude, Codex, Gemini, Aider und Mock. Pro Adapter ein eigenes infrastructure/<name>-Paket. Unter claudecode/ seit v0.7.0 zusätzlich ClaudeStreamJsonParser für Live-Token-Events und TokenEstimator für lokale Vorab-Schätzungen.

git

Git-Operationen im Workspace: git init, Auto-Commits, Checkpoints, Diff- und Log-Ausgabe für die Run-Detail-Seite. Seit v0.8.0 diffSeit(workspace, sinceSha, maxBytes) für Inline-Diffs vor Approval. Shellt direkt zu git statt JGit, um die Kompatibilitätsmatrix klein zu halten.

license

Lizenzlogik: DEMO / COMMUNITY / FULL Tiers, Lease-JWT-Verifikation gegen Keycloak-Public-Key, Limit-Enforcement. Routen: /license, /admin/license.

policy

Approval-Policies: welche Phasen brauchen menschliche Freigabe, welche laufen automatisch durch. Speichert ApprovalDecision-Records.

projectdefinition

Die ProjectDefinition-Entity mit allen Editor-Feldern (Vision, Zielgruppe, Tech, Architektur, Sicherheit, …). Routen: /projects, /projects/{id}/edit.

prompt

Generiert die sechs Markdown-Artefakte (PROJECT.md, INSTRUCTIONS.md, AGENTS.md, WORKFLOW.md, DEFINITION_OF_DONE.md, README.md) aus den Projektfeldern. Templates liegen unter resources/prompts/.

qualitygate

Aggregiert die Reviewer-Findings zum Quality-Gate-Verdict (PASSED / WARNING / FAILED / SKIPPED / ERROR). Sonderregeln für SECURITY/HIGH und ARCHITECTURE/CRITICAL. Routen: /runs/{id}/quality-gate.

review

Implementierungen der Reviewer: aider-review, claude-review, security, architecture-reviewer, hallucination-review. CLI-Aufrufer im read-only-Modus oder statische Heuristiken.

run

Das Run-Modell mit Phasen, Status, Logs, Token-Usage und Workspace-Pfad. Hauptklassen: Run, RunPhase, RunOrchestrationService, WorkspaceService. Routen: /runs, /runs/{id}.

secrets

Verschlüsselte Speicherung von API-Keys (Anthropic, OpenAI, Gemini) im Postgres. AES-GCM mit Master-Key aus SOFTWAREFABRIK_SECRETS_MASTER_KEY. Routen: /integrations.

security

Spring-Security-Konfiguration: Login, Bootstrap-Admin, Rollenmodell (USER / ADMIN), CSRF, BCrypt-Passwörter. Hauptklasse: SecurityConfig.

settings

Globale Plattform-Defaults unter /einstellungen mit 5-Minuten-TTL-Cache und Override-Ordnung PROJECT > USER > GLOBAL > YAML. Tabelle app_setting (V9). Seit v0.7.0 zusätzlich Schlüssel execution.sandbox.variant für die Container-Sandbox.

team

Team-Zusammenstellung aus Agentenrollen pro Projekt. Wird in AGENTS.md serialisiert.

validation

Build-Validierung pro Run: mvn verify, npm run build oder konfigurierter Build-Befehl. Schreibt BuildResult.

web

Querschnittsthemen der UI: globale Layouts, kopfbereich.html, Theme-Toggle, Dashboard-Controller. Keine fachliche Logik.

wizard

Vier-Schritt-Assistent unter /wizard. Hauptklassen: WizardController, WizardService, WizardDraft, TemplateRegistry, ToggleRegistry, VersionLookupClient, WizardCostEstimator. Tabelle wizard_draft (V12), version_cache (V13). Sechs Templates registriert (Spring Boot, Static Frontend, .NET, Python, Node, Existing-Repo-Import); v0.8.x bringt Progress-Stepper, Objective-Vorschau und lokale Kosten-Schätzung in Schritt 4.

Datenflüsse: vom Wizard bis zum Run

Der typische Lebenszyklus eines Projekts durchläuft fünf Stationen. Jede schreibt persistent in Postgres, jede ist in der UI sichtbar:

┌──────────┐   ┌────────────┐   ┌────────────┐   ┌───────┐   ┌──────────────┐
│ Wizard   │ → │ Project    │ → │ Artefakte  │ → │ Run   │ → │ Quality Gate │
│ /wizard  │   │ (DRAFT)    │   │ (Markdown) │   │       │   │ (Verdict)    │
└──────────┘   └────────────┘   └────────────┘   └───────┘   └──────────────┘
     │              │                │              │                │
     ▼              ▼                ▼              ▼                ▼
 wizard_draft   project_def   prompt_artifact   run, run_phase    quality_gate_run
                                                run_log,
                                                token_usage
  1. Wizard sammelt Antworten in wizard_draft (überlebt Browser-Reload und Server-Restart). Bei Abschluss wird ein ProjectDefinition erzeugt und der Draft auf completed gesetzt.
  2. ProjectDefinition hält alle Editor-Felder. Status ist anfangs DRAFT, geht nach Artefakt-Generierung auf READY.
  3. Artefakte werden vom PromptAssemblyService aus den Projektfeldern erzeugt. Sie sind editierbar — der Agent liest immer die zuletzt gespeicherte Version.
  4. Run orchestriert die Phasen. Der RunOrchestrationService ist asynchron (Spring @Async), die UI pollt nicht, sondern hört per SSE.
  5. Quality Gate wird am Ende oder auf Knopfdruck zwischendurch angetriggert, ruft Reviewer parallel auf und aggregiert die Findings zum Verdict.

Persistenz: Postgres + Flyway

Die Plattform nutzt PostgreSQL als einzige Datenquelle. Schema-Änderungen laufen ausschließlich über Flyway-Migrationen unter app/src/main/resources/db/migration/. Jede Migration ist nummeriert (V<n>__<name>.sql) und unveränderlich, sobald sie einmal in Produktion war — neue Änderungen kommen als zusätzliche Migration, nicht als Edit der alten.

MigrationInhalt
V1Initial-Schema: project_definition, agent_definition, team, run, run_phase.
V2Logs und Token-Usage: run_log, token_usage_event.
V3Approval-Policies: approval_policy, approval_decision.
V4Verschlüsselte Secrets: integration_secret.
V5Audit-Log: audit_event.
V6Quality-Gate-Tabellen: quality_gate_run, review_finding.
V7License-Tabellen: license, license_lease.
V8Build-Resultate: build_result.
V9v0.4.0: app_setting mit Scope-Spalte (GLOBAL / USER / PROJECT) und Audit-Trail.
V10Run-Tags und Quick-Start-Marker.
V11v0.4.0: agent_preferred_model — Spalte für rolle-spezifische Modellauswahl.
V12v0.4.0: wizard_draft — JSON-Spalte für State, Schritt-Zähler, Template-ID, Cleanup-TTL.
V13v0.4.0: version_cache — Cache-Key, JSON-Wert, refreshed_at, last_error. Java-berechnete Staleness, kein DB-Computed-Column (damit das H2-Test-Profil funktional bleibt).
V14v0.6.0: erweiterte agent_definition für Conductor (Mission, aktive Skills).
V15v0.7.0: run_template — Adapter, Team, Objective, optionaler Projekt-Scope, Audit-Felder. Quelle für "Run aus Template starten".
Test-Strategie: Tests laufen gegen H2 mit Postgres-Kompatibilitätsmodus. Damit das geht, vermeiden Migrationen Postgres-spezifische Features wie GENERATED ALWAYS AS. Wo das doch mal nötig wäre, gibt es ein db.migration.h2/-Override.

Adapter-Registry

Adapter sind Spring-Beans, die das Interface ExecutionAdapter implementieren. Beim Start sammelt die ExecutionAdapterRegistry alle verfügbaren Beans in einer Map <Name → Adapter>. Der Name kommt aus der Adapter-Konstante (z.B. "claudecode", "codex", "gemini", "aider", "mock").

Beim Start eines Runs wird der zu nutzende Adapter in dieser Reihenfolge bestimmt:

  1. Run-spezifischer Override — wenn der Nutzer beim Anlegen des Runs einen Adapter ausgewählt hat.
  2. Projekt-Default — wenn das Projekt einen preferredAdapter hat.
  3. User-Default — wenn der eingeloggte Nutzer ein USER-Setting gesetzt hat.
  4. Globaler Default — aus app_setting mit Scope GLOBAL.
  5. YAML-Default — aus application.yml als letzter Fallback.

Diese Override-Reihenfolge — PROJECT > USER > GLOBAL > YAML — wird vom SettingService implementiert und gilt überall in der Plattform, nicht nur für Adapter.

// vereinfacht
ExecutionAdapter adapter = registry.byName(
    settingService.resolve("execution.adapter.default", scope)
);
Mock-Adapter: Ist immer registriert und funktioniert ohne API-Key. Er schreibt deterministische Pseudo-Tokens in den Workspace und ist ideal, um die Plattform-Mechanik ohne Kosten kennenzulernen oder Bugs lokal zu reproduzieren.

Versions-Cache und @Scheduled-Jobs

Damit der Wizard immer aktuelle stabile Versionen vorbelegen kann, hält die Plattform einen Cache mit Versions-Lookups. Der Cache wird täglich um 03:00 Uhr per @Scheduled-Job aktualisiert; ein zweiter Job läuft um 04:00 Uhr und löscht abgelaufene wizard_draft-Einträge (TTL 30 Tage).

Die Versions-Lookups folgen dem Strategy-Pattern: ein VersionLookupClient-Interface, drei Implementierungen:

Der Cache-Key ist eine fachliche Konstante, kein technischer Pfad — z.B. spring-boot.3.x, archunit.latest, owasp.dependency-check.latest, trivy.cli.latest, playwright.npm.latest. So lässt sich die Quelle wechseln, ohne die Konsumenten zu brechen.

Staleness wird in Java berechnet (Schwelle: 25 Stunden; gibt einen Toleranzpuffer für den 03:00-Job): now - refreshed_at > 25h. Wir machen das bewusst nicht als Postgres-GENERATED-Spalte, weil das H2-Test-Profil sonst stolpern würde.

Admins können den Cache unter /einstellungen/wizard/versions inspizieren. Pro Eintrag werden Cache-Key, gelieferter Wert, refreshed_at, last_error und ein Stale-Badge angezeigt; ein Knopf "Jetzt manuell refreshen" triggert den Lookup synchron für genau diesen Key.

Sandbox-Modell

Jeder Run bekommt einen eigenen Workspace — ein lokales Verzeichnis unter SOFTWAREFABRIK_WORKSPACES_ROOT/<project-slug>/<run-id>. Der Adapter (z.B. claudecode) wird in diesem Verzeichnis als eigener Prozess gestartet (LocalProcessSandbox). Jeder Run hat damit seine eigene Git-Historie, sein eigenes Build-Output, seine eigene node_modules oder target-Folder — Runs können nicht versehentlich Files anderer Runs überschreiben.

Die LocalProcessSandbox setzt Umgebungsvariablen sauber pro Prozess (kein System.setenv auf Plattform-Ebene), terminiert Prozesse hart bei Cancel/Timeout (destroyForcibly()) und schreibt stdout und stderr zeilenweise in die run_log-Tabelle plus parallel in den SSE-Stream.

Container-Variante: Seit v0.7.0 existiert zusätzlich die ContainerProcessSandbox (ADR-0011 Accepted, Variante B). Sie startet jeden Agent in einem ephemerem Docker- oder Podman-Container mit --cpus 2 --memory 4g --pids-limit 512 --read-only, Bindmount auf den Workspace und --network=none als Default. Aktiviert per Setting execution.sandbox.variant=container; fehlt Docker im PATH, fällt die ExecutionSandboxFactory mit Log-Warnung auf die lokale Variante zurück. Single-Tenant-Hosts merken nichts; Enterprise-Tier kann ohne Code-Änderung umschalten.

Sicherheit: Bootstrap-Admin, Audit, Settings, Approvals

Sicherheit ist in der Architektur nicht ein Modul, sondern eine Querschnittsdisziplin. Die wichtigsten Punkte:

Settings-Architektur

Das Settings-Modul ist das Schaltbrett der Plattform. Es kombiniert drei Designentscheidungen, die zusammen funktionieren:

  1. Scope-Hierarchie: jedes Setting ist einem Scope zugeordnet (GLOBAL, USER, PROJECT). SettingService.resolve(key, context) sucht von eng nach weit: erst PROJECT, dann USER, dann GLOBAL, dann YAML.
  2. 5-Minuten-TTL-Cache: der SettingService cached resolved Werte. Damit kostet ein resolve in 99 % der Fälle nichts. Bei einer UI-Änderung wird der Cache nicht hart invalidiert — er wartet maximal 5 Minuten, dann ist der neue Wert da. Kein App-Restart nötig.
  3. Audit-Trail: jede Schreiboperation auf app_setting erzeugt einen audit_event-Eintrag mit Subject, Key, Old-Value, New-Value. Damit ist jederzeit nachvollziehbar, wer wann was geändert hat.

Konkrete Settings, die heute verwaltbar sind:

KeyBedeutungTypischer Wert
workspace.rootWurzelverzeichnis für alle Run-Workspaces/var/softwarefabrik/workspaces
git.user.nameGit-Author für Auto-CommitsSoftware Factory Bot
git.user.emailGit-E-Mailbot@softwarefabrik.local
execution.adapter.defaultDefault-Execution-Adapterclaudecode
execution.model.claudecodeDefault-Modell für Claude Codeclaude-sonnet-4-6
execution.model.codexDefault-Modell für Codexgpt-5
budget.tokens.dailyTages-Token-Cap2000000
budget.tokens.weeklyWochen-Token-Cap10000000
budget.threshold.softSoft-Schwelle in Prozent (warnt nur)80

Live-Streaming und SSE

Die Run-Detail-Seite streamt drei Dinge live aus dem Backend in den Browser: Logs, Phasen-Updates und Token-Counter. Statt Polling nutzt die Plattform Server-Sent Events (SSE) — ein offener HTTP-Stream, der vom Backend einseitig Push-Nachrichten an den Browser schickt.

Die SSE-Endpoints liegen unter /runs/{id}/stream/logs, /runs/{id}/stream/phases, /runs/{id}/stream/tokens. Falls SSE durch einen Reverse-Proxy nicht durchkommt (manche Corporate-Setups blocken HTTP-Streaming), schaltet die UI einen Polling-Fallback an, der dieselben Daten in 2-Sekunden-Takten holt.

Test-Strategie und Coverage-Gate

Tests sind nicht optional, sondern Architekturbestandteil. Drei Säulen:

Coverage-Gate: JaCoCo 80/80 (Lines + Branches). Wer einen Service ohne Test mergen will, kommt durch mvn verify nicht durch. Das ist absichtlich streng — die Plattform ist kein Prototyp mehr.

CI-Pipeline (.github/workflows/ci.yml): Build + Test + ArchUnit + JaCoCo + OWASP Dependency-Check + Trivy-File-Scan. OWASP läuft non-blocking ohne NVD-Key, Trivy nur im dedizierten Job.

Was bewusst NICHT in der Architektur ist

Genauso wichtig wie das, was die Plattform tut, ist das, was sie absichtlich nicht tut. Diese Auslassungen halten v1 lesbar und ausbaubar:

Merksatz: v1 soll nicht alles können. Sie soll die richtigen Grenzen und Erweiterungspunkte sauber setzen. Das macht den Code in 6 Monaten noch lesbar.

Lizenz- und Identitätsschicht

Neben dem Produkt-Backend existiert ein eigener Stack für Identität und Lizenzierung, entkoppelt von der Plattform-DB:

Der Client startet nach der Installation im registrierungsfreien DEMO-Modus (nur Mock-Adapter, harte Limits im Code, kein Server-Kontakt). Nach einer Registrierung per Device-Flow wird automatisch eine COMMUNITY-Lizenz angelegt; ein Admin kann den Tier in der Admin-UI auf FULL anheben. Das Lease legt fest, welcher Adapter verwendet werden darf und wie Run-Anlage und Team-Größe begrenzt sind.

Wo es weitergeht

EinführungBedienungs-orientierter Walkthrough vom Login bis zum ersten Run.
SchnellstartKürzeste Route zum ersten lokalen Run mit Mock-Adapter.
TutorialVollständiger Workflow gegen Claude Code.
Whitepaper76 Seiten konzeptioneller Hintergrund.
FAQAntworten auf typische Fragen.
Live-DemoKlick dich durch eine echte Instanz.