Full-Stack-Entwicklungsfähigkeit (UI & Backend)
Diese Fähigkeit bietet einen durchgängigen architektonischen Leitfaden, einen Arbeitsablauf und Codierungsstandards für die Entwicklung von produktionsreifen Webanwendungen mit modernem TypeScript/JavaScript sowohl auf dem Frontend (UI) als auch auf dem Backend.
1. Grundlegende Architekturprinzipien
- Durchgängige Typsicherheit:
- Einzige Quelle der Wahrheit für Domänenmodelle und Datenübertragungsobjekte (DTOs).
- Verwenden Sie Schema-Validierungsbibliotheken (wie Zod), um Anfragen zur Laufzeit zu parsen und zu validieren und TypeScript-Typen zur Kompilierzeit abzuleiten.
- Schichtenbasierte Trennung von Belangen:
- Frontend: Strikte Trennung von darstellenden Primitiven, Geschäfts-/Container-Komponenten und Data-Fetching-Hooks.
- Backend: Strikte Trennung zwischen Routing/Controllern (HTTP-Transport), Service-Schicht (Geschäftslogik) und Datenzugriffs-/Repository-Schicht (Datenbank/ORM).
- API-Contract First:
- Legen Sie Anfrage- und Antwortverträge fest, bevor Sie UI- oder Backend-Details implementieren.
- Einheitlicher Antwort-Envelope:
{ success: true, data: T }oder{ success: false, error: { message, code, details } }.
- Resilienz und Fehlerbehandlung:
- Frontend: Fehlergrenzen, Skelett-Ladezustände, anmutige Fallbacks und benutzerfreundliche Benachrichtigungen.
- Backend: Zentrale Fehler-Middleware, Kategorisierung von Betriebs- vs. Programmierfehlern, strukturiertes Logging und sichere Standardeinstellungen.
- Sicherheit standardmäßig:
- Sicherheits-Header (Helmet), explizite CORS-Richtlinien, Eingabevalidierung/-bereinigung, Ratenbegrenzung und sichere Authentifizierung (HTTP-only-Cookies oder Bearer-Tokens).
2. Empfohlene Verzeichnisstruktur
Für Full-Stack-Repositories (Monorepo oder geteilte Struktur):
├── packages/ or apps/
│ ├── client/ # Frontend UI (React / Next.js / Vite)
│ │ ├── src/
│ │ │ ├── assets/ # Statische Assets (Bilder, Icons, Schriftarten)
│ │ │ ├── components/ # UI-Komponenten (Atome, Moleküle, Organismen)
│ │ │ │ ├── ui/ # Kern-Designsystem-Primitive (Button, Input, Modal)
│ │ │ │ └── features/ # Domänenspezifische zusammengesetzte Komponenten
│ │ │ ├── hooks/ # Benutzerdefinierte React-Hooks (useAuth, useDebounce, etc.)
│ │ │ ├── services/ # API-Client und Service-Integrationen
│ │ │ ├── store/ # Globales State Management (Zustand, Context, etc.)
│ │ │ ├── types/ # Client-spezifische Typen
│ │ │ ├── utils/ # Hilfsfunktionen und Formatierungsdienstprogramme
│ │ │ ├── App.tsx # Hauptanwendungskomponente & Routen
│ │ │ ├── main.tsx # Einstiegspunkt
│ │ │ └── index.css # Globales CSS & Design-Tokens
│ │ └── package.json
│ │
│ ├── server/ # Backend API (Node.js / Express / Fastify)
│ │ ├── src/
│ │ │ ├── config/ # Umgebungsvariablen & Laufzeitkonfigurationen
│ │ │ ├── controllers/ # HTTP-Routen-Handler (Parameter extrahieren, Services aufrufen)
│ │ │ ├── middleware/ # Auth, Validierung, Fehlerbehandlung, Ratenbegrenzung
│ │ │ ├── models/ # Datenbankmodelle / Schemata (Prisma, Drizzle, etc.)
│ │ │ ├── routes/ # Express/Fastify-Routendefinitionen
│ │ │ ├── services/ # Geschäftslogik & externe API-Interaktionen
│ │ │ ├── utils/ # Backend-Helfer (jwt, logger, crypto)
│ │ │ ├── app.ts # Express/Fastify-App-Konfiguration
│ │ │ └── server.ts # Server-Einstiegspunkt & Graceful Shutdown
│ │ └── package.json
│ │
│ └── shared/ # Gemeinsame Schemata, DTOs & TypeScript-Verträge
│ ├── src/
│ │ ├── schemas/ # Zod-Schemata (user.schema.ts, auth.schema.ts)
│ │ └── types/ # Abgeleitete Typen (export type User = z.infer<...>)
│ └── package.json
3. Schritt-für-Schritt-Entwicklungsworkflow
Wenn Sie eine Full-Stack-Funktion implementieren, führen Sie die folgenden Phasen in der angegebenen Reihenfolge aus:
Phase 1: Gemeinsame Verträge & Schemata definieren
- Definieren Sie das Zod-Schema für Anfrage-Payloads und Antwort-Entitäten in
shared/odercommon/. - Exportieren Sie die abgeleiteten TypeScript-Typen.
import { z } from 'zod';
export const CreateUserSchema = z.object({
email: z.string().email('Ungültige E-Mail-Adresse'),
name: z.string().min(2, 'Der Name muss mindestens 2 Zeichen lang sein'),
role: z.enum(['admin', 'user']).default('user'),
});
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
export type UserResponse = CreateUserInput & { id: string; createdAt: string };
Phase 2: Backend-Service & Endpunkte erstellen
- Validierungs-Middleware: Wenden Sie eine Validierungs-Middleware mit dem Zod-Schema an, um fehlerhafte Eingaben frühzeitig mit HTTP 400 abzulehnen.
- Service-Schicht: Schreiben Sie reine Geschäftslogik in einer Service-Klasse oder einem Modul. Isolieren Sie Datenbankabfragen von HTTP-Konzepten (
req,res). - Controller: Kümmern Sie sich um die Extraktion von HTTP-Eingaben, rufen Sie den Service auf und geben Sie standardisierte Antworten mit
sendSuccess()zurück. - Fehlerbehandlung: Verwenden Sie benutzerdefinierte Fehler (
NotFoundError,BadRequestError,UnauthorizedError) und lassen Sie die zentrale Fehler-Middleware die Antwort abfangen und formatieren.
Phase 3: Frontend-UI-Komponenten erstellen
- Design-System-Tokens: Stellen Sie sicher, dass CSS-Custom-Properties oder Design-System-Variablen für Farben, Typografie, Abstände und Schatten existieren.
- Atomare / UI-Primitive: Erstellen Sie Basis-UI-Komponenten (Buttons, Eingabefelder, Badges, Dialoge) oder verwenden Sie diese wieder – mit Tastaturnavigation und ARIA-Attributen.
- Feature-Ansicht / Seite: Setzen Sie zusammengesetzte Komponenten mit sauberer visueller Hierarchie, großzügigen Abständen, moderner Typografie und responsiven Grid-/Flexbox-Layouts zusammen.
- Zustand & Mikro-Interaktionen:
- Bieten Sie visuelles Feedback für Hover-, Active-, Focus-Visible- und Disabled-Zustände.
- Fügen Sie Lade-Skelette, Spinner-Indikatoren und deaktivierte Buttons während laufender Übermittlungen hinzu.
Phase 4: Integration & Datenabruf
- API-Client: Verwenden Sie einen zentralisierten Fetch-Wrapper oder eine Axios-Instanz, die mit Basis-URL, Timeout, Anmeldeinformationen und Interceptors konfiguriert ist.
- Server-State: Verwenden Sie TanStack Query (React Query) oder Standard-Hooks zum Abfragen und Mutieren von Daten.
- Cache-Invalidierung bei erfolgreichen Mutationen.
- Optimistische Updates für eine flüssige Benutzererfahrung.
- Formular-Handling: Integrieren Sie Formulare mit React Hook Form + Zod-Resolver, um exakt dieselben Validierungsregeln zwischen Client und Server zu teilen.
Phase 5: Verifikation & Härtung
- Backend-Verifikation: Testen Sie Endpunkte mit Unit-/Integrationstests oder curl/fetch, um Randfälle zu überprüfen (ungültige Eingaben, fehlende Tokens, doppelte Schlüssel).
- Frontend-Verifikation: Überprüfen Sie die Reaktionsfähigkeit auf Desktop-, Tablet- und mobilen Bildschirmbreiten.
- Sicherheitscheck: Bestätigen Sie, dass CORS, Ratenbegrenzung und Authentifizierungsschutz aktiv sind.
4. Schnelle Code-Muster
Standard-API-Antwort-Helfer (Backend)
// server/src/utils/response.ts
import { Response } from 'express';
export function sendSuccess<T>(res: Response, data: T, statusCode = 200, meta?: unknown) {
return res.status(statusCode).json({
success: true,
data,
...(meta ? { meta } : {}),
});
}
export function sendError(res: Response, message: string, statusCode = 500, details?: unknown) {
return res.status(statusCode).json({
success: false,
error: {
message,
statusCode,
details: details ?? null,
},
});
}
Zentrale Express-Fehler-Middleware
// server/src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';
import { ZodError } from 'zod';
export class AppError extends Error {
constructor(public message: string, public statusCode = 500, public details?: unknown) {
super(message);
this.name = 'AppError';
}
}
export function errorHandler(err: unknown, req: Request, res: Response, next: NextFunction) {
if (err instanceof ZodError) {
return res.status(400).json({
success: false,
error: {
message: 'Validierung fehlgeschlagen',
statusCode: 400,
details: err.flatten().fieldErrors,
},
});
}
if (err instanceof AppError) {
return res.status(err.statusCode).json({
success: false,
error: {
message: err.message,
statusCode: err.statusCode,
details: err.details ?? null,
},
});
}
console.error('Unbehandelter Serverfehler:', err);
return res.status(500).json({
success: false,
error: {
message: 'Interner Serverfehler',
statusCode: 500,
},
});
}
Typsicherer API-Client (Frontend)
// client/src/services/apiClient.ts
interface ApiResponse<T> {
success: boolean;
data: T;
error?: {
message: string;
statusCode: number;
details?: unknown;
};
}
export async function apiRequest<T>(url: string, options?: RequestInit): Promise<T> {
const res = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
...options,
});
const body: ApiResponse<T> = await res.json();
if (!res.ok || !body.success) {
throw new Error(body.error?.message || `HTTP ${res.status}: Anfrage fehlgeschlagen`);
}
return body.data;
}
5. Referenzen und vertiefende Themen
Für tiefere architektonische Muster konsultieren Sie: