Comprehensive analysis of code duplication across the monorepo: - Backend patterns: ~2,300 LOC savings (metrics, main.ts, health) - Frontend stores: ~700 LOC savings (settings, navigation) - UI components: ~1,200 LOC savings (skeletons, duplicates) - Configurations: ~900 LOC savings (tsconfig, vite, drizzle) Total potential: ~5,200-6,500 LOC reduction Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
12 KiB
Konsolidierungsmöglichkeiten - Monorepo Analyse
Erstellt: 29. Januar 2026 Geschätzte Gesamteinsparung: ~6.500-8.000 LOC
Übersicht nach Priorität
| Priorität | Bereich | Geschätzte Einsparung | Aufwand |
|---|---|---|---|
| KRITISCH | Backend Metrics Migration | 350 LOC | Niedrig |
| HOCH | Skeleton Components | 800-1.000 LOC | Mittel |
| HOCH | App Settings Stores | 600-700 LOC | Mittel |
| HOCH | Main.ts/CORS Patterns | 1.800 LOC | Mittel |
| MITTEL | TypeScript Configs | 400 LOC | Niedrig |
| MITTEL | UI Component Cleanup | 400 LOC | Niedrig |
| MITTEL | Vite Configs | 300 LOC | Niedrig |
| MITTEL | Navigation Stores | 50 LOC | Niedrig |
| NIEDRIG | Drizzle Configs | 200 LOC | Niedrig |
| NIEDRIG | Logger Utilities | 130 LOC | Niedrig |
1. Backend Patterns (NestJS)
1.1 KRITISCH: Metrics Migration (350 LOC)
Problem: 7 Backends haben lokale MetricsService Implementierungen, obwohl @manacore/shared-nestjs-metrics existiert.
Betroffene Backends:
apps/chat/apps/backend/src/metrics/(50 LOC)apps/calendar/apps/backend/src/metrics/(50 LOC)apps/todo/apps/backend/src/metrics/(68 LOC)apps/contacts/apps/backend/src/metrics/(50 LOC)apps/skilltree/apps/backend/src/metrics/(50 LOC)apps/clock/apps/backend/src/metrics/(50 LOC)apps/planta/apps/backend/src/metrics/(50 LOC)
Aktion: Migriere zu @manacore/shared-nestjs-metrics (bereits vorhanden!)
// Vorher (50 LOC pro Backend)
@Injectable()
export class MetricsService implements OnModuleInit {
private readonly register: client.Registry;
// ... 45 weitere Zeilen
}
// Nachher (5 LOC)
import { MetricsModule } from '@manacore/shared-nestjs-metrics';
@Module({ imports: [MetricsModule.forRoot({ prefix: 'chat_' })] })
1.2 HOCH: Main.ts/CORS Setup (1.800 LOC)
Problem: 14 Backends haben fast identische main.ts mit CORS, ValidationPipe, GlobalPrefix.
Empfehlung: Erstelle @manacore/shared-nestjs-setup
// packages/shared-nestjs-setup/src/bootstrap.ts
export interface BootstrapOptions {
corsOrigins?: string[];
apiPrefix?: string;
excludeFromPrefix?: string[];
enableMetrics?: boolean;
defaultPort?: number;
}
export async function bootstrapApp(
AppModule: Type<any>,
options: BootstrapOptions = {}
): Promise<INestApplication> {
const app = await NestFactory.create(AppModule);
// CORS (25 LOC -> 1 LOC)
setupCors(app, options.corsOrigins);
// Validation (10 LOC -> 0 LOC)
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
}));
// Prefix (5 LOC -> 0 LOC)
app.setGlobalPrefix(options.apiPrefix || 'api/v1', {
exclude: options.excludeFromPrefix || ['health', 'metrics'],
});
return app;
}
Vorher (85 LOC pro Backend):
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const corsOrigins = process.env.CORS_ORIGINS?.split(',') || [...];
app.enableCors({ origin: corsOrigins, ... });
app.useGlobalPipes(new ValidationPipe({ ... }));
app.setGlobalPrefix('api/v1', { exclude: ['health'] });
// ...
}
Nachher (15 LOC):
import { bootstrapApp } from '@manacore/shared-nestjs-setup';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await bootstrapApp(AppModule, {
defaultPort: 3002,
enableMetrics: true,
});
await app.listen(process.env.PORT || 3002);
}
bootstrap();
1.3 MITTEL: Health Endpoints (170 LOC)
Problem: 13 Backends haben identische Health-Controller.
Empfehlung: Erstelle @manacore/shared-nestjs-health
// Vorher (14 LOC pro Backend)
@Controller('health')
export class HealthController {
@Get()
check() {
return { status: 'ok', timestamp: new Date().toISOString(), service: 'chat' };
}
}
// Nachher (1 LOC)
import { HealthModule } from '@manacore/shared-nestjs-health';
@Module({ imports: [HealthModule.forRoot('chat-backend')] })
2. Frontend Stores (Svelte 5)
2.1 HOCH: App Settings Stores (600-700 LOC)
Problem: 3 Apps (todo, calendar, contacts) haben fast identische Settings-Store Implementierungen mit localStorage Persistenz.
Betroffene Dateien:
apps/todo/apps/web/src/lib/stores/settings.svelte.ts(259 LOC)apps/calendar/apps/web/src/lib/stores/settings.svelte.ts(433 LOC)apps/contacts/apps/web/src/lib/stores/settings.svelte.ts(278 LOC)
Dupliziertes Pattern (100% identisch):
// Boilerplate in jedem (80-100 LOC):
- TypeScript Interface für Settings
- DEFAULT_SETTINGS Konstante
- STORAGE_KEY
- loadSettings() - localStorage laden + merge mit defaults
- saveSettings() - localStorage speichern
- let settings = $state(...)
- toggleImmersiveMode(), initialize(), set(), update(), reset(), getDefaults()
Empfehlung: Erstelle createAppSettingsStore<T>() Factory in @manacore/shared-stores
// packages/shared-stores/src/createAppSettingsStore.ts
export function createAppSettingsStore<T extends Record<string, any>>(
storageKey: string,
defaultSettings: T,
options?: { cloudSync?: boolean }
) {
let settings = $state<T>(defaultSettings);
function loadSettings(): T { /* localStorage logic */ }
function saveSettings(newSettings: T): void { /* localStorage logic */ }
return {
get value() { return settings; },
initialize() { settings = loadSettings(); },
set<K extends keyof T>(key: K, value: T[K]) { /* ... */ },
update(updates: Partial<T>) { /* ... */ },
reset() { settings = defaultSettings; saveSettings(settings); },
getDefaults() { return defaultSettings; },
};
}
Einsparung: ~200 LOC Boilerplate pro App = 600 LOC
2.2 MITTEL: Navigation Stores (50 LOC)
Problem: 9 Apps haben fast identische Navigation-Stores.
Pattern (5-6 LOC pro App):
import { writable } from 'svelte/store';
export const isSidebarMode = writable(false);
export const isNavCollapsed = writable(false);
Ausnahme: Clock (36 LOC) mit localStorage Persistenz + Media Query Listeners
Empfehlung: Factory in @manacore/shared-stores
export function createNavigationStore(options?: {
persist?: boolean;
mediaQueryCollapse?: string;
}) {
// ...
}
2.3 NIEDRIG: Theme Stores Migration
Problem: 2 Apps nutzen nicht @manacore/shared-theme:
apps/storage/apps/web/src/lib/stores/theme.svelte.ts(96 LOC - custom)apps/questions/apps/web/src/lib/stores/theme.ts(custom)
Aktion: Migriere zu createThemeStore() aus @manacore/shared-theme
3. UI Components
3.1 HOCH: Skeleton Components (800-1.000 LOC)
Problem: 31 Skeleton-Komponenten über Apps verteilt, obwohl shared-ui Primitives hat.
Betroffene Apps:
apps/contacts/- 11 Skeletons (925 LOC)apps/calendar/- 5 Skeletons (338 LOC)apps/todo/- 5 Skeletons
Shared-UI hat bereits:
SkeletonBox,SkeletonAvatar,SkeletonCard,SkeletonGrid,SkeletonList,SkeletonRow,SkeletonText
Empfehlung:
- Dokumentation für Skeleton-Komposition aus Primitives
- Page-Level Presets erstellen:
ListPageSkeleton,DetailPageSkeleton,GridPageSkeleton
3.2 MITTEL: Sofort löschbare Duplikate (144 LOC)
Picture App hat lokale Kopien von shared-ui Komponenten:
| Datei | LOC | shared-ui Alternative |
|---|---|---|
apps/picture/apps/web/src/lib/components/ui/Button.svelte |
53 | @manacore/shared-ui/Button |
apps/picture/apps/web/src/lib/components/ui/Input.svelte |
70 | @manacore/shared-ui/Input |
apps/picture/apps/web/src/lib/components/ui/Card.svelte |
21 | @manacore/shared-ui/Card |
Aktion: Lösche lokale Dateien, importiere aus shared-ui.
3.3 MITTEL: AppSlider Cleanup (240 LOC)
Problem: 8 Apps haben lokale AppSlider.svelte Kopien, obwohl shared-ui Version existiert.
Betroffene Apps: calendar, chat, contacts, manadeck, manacore, picture, presi, todo
Aktion: Verifiziere Import aus @manacore/shared-ui, lösche lokale Kopien.
3.4 NIEDRIG: LanguageSelector (75 LOC)
Problem: 5+ Apps haben identische LanguageSelector Implementierungen.
Empfehlung: Verschiebe nach @manacore/shared-ui/navigation/LanguageSelector.svelte
4. Konfigurationsdateien
4.1 MITTEL: TypeScript Configs (400 LOC)
Problem: Identische tsconfig.json in:
- 12+ NestJS Backends (95% identisch)
- 15+ SvelteKit Web Apps (99% identisch)
- 6 Expo Mobile Apps (99% identisch)
- 6+ Astro Landing Pages (95% identisch)
Empfehlung: Erstelle @manacore/shared-tsconfig
packages/shared-tsconfig/
├── nestjs.json
├── sveltekit.json
├── expo.json
├── astro.json
└── base.json
Vorher (30 LOC pro App):
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
// ... 25 weitere Zeilen
}
}
Nachher (3 LOC):
{
"extends": "@manacore/shared-tsconfig/nestjs"
}
4.2 MITTEL: Vite Configs (300 LOC)
Problem: 15 SvelteKit Apps haben 70% identische vite.config.ts.
Empfehlung: Factory-Funktion in @manacore/shared-vite-config
// packages/shared-vite-config/src/sveltekit.ts
export function createSvelteKitConfig(options: {
port: number;
packages?: string[];
}) {
return defineConfig({
plugins: [sveltekit(), tailwindcss()],
server: { port: options.port, strictPort: true },
ssr: { noExternal: options.packages || [] },
optimizeDeps: { exclude: options.packages || [] },
});
}
4.3 NIEDRIG: Drizzle Configs (200 LOC)
Problem: 12 Backends haben 90% identische drizzle.config.ts.
Empfehlung: Factory-Funktion
// Vorher (17 LOC pro Backend)
export default defineConfig({
schema: './src/db/schema/index.ts',
out: './src/db/migrations',
dialect: 'postgresql',
dbCredentials: { url: process.env.DATABASE_URL || '...' },
});
// Nachher (5 LOC)
import { createDrizzleConfig } from '@manacore/shared-drizzle-config';
export default createDrizzleConfig('chat');
5. Utility Functions
5.1 NIEDRIG: Logger Utilities (130 LOC)
Problem: 2 Mobile Apps haben eigene Logger:
apps/manadeck/apps/mobile/utils/logger.ts(34 LOC)apps/picture/apps/mobile/utils/logger.ts(92 LOC - erweitert)
Empfehlung: Erstelle @manacore/shared-logger mit:
logger.debug/info/warn/error/successperfLogger.start/endnetworkLogger.request/response/error
5.2 NIEDRIG: Sleep Function Duplikat
Problem: sleep() existiert in:
packages/shared-utils/src/async.ts(8 LOC)packages/shared-api-client/src/utils.ts(3 LOC)
Aktion: Entferne aus shared-api-client, importiere aus shared-utils.
Aktionsplan
Phase 1: Quick Wins (1-2 Tage, ~1.000 LOC)
| Aufgabe | LOC | Aufwand |
|---|---|---|
| Metrics zu shared-nestjs-metrics migrieren (7 Backends) | 350 | Niedrig |
| Picture UI-Komponenten löschen (Button/Input/Card) | 144 | Niedrig |
| AppSlider lokale Kopien entfernen (8 Apps) | 240 | Niedrig |
| Sleep-Duplikat entfernen | 3 | Minimal |
Phase 2: Stores & Configs (3-5 Tage, ~1.500 LOC)
| Aufgabe | LOC | Aufwand |
|---|---|---|
createAppSettingsStore() Factory erstellen |
600 | Mittel |
@manacore/shared-tsconfig Package erstellen |
400 | Niedrig |
@manacore/shared-vite-config Factory erstellen |
300 | Niedrig |
| Navigation Store Factory erstellen | 50 | Niedrig |
Phase 3: Backend Setup (5-7 Tage, ~2.000 LOC)
| Aufgabe | LOC | Aufwand |
|---|---|---|
@manacore/shared-nestjs-setup erstellen |
1.800 | Mittel |
@manacore/shared-nestjs-health erstellen |
170 | Niedrig |
| Drizzle Config Factory erstellen | 200 | Niedrig |
Phase 4: Skeleton Refactoring (Optional, ~800 LOC)
| Aufgabe | LOC | Aufwand |
|---|---|---|
| Page-Level Skeleton Presets erstellen | 400 | Mittel |
| Bestehende Skeletons refactoren | 400 | Mittel |
Zusammenfassung
| Kategorie | Geschätzte Einsparung |
|---|---|
| Backend (NestJS) | 2.300 LOC |
| Frontend Stores | 700 LOC |
| UI Components | 1.200 LOC |
| Konfigurationen | 900 LOC |
| Utilities | 130 LOC |
| Gesamt | ~5.200-6.500 LOC |
Plus Wartungsvorteile:
- Einheitliche Patterns über alle Apps
- Single Point of Change für Updates
- Bessere Onboarding-Erfahrung für neue Entwickler
- Reduzierte Fehlerquellen durch Duplikate