Termin buchen
Best Practices

Clean Architecture in Node.js: Skalierbare Backends

Sohib Falmz··6 Min. Lesezeit
Clean Architecture in Node.js: Skalierbare Backends

Warum Clean Architecture in Node.js-Projekten unverzichtbar ist

Node.js hat sich in den letzten Jahren zu einer der führenden Technologien für skalierbare Backend-Systeme entwickelt. Doch mit wachsender Projektkomplexität zeigen sich schnell die Schwächen klassischer MVC-Strukturen: Geschäftslogik vermischt sich mit Datenbankabfragen, HTTP-Controller enthalten Validierungen und Services werden zu unübersichtlichen Monolithen. Hier setzt Clean Architecture nach Robert C. Martin an – ein Architekturmuster, das Ihre Softwareentwicklung langfristig wartbar, testbar und unabhängig von externen Frameworks macht.

In diesem Guide zeigen wir Ihnen als erfahrene Spezialisten für individuelle Softwareentwicklung Made in Germany, wie Sie Clean Architecture in Node.js-Projekten mit TypeScript pragmatisch umsetzen. Wir orientieren uns dabei an realen Enterprise-Projekten, in denen wir CTOs und IT-Leiter bei der Modernisierung ihrer Backend-Systeme begleiten.

Die vier Schichten der Clean Architecture im Überblick

Clean Architecture basiert auf dem Prinzip konzentrischer Kreise, die jeweils unterschiedliche Verantwortlichkeiten kapseln. Die Abhängigkeiten zeigen dabei stets nach innen – eine äußere Schicht kennt innere Schichten, aber niemals umgekehrt (Dependency Rule).

1. Entities (Domain Layer)

Im Zentrum stehen die Entities – reine Geschäftsobjekte mit Geschäftsregeln, die unabhängig von jeder Technologie funktionieren. Sie kennen weder Express noch PostgreSQL, weder React noch AWS.

2. Use Cases (Application Layer)

Die Use Cases orchestrieren die Entities und implementieren anwendungsspezifische Geschäftslogik. Ein typischer Use Case wäre „RegisterCustomer" oder „ProcessPayment". Sie definieren Ports (Interfaces), über die Infrastruktur-Komponenten später eingebunden werden.

3. Interface Adapters

Hier finden sich Controller, Presenter und Gateways, die zwischen der inneren Welt (Use Cases) und der äußeren Welt (HTTP, Datenbank, Message Broker) übersetzen.

4. Frameworks & Drivers

Die äußerste Schicht enthält Express.js, Fastify, Prisma, TypeORM, Kafka-Clients und andere Infrastruktur-Details. Diese sollten austauschbar sein.

Projektstruktur: So organisieren Sie Ihren Node.js-Code

Eine bewährte Ordnerstruktur für ein Clean-Architecture-Projekt mit TypeScript sieht folgendermaßen aus:

src/
├── domain/
│   ├── entities/
│   │   └── Customer.ts
│   └── value-objects/
│       └── Email.ts
├── application/
│   ├── use-cases/
│   │   └── RegisterCustomer.ts
│   └── ports/
│       ├── CustomerRepository.ts
│       └── EmailService.ts
├── infrastructure/
│   ├── persistence/
│   │   └── PrismaCustomerRepository.ts
│   ├── email/
│   │   └── SendGridEmailService.ts
│   └── http/
│       └── express/
│           └── CustomerController.ts
└── main/
    ├── config/
    └── server.ts

Diese Struktur spiegelt die Dependency Rule unmittelbar wider: Code in domain/ importiert niemals aus infrastructure/, und application/ kennt ausschließlich abstrakte Ports.

Entities: Geschäftslogik ohne Framework-Kopplung

Eine Entity in TypeScript kapselt Zustand und Verhalten der Domäne. Sie ist frei von ORM-Decorators oder HTTP-Annotationen:

export class Customer {
  private constructor(
    public readonly id: string,
    public readonly email: Email,
    private _isActive: boolean
  ) {}

  static create(id: string, email: Email): Customer {
    return new Customer(id, email, true);
  }

  deactivate(): void {
    if (!this._isActive) {
      throw new Error("Customer already inactive");
    }
    this._isActive = false;
  }

