Konzept: Weitere Core/Client Erweiterungs-Patterns¶
Stand: März 2026
Status: Entwurf
Bereits umgesetzt¶
| Bereich | Pattern | Beschreibung |
|---|---|---|
| Flutter/Dart | Git-Dependency + ClientConfig | Core-Packages via pubspec.yaml, 50+ Override-Punkte |
| Firestore Rules | _core + _extra Merge |
Core-Regeln + Client-Erweiterungen, gemerged beim Deploy |
| Storage Rules | _core + _extra Merge |
Identisch zu Firestore Rules |
| Firestore Indexes | _core + _extra Merge |
JSON-Array-Merge via Node.js |
| Cloud Functions | Merge-at-Deploy | Core + Client Jobs/Connectors/Triggers in einer Codebase |
Vorschlag 1: Unified Deploy Script (deploy_all.sh)¶
Problem¶
Aktuell existieren 3 separate Deploy-Scripts im Client-Repo:
deploy_rules.sh → Firebase Rules + Indexes
deploy_functions.sh → Cloud Functions (Merge-at-Deploy)
deploy_cors.sh → CORS auf Storage Bucket
Plus Hosting-Deploy in auto-deploy.yml (CI) oder manuell via Firebase CLI. Bei einem vollständigen Redeploy muss man alles einzeln ausführen und die Reihenfolge kennen.
Lösung¶
Ein deploy_all.sh das alle Schritte orchestriert:
Ablauf:
1. Rules deployen (deploy_rules.sh)
2. CORS deployen (deploy_cors.sh)
3. Functions deployen (deploy_functions.sh)
4. Hosting deployen (flutter build web + firebase deploy --only hosting)
Vorteile:
- Ein Befehl für alles → weniger Fehler
- Richtige Reihenfolge garantiert (Rules vor Functions, da Triggers auf Rules-Änderungen reagieren können)
- Skip-Flags für partielle Deploys
- Ideal nach Core-Update: ./deploy_all.sh gemuesebau-steiner-dev
Aufwand: Gering (~80 Zeilen Bash)
Priorität: ⭐⭐⭐ Hoch
Vorschlag 2: CI/CD Workflow als Reusable Workflow¶
Problem¶
auto-deploy.yml ist aktuell eine statische Kopie in jedem Client-Repo. Wenn der Core eine CI-Verbesserung macht (z.B. neuer Build-Step, Sentry-Sourcemaps, Caching-Optimierung), müssen alle Client-Repos manuell aktualisiert werden — exakt das Problem, das wir bei Functions mit Merge-at-Deploy gelöst haben.
Ist-Zustand¶
Core: .github/workflows/web-core-deployment.yml (baut Core-Demo-Apps)
.github/workflows/notify-clients.yml (triggert Clients via repository_dispatch)
Client: .github/workflows/auto-deploy.yml (STATISCHE KOPIE, 80+ Zeilen)
Lösung: Reusable Workflow im Core¶
Core stellt einen gemeinsamen Workflow bereit:
# Core: .github/workflows/client-deploy-reusable.yml
name: Client Deploy (Reusable)
on:
workflow_call:
inputs:
environment:
type: string
default: 'production'
deploy_hosting:
type: boolean
default: true
deploy_functions:
type: boolean
default: true
deploy_rules:
type: boolean
default: true
flutter_build_args:
type: string
default: ''
secrets:
FIREBASE_SA:
required: true
SENTRY_DSN:
required: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkout Core
uses: actions/checkout@v4
with:
repository: Tech-Schuppen/easySale
path: _core
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Flutter
uses: subosito/flutter-action@v2
with: { channel: stable, cache: true }
# ... Build + Deploy Steps (zentral gepflegt)
Client nutzt den Workflow mit 10 Zeilen:
# Client: .github/workflows/auto-deploy.yml
name: Build & Deploy
on:
repository_dispatch:
types: [core-updated]
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
uses: Tech-Schuppen/easySale/.github/workflows/client-deploy-reusable.yml@main
with:
environment: production
flutter_build_args: '--dart-define=CUSTOM_FLAG=true'
secrets:
FIREBASE_SA: ${{ secrets.FIREBASE_SA_PROD }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
Vorteile:
- CI-Verbesserungen wirken automatisch für alle Clients
- Client-spezifische Parameter via inputs
- Weniger Maintenance pro Client-Repo
- Einheitliche Build-Qualität
Einschränkung: Das Repo Tech-Schuppen/easySale muss für Client-Repos lesbar sein (Enterprise/Org-level, oder Public Repo für Workflow). Alternativ: Workflow als separates Public Repo.
Aufwand: Mittel (Reusable Workflow schreiben + bestehende Client-Workflows migrieren)
Priorität: ⭐⭐⭐ Hoch
Vorschlag 3: CORS _core + _extra Merge¶
Problem¶
Aktuell pflegt jeder Client seine CORS-Konfiguration komplett selbst (firebase/cors.dev.json, firebase/cors.prod.json). Wenn Core neue Response-Headers oder Methoden hinzufügt, müssen alle Clients manuell aktualisiert werden.
Ist-Zustand¶
// Client cors.dev.json — vollständig self-managed
[{
"origin": ["http://localhost:8080", "https://gemuesebau-steiner-dev.web.app", ...],
"method": ["GET", "HEAD", "PUT", "POST", "DELETE", "OPTIONS"],
"responseHeader": ["Content-Type", "Authorization", "X-Requested-With"],
"maxAgeSeconds": 3600
}]
Lösung: Merge-Pattern wie bei Rules¶
Client-Struktur:
firebase/
cors_core.dev.json ← Wird beim Core-Update automatisch aktualisiert
cors_core.prod.json ← Wird beim Core-Update automatisch aktualisiert
cors_extra.dev.json ← Client-spezifische Erweiterungen
cors_extra.prod.json ← Client-spezifische Erweiterungen
cors.dev.json ← GENERIERT (gitignored)
cors.prod.json ← GENERIERT (gitignored)
Core liefert Standard-Config:
// cors_core.dev.json
[{
"origin": ["http://localhost:8080", "http://localhost:8081"],
"method": ["GET", "HEAD", "PUT", "POST", "DELETE", "OPTIONS"],
"responseHeader": ["Content-Type", "Authorization", "X-Requested-With", "Accept", "Origin"],
"maxAgeSeconds": 3600
}]
Client fügt nur eigene Origins hinzu:
// cors_extra.dev.json
{
"extraOrigins": [
"https://gemuesebau-steiner-dev.web.app",
"https://gemuesebau-steiner-dev.firebaseapp.com"
]
}
Merge-Logik (in deploy_cors.sh):
# Core-Origins + Client-Origins zusammenführen
node -e "
const core = require('./$CORS_CORE');
const extra = require('./$CORS_EXTRA');
core[0].origin = [...core[0].origin, ...(extra.extraOrigins || [])];
if (extra.extraHeaders) core[0].responseHeader.push(...extra.extraHeaders);
console.log(JSON.stringify(core, null, 2));
" > "$CORS_OUTPUT"
Vorteile: - Core kann Methods/Headers aktualisieren → alle Clients bekommen es - Client muss nur seine eigenen Hosting-URLs pflegen - Gleicher Pattern wie Rules → vertrautes Konzept
Aufwand: Gering (~30 Zeilen Merge-Logik)
Priorität: ⭐⭐ Mittel
Vorschlag 4: Feature-Flag Defaults aus dem Core¶
Problem¶
firebase_config_*.json (pro Client/Environment) enthält Feature-Flags. Aktuell definiert jeder Client alle Features selbst. Wenn Core ein neues Feature einführt (z.B. advanced_search: true), muss jeder Client manuell aktualisiert werden.
Ist-Zustand¶
// Client: erp/assets/firebase_config/firebase_config_development.json
{
"apiKey": "...",
"projectId": "gemuesebau-steiner-dev",
"storageBucket": "...",
"cloudFunctionsRegion": "europe-west1"
}
Features werden aktuell über ClientConfig.features in Dart gesetzt — kein zentraler Config-Mechanismus.
Lösung: Feature-Defaults vom Core + Client-Overrides¶
Core stellt Default-Features bereit:
// core/shared/assets/default_features.json
{
"order_export": true,
"article_import": true,
"customer_import": true,
"advanced_search": true,
"stock_management": true,
"multi_warehouse": false,
"invoice_generation": false,
"shop_system": true
}
Client überschreibt nur Abweichungen:
// Client: erp/assets/firebase_config/feature_overrides.json
{
"multi_warehouse": true,
"shop_system": false,
"custom_client_feature": true
}
Merge-Logik (im Dart-Code beim App-Start):
// In AppConfig oder ClientConfig
Map<String, bool> get mergedFeatures {
final defaults = json.decode(rootBundle.loadString('assets/default_features.json'));
final overrides = json.decode(rootBundle.loadString('assets/feature_overrides.json'));
return {...defaults, ...overrides}; // Client gewinnt bei Konflikten
}
Vorteile:
- Neue Core-Features sind sofort für alle Clients verfügbar
- Clients aktivieren/deaktivieren nur was sie brauchen
- Ein Blick auf feature_overrides.json zeigt sofort Client-Anpassungen
- Keine Dart-Code-Änderung im Client nötig für Feature-Toggles
Aufwand: Gering (eine JSON-Datei im Core, Merge-Logik ~20 Zeilen Dart)
Priorität: ⭐⭐ Mittel
Vorschlag 5: Hosting Security Headers _core + _extra¶
Problem¶
firebase.json im Client enthält Hosting-Konfiguration mit Security-Headers (CSP, HSTS, X-Frame-Options etc.). Wenn Core einen Header aktualisiert (z.B. neue CSP-Directive), müssen alle Clients ihre firebase.json manuell anpassen.
Ist-Zustand¶
// Client: firebase/firebase.json
{
"hosting": {
"public": "../erp/build/web",
"rewrites": [{"source": "**", "destination": "/index.html"}]
}
}
Aktuell hat der Client minimale Headers. Core hat umfangreichere.
Lösung: firebase.json Generierung beim Deploy¶
Core liefert Standard-Hosting-Config:
// firebase_hosting_core.json
{
"headers": [
{
"source": "**",
"headers": [
{"key": "X-Frame-Options", "value": "DENY"},
{"key": "X-Content-Type-Options", "value": "nosniff"},
{"key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains"},
{"key": "Referrer-Policy", "value": "strict-origin-when-cross-origin"},
{"key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()"}
]
}
]
}
Client kann Headers ergänzen oder überschreiben:
// firebase_hosting_extra.json
{
"extraHeaders": [
{"key": "X-Custom-Header", "value": "gemuesebau-steiner"}
],
"overrideHeaders": {
"Permissions-Policy": "camera=(self), microphone=(), geolocation=()"
}
}
Deploy-Script merged Core + Extra → finale firebase.json.
Vorteile: - Security-Best-Practices zentral im Core - Client kann Client-spezifische Headers ergänzen (z.B. CSP für externe Dienste) - Neue Security-Headers wirken automatisch
Aufwand: Mittel (JSON-Merge-Logik + Deploy-Script-Anpassung)
Priorität: ⭐ Niedrig (erst relevant wenn Core komplexere Headers einführt)
Vorschlag 6: Cloud Tasks Queue Erweiterung¶
Problem¶
Aktuell erstellt setup_cloud_tasks_queue.sh nur die hartcodierte order-export-queue. Wenn ein Client einen Custom-Job hat, der eine eigene Queue braucht, gibt es keinen strukturierten Weg.
Lösung: Queue-Definition per Konfigurationsdatei¶
Client definiert benötigte Queues:
// Client: firebase/cloud_tasks_queues.json
[
{
"name": "custom-sync-queue",
"maxConcurrentDispatches": 5,
"maxDispatchesPerSecond": 2,
"maxAttempts": 3
}
]
Core-Queue (order-export-queue) wird immer erstellt. Client-Queues nur wenn Datei existiert.
Deploy-Script:
# Immer: Core-Queue
gcloud tasks queues create order-export-queue ...
# Optional: Client-Queues aus cloud_tasks_queues.json
if [[ -f "$CLIENT_DIR/firebase/cloud_tasks_queues.json" ]]; then
# JSON parsen und Queues erstellen
fi
Aufwand: Gering (~40 Zeilen Bash)
Priorität: ⭐ Niedrig (erst relevant wenn Clients Custom-Queues brauchen)
Priorisierte Roadmap¶
| Prio | Vorschlag | Aufwand | Nutzen |
|---|---|---|---|
| 1 | Unified Deploy Script | Gering | Sofort nutzbar, weniger Fehler |
| 2 | CI/CD Reusable Workflow | Mittel | Keine manuelle CI-Pflege pro Client |
| 3 | CORS Merge | Gering | Konsistente CORS-Updates |
| 4 | Feature-Flag Defaults | Gering | Neue Features automatisch verfügbar |
| 5 | Hosting Headers Merge | Mittel | Security-Updates automatisch |
| 6 | Cloud Tasks Extension | Gering | Nur bei Bedarf |
Empfehlung¶
Sofort umsetzen (Quick Wins): - Vorschlag 1 (deploy_all.sh) — am meisten praktischer Nutzen im Alltag - Vorschlag 3 (CORS Merge) — gleicher bewährter Pattern wie Rules
Nächste Iteration: - Vorschlag 2 (Reusable Workflow) — größter langfristiger Wartungs-Vorteil - Vorschlag 4 (Feature-Flag Defaults) — sinnvoll sobald Feature-Anzahl wächst
Bei Bedarf: - Vorschlag 5 (Hosting Headers) + Vorschlag 6 (Cloud Tasks)