Modulare SaaS-Architektur mit TypeScript und Prisma

Warum modulare Software-Architektur ab Tag eins entscheidet, ob ein SaaS-Projekt skaliert

Wenn man eine All-in-One-Plattform für eine bestimmte Branche baut – wie etwa eine gastro-orientierte SaaS-Lösung mit Menü-Managern, Live-Bestellkanälen und dynamischen Admin-Oberflächen –, droht der Code extrem schnell zu einem unübersichtlichen Monolithen zu verschwimmen. Wer hier unsauber strukturiert, zahlt bei jedem neuen Feature doppelt: Jede Anpassung reißt Seiteneffekte in Modulen auf, die eigentlich unabhängig voneinander laufen sollten.

Kunden wollen Flexibilität. Sie buchen Module einzeln per Subscription oder kaufen sie als feste Komponente. Entsprechend dürfen Datenbankschemas, API-Routes und UI-Komponenten nicht hardcodet miteinander verknüpft sein.

Saubere Datenmodelle mit Prisma ORM

Ein modularer Ansatz bricht meistens direkt beim Datenbankschema zusammen. Mit Prisma lässt sich das über relationale Schemas und klar getrennte Entitäten abbilden, statt alles in eine gigantische Tabelle zu pressen:

model Tenant {
  id           String        @id @default(uuid())
  name         String
  subscriptions TenantModule[]
  menus        Menu[]
  createdAt    DateTime      @default(now())
}

model ModuleDefinition {
  id          String         @id @default(uuid())
  code        String         @unique // z.B. "menu_manager" oder "pos_integration"
  name        String
  tenants     TenantModule[]
}

model TenantModule {
  tenantId           String
  moduleDefinitionId String
  isEnabled          Boolean  @default(false)
  
  tenant             Tenant   @relation(fields: [tenantId], references: [id])
  moduleDefinition   ModuleDefinition @relation(fields: [moduleDefinitionId], references: [id])

  @@id([tenantId, moduleDefinitionId])
}

Über dieses Schema weiß das Backend zu jedem Zeitpunkt exakt, welcher Mandant welches Modul freigeschaltet hat. Die Berechtigungsprüfung greift direkt vor dem Ausführen der API-Endpunkte.

Feature-basierte Ordnerstruktur in Next.js

Statt Code nach technischem Typ (alle Komponenten in einen Ordner, alle Actions in einen anderen) zu sortieren, trennt eine feature-basierte Struktur die Logik nach Anwendungsbereichen:

/src
  /modules
    /menu
      /components    # UI-Komponenten nur für den Menü-Manager
      /hooks         # Custom Hooks für Menü-Daten
      /server        # Prisma-Abfragen und Server-Actions für das Menü
    /pos
      /components
      /server
  /shared            # Globale UI-Komponenten und Utilities

Jedes Modul bringt seine eigene Server-Logik und seine eigenen Komponenten mit. Wenn ein Modul Daten eines anderen benötigt, läuft das über definierte interne Schnittstellen – nicht über direkte SQL-Abfragen in fremden Tabellen.

Fazit

Modularität kostet am Anfang mehr Disziplin und saubere Planung. Aber wenn das System wächst, lassen sich Features wie passgenaue Zahnräder einsetzen, ohne dass das Fundament ins Wanken gerät.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Nach oben scrollen