Document the architecture decision for a modular multi-app system with: - Split-screen functionality between apps - Cross-app drag & drop support - Flexible deployment configurations - Scalability for 20+ apps Evaluates 5 approaches and explains why Micro-Frontend Orchestrator was chosen over monolith and other alternatives. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
14 KiB
ManaCore Workspace Orchestrator
Architektur-Entscheidung für modulares Multi-App-System mit Split-Screen und Drag & Drop
Status: Proposal Datum: 2024-12-12 Autor: Till Schneider / Claude
Executive Summary
Dieses Dokument beschreibt die Architektur des ManaCore Workspace Orchestrators - ein modulares System, das es ermöglicht:
- Mehrere Apps nebeneinander im Split-Screen anzuzeigen
- Drag & Drop zwischen Apps zu unterstützen
- Flexible Deployments mit unterschiedlichen App-Kombinationen zu erstellen
- Die Anzahl der Apps beliebig zu skalieren
Problemstellung
Aktuelle Situation
Das ManaCore-Ökosystem besteht aus mehreren unabhängigen SvelteKit-Anwendungen:
| App | Port (Dev) | Domain (Prod) |
|---|---|---|
| Calendar | 5179 | calendar.manacore.app |
| Contacts | 5184 | contacts.manacore.app |
| Todo | 5188 | todo.manacore.app |
| Chat | 5174 | chat.manacore.app |
| Clock | 5187 | clock.manacore.app |
| Picture | 5185 | picture.manacore.app |
Jede App ist eine eigenständige SvelteKit-Instanz mit:
- Eigenem Dev-Server und Production-Build
- Eigener Domain/Subdomain
- Eigenem Backend (NestJS)
- Geteilter Auth über Mana Core Auth (JWT)
Anforderungen
- Split-Screen: Zwei Apps nebeneinander anzeigen
- Drag & Drop: Elemente zwischen Apps verschieben (z.B. Kontakt auf Kalender droppen)
- Modulare Deployments: Kunde A bekommt nur Calendar+Todo, Kunde B bekommt alles
- Skalierbarkeit: System muss mit 20+ Apps funktionieren
- Wartbarkeit: Neue Apps einfach hinzufügen
Warum die aktuelle Architektur nicht ausreicht
- Separate Browser-Tabs: Kein Drag & Drop zwischen Tabs möglich
- iFrames: Drag & Drop über iFrame-Grenzen ist technisch problematisch (CORS, Event-Blocking)
- Keine geteilte State: Jede App hat eigenen Svelte-Kontext
Evaluierte Ansätze
1. Build-Time Feature Flags
Konzept: Zur Build-Zeit konfigurieren welche Apps inkludiert werden.
ENABLED_APPS=calendar,todo pnpm build
Bewertung:
- ✅ Minimale Bundle-Size
- ✅ Einfaches Konzept
- ❌ Neuer Build pro Konfiguration nötig
- ❌ Keine dynamische Aktivierung
- ❌ Viele Build-Artefakte zu managen
Fazit: Zu unflexibel für unterschiedliche Deployment-Szenarien.
2. Plugin-Architektur (Runtime Loading)
Konzept: Apps als Plugins zur Laufzeit laden.
// manifest.json
{
"apps": [
{ "id": "calendar", "enabled": true, "url": "/plugins/calendar.js" }
]
}
Bewertung:
- ✅ Ein Build, viele Konfigurationen
- ✅ Dynamische Aktivierung möglich
- ❌ Komplexe Plugin-API
- ❌ Versionierung zwischen Plugins
- ❌ Initiale Ladezeit durch viele Chunks
Fazit: Guter Ansatz, aber zu komplex für unsere Anforderungen.
3. Monorepo mit Conditional Exports
Konzept: Alle Apps als separate Packages, verschiedene Entry-Points pro Deployment.
packages/
├── app-calendar/
├── app-todo/
└── ...
deployments/
├── full/ # Alle Apps
├── productivity/ # Calendar + Todo
└── crm/ # Contacts + Calendar
Bewertung:
- ✅ Klare Package-Grenzen
- ✅ Gutes Dependency-Management
- ❌ Viele Packages zu maintainen
- ❌ Versionskoordination aufwändig
Fazit: Solide, aber zu viel Overhead.
4. Monolith (Alle Apps in einer SvelteKit-Instanz)
Konzept: Alle Apps in eine einzige SvelteKit-App zusammenführen.
src/routes/
├── calendar/[...rest]
├── todo/[...rest]
└── contacts/[...rest]
Bewertung:
- ✅ Triviales Drag & Drop (alles im selben DOM)
- ✅ Gemeinsamer Svelte-Store
- ✅ Einfache Implementierung
- ❌ Keine flexiblen Deployments möglich
- ❌ Bundle enthält immer alle Apps
- ❌ Skaliert schlecht bei 20+ Apps
- ❌ Einzelne Apps nicht unabhängig deploybar
Fazit: Löst Drag & Drop, aber widerspricht den Modularitäts-Anforderungen.
5. Micro-Frontend Orchestrator (Gewählt)
Konzept: Kombination aus Shell-Anwendung, App-Registry und Build-Optimierung.
Bewertung:
- ✅ Flexibel: Build-Time ODER Runtime-Konfiguration
- ✅ Skaliert auf viele Apps
- ✅ Klare Contracts zwischen Apps
- ✅ Drag & Drop ist First-Class-Citizen
- ✅ Code-Splitting out of the box
- ✅ Apps können einzeln oder zusammen deployed werden
- ⚠️ Initialer Setup-Aufwand
Fazit: Bester Trade-off zwischen Flexibilität und Komplexität.
Gewählte Architektur: Micro-Frontend Orchestrator
Architektur-Übersicht
┌─────────────────────────────────────────────────────────────┐
│ Workspace Shell │
│ ┌───────────────┬─────────────────┬───────────────────┐ │
│ │ App Registry │ Drag Context │ Split Router │ │
│ └───────────────┴─────────────────┴───────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ App Manifest │
│ { "calendar": {...}, "todo": {...}, "contacts": {...} } │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Calendar │ │ Todo │ │ Contacts │ ... │
│ │ Module │ │ Module │ │ Module │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ ↑ ↑ ↑ │
│ └───────────────┴───────────────┘ │
│ Shared Services Layer │
│ (Auth, Theme, i18n, API Client, Drag/Drop) │
└─────────────────────────────────────────────────────────────┘
Kernkomponenten
1. Workspace Shell
Die äußere Hülle, die immer geladen wird:
- Split-Pane Layout: Rendert 1-2 App-Panels nebeneinander
- App Registry: Kennt alle verfügbaren Apps und ihre Capabilities
- Drag Context: Globaler Drag-Layer über allen Panels
- Navigation: PillNavigation mit App-Switcher für Split-Screen
2. App Module
Jede App ist ein eigenständiges Modul:
interface ManaAppModule {
// Identifikation
id: string; // 'calendar'
name: string; // 'Kalender'
icon: string; // App-Icon
color: string; // Primärfarbe
// Capabilities
draggable: DragType[]; // Was kann aus dieser App gedraggt werden?
droppable: DropHandler[]; // Was kann diese App empfangen?
// UI
component: SvelteComponent; // Haupt-Komponente
routes: RouteDefinition[]; // Interne Routes
// Optional
toolbar?: SvelteComponent; // App-spezifische Toolbar
quickActions?: QuickAction[]; // Für QuickInputBar
}
3. Drag & Drop Registry
Zentrale Koordination für Cross-App Drag & Drop:
// Drag-Types die Apps exportieren können
type DragType =
| 'contact' // Kontakt-Karte
| 'event' // Kalender-Event
| 'task' // Todo-Task
| 'file' // Datei
| 'note'; // Notiz
// Drop-Handler die Apps registrieren
interface DropHandler {
accepts: DragType[];
zone: 'panel' | 'specific'; // Ganzes Panel oder spezifische Bereiche
onDrop: (item: DragItem, target: DropTarget) => void;
preview?: (item: DragItem) => SvelteComponent;
}
4. Deployment-Konfiguration
// manacore.config.ts
export default defineWorkspace({
// Welche Apps in diesem Build?
apps: ['calendar', 'todo', 'contacts'],
// Wie werden Apps geladen?
loadStrategy: 'lazy', // 'eager' | 'lazy' | 'on-demand'
// Feature-Flags pro App
features: {
calendar: {
views: ['week', 'month', 'agenda'],
sharing: true,
recurring: true,
},
todo: {
projects: true,
labels: true,
recurring: false,
},
contacts: {
import: true,
export: true,
googleSync: false,
}
},
// Erlaubte Drag & Drop Verbindungen
connections: [
{ from: 'contacts.contact', to: 'calendar.attendee' },
{ from: 'contacts.contact', to: 'todo.assignee' },
{ from: 'calendar.event', to: 'todo.task' },
{ from: 'todo.task', to: 'calendar.event' },
],
// Default Split-Screen Konfiguration
defaultLayout: {
primary: 'calendar',
secondary: null, // Kein Split-Screen als Default
}
});
Build-Output
dist/
├── shell.js # Workspace Shell (~50KB)
├── shared.js # Shared Services (~100KB)
├── manifest.json # App-Konfiguration
└── apps/
├── calendar.js # Calendar Module (lazy)
├── calendar.css
├── todo.js # Todo Module (lazy)
├── todo.css
├── contacts.js # Contacts Module (lazy)
└── contacts.css
Split-Screen URL-Schema
/workspace # Single App (Default)
/workspace?app=calendar # Calendar alleine
/workspace?left=calendar&right=todo # Split-Screen
/workspace?left=calendar&right=todo&split=60 # 60/40 Split
Drag & Drop Implementierung
Globaler Drag-Layer
<!-- WorkspaceShell.svelte -->
<div class="workspace">
<!-- App Panels -->
<div class="panels">
<AppPanel app={leftApp} />
{#if rightApp}
<Divider />
<AppPanel app={rightApp} />
{/if}
</div>
<!-- Globaler Drag Overlay (über allen Panels) -->
<DragOverlay />
</div>
Drag-Flow
- Drag Start: App signalisiert Drag mit Type und Payload
- Drag Move: Element wird im globalen Overlay gerendert
- Drag Over: Drop-Zonen in allen Apps highlighten
- Drop: Ziel-App erhält Payload und verarbeitet ihn
Beispiel: Kontakt auf Kalender droppen
// Contacts App registriert Draggable
ContactsModule.draggable = [{
type: 'contact',
getData: (contact) => ({
id: contact.id,
name: contact.displayName,
email: contact.email,
}),
preview: ContactDragPreview,
}];
// Calendar App registriert Drop-Handler
CalendarModule.droppable = [{
accepts: ['contact'],
zone: 'event-form', // Nur in Event-Formularen
onDrop: (item) => {
// Kontakt als Teilnehmer hinzufügen
addAttendee({
name: item.data.name,
email: item.data.email,
});
},
}];
Deployment-Szenarien
Szenario 1: Vollversion (SaaS)
// manacore.config.ts
apps: ['calendar', 'todo', 'contacts', 'chat', 'files', 'notes', ...]
Alle Apps verfügbar, User kann Split-Screen frei konfigurieren.
Szenario 2: Produktivitäts-Suite
// productivity.config.ts
apps: ['calendar', 'todo', 'notes']
features: {
calendar: { sharing: false }, // Keine Team-Features
}
Fokussiertes Bundle für Einzelnutzer.
Szenario 3: CRM-Paket
// crm.config.ts
apps: ['contacts', 'calendar', 'tasks']
connections: [
{ from: 'contacts.contact', to: 'calendar.attendee' },
{ from: 'contacts.contact', to: 'tasks.assignee' },
]
Kontakt-zentriertes Bundle mit relevanten Verknüpfungen.
Szenario 4: Single-App (Legacy-Kompatibilität)
// calendar-only.config.ts
apps: ['calendar']
features: {
splitScreen: false, // Kein Split-Screen UI
}
Einzelne App wie bisher, für Migration bestehender User.
Warum nicht Monolith?
Der Monolith-Ansatz (alle Apps in einer SvelteKit-Instanz) wurde bewusst nicht gewählt, obwohl er Drag & Drop trivial machen würde:
| Kriterium | Monolith | Orchestrator |
|---|---|---|
| Drag & Drop | Trivial | Erfordert Koordination |
| Bundle-Size | Immer alles | Nur aktivierte Apps |
| Flexible Deployments | Nicht möglich | Beliebig konfigurierbar |
| App-Unabhängigkeit | Keine | Vollständig |
| Skalierung (20+ Apps) | Problematisch | Kein Problem |
| Build-Zeit | Wächst mit jeder App | Konstant pro App |
| Team-Arbeit | Merge-Konflikte | Unabhängige Entwicklung |
Kernargument: Bei wachsender App-Anzahl wird der Monolith unwartbar. Der Orchestrator skaliert linear, der Monolith exponentiell (in Komplexität).
Migrations-Strategie
Phase 1: Workspace Shell erstellen
- Neue App:
apps/workspace - Implementiert Shell, Registry, Drag-Layer
- Kann bestehende Apps als "Legacy iFrames" einbetten (Fallback)
Phase 2: App-Module extrahieren
- Calendar, Todo, Contacts als Module refactoren
- Gemeinsame Komponenten in Shared Services
- Drag & Drop Contracts definieren
Phase 3: Schrittweise Migration
- Eine App nach der anderen in Orchestrator integrieren
- Parallelbetrieb: Standalone + Workspace
- Alte Standalone-Deployments weiter unterstützen
Phase 4: Neue Apps als Module
- Alle neuen Apps direkt als Module entwickeln
- Einheitliche App-Template verwenden
Offene Fragen
- Routing: Wie verhalten sich Deep-Links im Split-Screen?
- State-Sync: Sollen Apps Änderungen in Echtzeit sehen?
- Mobile: Split-Screen nur Desktop oder auch Tablet?
- Keyboard-Navigation: Wie wechselt Fokus zwischen Panels?
- Undo/Redo: Global oder pro Panel?
Nächste Schritte
- Workspace Shell Proof-of-Concept
- Drag & Drop Registry implementieren
- Calendar als erstes Modul migrieren
- Split-Screen Layout mit Resize
- Deployment-Pipeline anpassen