  get isActive(): boolean {
    return this._isActive;
  }
}

Beachten Sie: Keine Abhängigkeit zu Prisma, TypeORM oder Express. Die Entity lässt sich in einem einfachen Jest-Unit-Test ohne Mocks validieren.

Use Cases: Die Anwendungslogik im Kern

Ein Use Case definiert einen einzelnen Anwendungsfall und folgt dem Single Responsibility Principle. Er erhält seine Abhängigkeiten via Dependency Injection:

export interface CustomerRepository {
  save(customer: Customer): Promise<void>;
  findByEmail(email: Email): Promise<Customer | null>;
}

export class RegisterCustomerUseCase {
  constructor(
    private readonly customerRepo: CustomerRepository,
    private readonly emailService: EmailService
  ) {}

  async execute(input: RegisterInput): Promise<Customer> {
    const email = Email.create(input.email);
    const existing = await this.customerRepo.findByEmail(email);
    if (existing) {
      throw new CustomerAlreadyExistsError();
    }
    const customer = Customer.create(crypto.randomUUID(), email);
    await this.customerRepo.save(customer);
    await this.emailService.sendWelcome(customer);
    return customer;
  }
}

Der Use Case kennt nur das CustomerRepository-Interface – ob dahinter PostgreSQL, MongoDB oder ein In-Memory-Store steht, ist für die Geschäftslogik irrelevant.

Best Practices für die Umsetzung in Node.js

Dependency Injection ohne schweres Framework

In Node.js benötigen Sie kein NestJS oder InversifyJS für saubere DI. Eine einfache Composition Root in main/server.ts genügt oft:

  • Instanziieren Sie Infrastruktur-Implementierungen einmalig
  • Injizieren Sie diese in Use Cases
  • Registrieren Sie Controller beim Express-Router

Für größere Projekte empfehlen wir leichtgewichtige Container wie tsyringe oder awilix.

Value Objects für Validierung und Typsicherheit

Primitive Obsession ist einer der häufigsten Anti-Patterns. Statt überall mit string zu arbeiten, kapseln Value Objects Validierungslogik:

export class Email {
  private constructor(public readonly value: string) {}

  static create(raw: string): Email {
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(raw)) {
      throw new InvalidEmailError(raw);
    }
    return new Email(raw.toLowerCase());
  }
}

Error Handling über Result Types oder Domain Errors

Werfen Sie in Use Cases typisierte Domain Errors (CustomerAlreadyExistsError) statt generischer Error-Instanzen. Im Controller übersetzen Sie diese in HTTP-Statuscodes – so bleibt die Domain frei von HTTP-Wissen.

Testbarkeit als oberste Priorität

  • Unit-Tests für Entities und Use Cases – schnell, ohne I/O
  • Integrationstests für Repositories mit Testcontainers (echte PostgreSQL-Instanz in Docker)
  • End-to-End-Tests nur für kritische Pfade

Durch die strikte Trennung erreichen Sie in unseren Projekten regelmäßig eine Testabdeckung von über 85 % bei der Geschäftslogik – ohne dabei Infrastrukturcode mocken zu müssen.

Integration mit Express, Fastify und modernen Frameworks

Der Controller ist ein dünner Adapter, der HTTP-Requests in Use-Case-Inputs übersetzt:

export class CustomerController {
  constructor(private readonly registerUseCase: RegisterCustomerUseCase) {}

  async register(req: Request, res: Response): Promise<void> {
    try {
      const customer = await this.registerUseCase.execute(req.body);
      res.status(201).json({ id: customer.id });
    } catch (err) {
      if (err instanceof CustomerAlreadyExistsError) {
        res.status(409).json({ error: err.message });
        return;
      }
      res.status(500).json({ error: "Internal Server Error" });
    }
  }
}

Der Controller enthält keine Geschäftslogik – nur Request-Parsing und Response-Mapping. Bei einem Wechsel von Express zu Fastify tauschen Sie ausschließlich diese Adapter-Schicht aus.

DSGVO und Clean Architecture: Eine natürliche Symbiose

