# Backend-Architektur ## Übersicht Das Backend folgt einer modernen, klassenbasierten Architektur mit klarer Trennung der Verantwortlichkeiten (Separation of Concerns). ## Architektur-Schichten ``` ┌─────────────────────────────────────┐ │ Routes (Express) │ ← HTTP-Endpunkte definieren ├─────────────────────────────────────┤ │ Controller-Klassen │ ← HTTP Request/Response Handling ├─────────────────────────────────────┤ │ Service-Klassen │ ← Business-Logik ├─────────────────────────────────────┤ │ Models │ ← Datenmodelle & Validierung ├─────────────────────────────────────┤ │ Datenspeicher │ ← In-Memory / Datenbank └─────────────────────────────────────┘ ``` ## Komponenten ### 1. Routes (`src/routes/`) **Verantwortlichkeit:** Definition der HTTP-Endpunkte ```javascript // src/routes/timeEntries.js router.get('/', timeEntryController.getAllEntries.bind(timeEntryController)); ``` - Definiert URL-Pfade und HTTP-Methoden - Bindet Endpunkte an Controller-Methoden - Keine Business-Logik ### 2. Controller (`src/controllers/`) **Verantwortlichkeit:** HTTP Request/Response Handling ```javascript // src/controllers/TimeEntryController.js class TimeEntryController { async getAllEntries(req, res) { try { const entries = timeEntryService.getAllEntries(); res.json(entries); } catch (error) { res.status(500).json({ error: 'Fehler...' }); } } } ``` **Controller sind zuständig für:** - ✅ Request-Parameter extrahieren - ✅ Service-Methoden aufrufen - ✅ HTTP-Statuscodes setzen - ✅ Response formatieren - ✅ Error-Handling (HTTP-spezifisch) **Controller sind NICHT zuständig für:** - ❌ Business-Logik - ❌ Datenvalidierung (außer HTTP-Parameter) - ❌ Datenbankzugriff - ❌ Komplexe Berechnungen ### 3. Services (`src/services/`) **Verantwortlichkeit:** Business-Logik ```javascript // src/services/TimeEntryService.js class TimeEntryService { createEntry(entryData) { // Validierung const runningEntry = this.timeEntries.find(e => e.isRunning); if (runningEntry) { throw new Error('Es läuft bereits ein Timer...'); } // Business-Logik const newEntry = new TimeEntry({ ... }); newEntry.validate(); // Speichern this.timeEntries.push(newEntry); return newEntry; } } ``` **Services sind zuständig für:** - ✅ Komplette Business-Logik - ✅ Datenvalidierung - ✅ Datenzugriff - ✅ Berechnungen - ✅ Business-Rules - ✅ Transaktionen **Services sind NICHT zuständig für:** - ❌ HTTP-spezifische Logik - ❌ Response-Formatierung - ❌ HTTP-Statuscodes ### 4. Models (`src/models/`) **Verantwortlichkeit:** Datenmodelle und Validierung ```javascript // src/models/TimeEntry.js class TimeEntry { constructor(data) { ... } validate() { if (!this.startTime) { throw new Error('Startzeit ist erforderlich'); } } calculateDuration() { ... } getFormattedDuration() { ... } } ``` **Models sind zuständig für:** - ✅ Datenstruktur definieren - ✅ Datenvalidierung - ✅ Hilfsmethoden für Daten - ✅ Formatierung ## Vorteile dieser Architektur ### 1. **Separation of Concerns** Jede Schicht hat eine klar definierte Verantwortlichkeit: - Routes → Routing - Controller → HTTP-Handling - Services → Business-Logik - Models → Datenmodelle ### 2. **Testbarkeit** ```javascript // Services können isoliert getestet werden const service = new TimeEntryService(); const entry = service.createEntry({ project: 'Test' }); ``` ### 3. **Wiederverwendbarkeit** Services können von verschiedenen Controllern oder anderen Services verwendet werden. ### 4. **Wartbarkeit** Änderungen an der Business-Logik betreffen nur Services, nicht Controller oder Routes. ### 5. **Skalierbarkeit** - Services können einfach auf Microservices aufgeteilt werden - Einfache Integration von Datenbanken - Caching-Schichten hinzufügen ## Datenfluss ### Beispiel: Neuen Zeiteintrag erstellen ``` 1. HTTP POST Request ↓ 2. Route: POST /api/time-entries ↓ 3. Controller.createEntry() - Extrahiert req.body - Ruft Service auf ↓ 4. Service.createEntry() - Validiert Daten - Prüft Business-Rules - Erstellt Model - Speichert Daten ↓ 5. Controller.createEntry() - Setzt Status 201 - Sendet JSON Response ↓ 6. HTTP Response ``` ## Singleton-Pattern Sowohl Controller als auch Services werden als Singleton exportiert: ```javascript // Am Ende der Datei module.exports = new TimeEntryService(); ``` **Vorteile:** - Gleicher Datenspeicher über alle Requests - Keine Instanziierung bei jedem Request - Shared State (für In-Memory Storage) **Hinweis:** Bei Datenbank-Integration kann auf Singleton verzichtet werden. ## Erweiterte Features ### Service-Features Die `TimeEntryService`-Klasse bietet erweiterte Funktionen: ```javascript // Statistiken mit Zeitfilter getStatistics() → { totalEntries, projectStats, timeStats: { today, week, month } } // Laufenden Timer abrufen getRunningEntry() // Nach Projekt filtern getEntriesByProject(projectName) // Datumsbereich filtern getEntriesByDateRange(startDate, endDate) ``` ### Controller-Features Der `TimeEntryController` bietet zusätzliche Endpunkte: ```javascript // GET /api/time-entries/running getRunningEntry() // GET /api/time-entries/project/:projectName getEntriesByProject() ``` ## Best Practices ### 1. Error Handling **In Services:** ```javascript throw new Error('Beschreibende Fehlermeldung'); ``` **In Controllern:** ```javascript catch (error) { if (error.message.includes('nicht gefunden')) { return res.status(404).json({ error: '...' }); } res.status(500).json({ error: '...' }); } ``` ### 2. Async/Await Controller-Methoden sind `async`, auch wenn Services aktuell synchron sind: ```javascript async getAllEntries(req, res) { // Vorbereitet für asynchrone Service-Calls (z.B. Datenbank) } ``` ### 3. Binding Bei Klassen-Methoden als Express-Handler muss `bind()` verwendet werden: ```javascript router.get('/', controller.method.bind(controller)); ``` ## Migration auf Datenbank Die Architektur ist vorbereitet für Datenbank-Integration: ```javascript class TimeEntryService { async createEntry(entryData) { // Statt In-Memory: // return await TimeEntryModel.create(entryData); } } ``` Nur die Service-Schicht muss angepasst werden, Controller bleiben unverändert! ## Zusammenfassung | Schicht | Datei | Verantwortlichkeit | Beispiel | |---------|-------|-------------------|----------| | **Routes** | `timeEntries.js` | URL-Mapping | `router.get('/', ...)` | | **Controller** | `TimeEntryController.js` | HTTP-Handling | `res.json(data)` | | **Service** | `TimeEntryService.js` | Business-Logik | `validateData()` | | **Model** | `TimeEntry.js` | Datenmodell | `validate()` | Diese Architektur ermöglicht eine professionelle, wartbare und skalierbare Backend-Anwendung! 🚀