Die strikte Trennung erleichtert DSGVO-Konformität erheblich. Use Cases wie „DeleteUserData" oder „ExportUserData" kapseln Löschung und Datenauskunft zentral. Audit-Logging lässt sich als Decorator um Use Cases legen, ohne die Geschäftslogik zu verunreinigen. Für deutsche Unternehmen, die regelmäßig mit Auftragsverarbeitungsverträgen (AVV) und Compliance-Audits konfrontiert sind, ist diese Nachvollziehbarkeit ein klarer Wettbewerbsvorteil.

Typische Fallstricke und wie Sie sie vermeiden

  • Anemic Domain Models: Entities ohne Verhalten sind reine Datencontainer – verlagern Sie Geschäftsregeln zurück in die Domain.
  • Leaky Abstractions: Wenn das Repository-Interface Prisma-Typen zurückgibt, ist die Abstraktion wertlos.
  • Over-Engineering: Nicht jedes Projekt braucht vier Schichten. Für Prototypen oder kleine Services genügen oft drei.
  • Zirkuläre Abhängigkeiten: Werkzeuge wie madge oder dependency-cruiser helfen, die Dependency Rule automatisiert zu prüfen.

Wann lohnt sich Clean Architecture?

Clean Architecture ist kein Selbstzweck. Sie rentiert sich besonders in folgenden Szenarien:

  • Langfristige Enterprise-Projekte mit mehrjähriger Laufzeit
  • Teams mit mehr als drei bis fünf Entwicklern
  • Hohe regulatorische Anforderungen (DSGVO, NIS2, Finanzbranche)
  • Geplante Modernisierung bestehender Legacy-Systeme
  • Microservices mit klaren Bounded Contexts

Für ein schnelles MVP oder eine kleine interne API ist ein schlankerer Ansatz häufig pragmatischer – Sie können jederzeit iterativ in Richtung Clean Architecture refaktorieren.

Fazit: Investition in wartbare Backend-Systeme

Clean Architecture in Node.js ist keine akademische Übung, sondern eine strategische Investition in die Wartbarkeit Ihrer Software. Durch die konsequente Trennung von Geschäftslogik und Infrastruktur schaffen Sie Codebasen, die Technologiewechsel überdauern, Audits bestehen und neue Teammitglieder schnell produktiv machen.

Als Spezialisten für individuelle Softwareentwicklung mit TypeScript, Node.js und Cloud-Native-Architekturen begleiten wir Sie von der ersten Architektur-Entscheidung bis zur produktiven Bereitstellung auf AWS oder Kubernetes. Lassen Sie uns gemeinsam die nächste Generation Ihrer Backend-Systeme gestalten – skalierbar, DSGVO-konform und Made in Germany.

Tipp für Sie

Möchten Sie diese Strategien in Ihrem Unternehmen umsetzen?

15-Minuten-Gespräch mit einem Experten. Kostenlos und unverbindlich.

Termin wählen

Weitere Beiträge

Unsere Partner & Technologie

Meta

Meta

Official Partner

Twilio

Official Partner

WhatsApp

WhatsApp Business

API Integration

OpenAI

OpenAI

KI-Technologie

Vercel

Vercel

Hosting Platform

Next.js

Next.js

Web-Framework

AWS Frankfurt

eu-central-1

Hetzner

Hetzner

Cloud Infrastructure

Cloudflare

Cloudflare

DNS & WAF

DSGVO-konform

Made in Germany

Entwickelt & gehostet in DE

Claude

Claude

KI-Assistent

EU-Server

Hosting in der EU

Meta

Meta

Official Partner

Twilio

Official Partner

WhatsApp

WhatsApp Business

API Integration

OpenAI

OpenAI

KI-Technologie

Vercel

Vercel

Hosting Platform

Next.js

Next.js

Web-Framework

AWS Frankfurt

eu-central-1

Hetzner

Hetzner

Cloud Infrastructure

Cloudflare

Cloudflare

DNS & WAF

DSGVO-konform

Made in Germany

Entwickelt & gehostet in DE

Claude

Claude

KI-Assistent

EU-Server

Hosting in der EU

Clean Architecture in Node.js: Skalierbare Backends | Inno Softwareentwicklung