Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 6 Min.

Module, Controller und Services: Die Architektur-Grundlagen von Nest.js

Ähnlich wie ASP.NET Core gliedert Nest.js Anwendungen in Module, Controller und Services. In diesem Artikel sehen wir uns an, wie die einzelnen Bausteine zusammenspielen, wo die Parallelen zu bekannten .NET-Patterns liegen und wo Nest.js eigene Wege geht.
© EMGenie

Das Modul als Organisationseinheit

In ASP.NET Core registriert man Services, Middleware und Konfiguration zentral, früher in der Startup.cs, heute direkt in der Program.cs. Nest.js geht einen Schritt weiter und macht diese Organisation explizit: Jedes fachliche Thema bekommt sein eigenes Modul. Ein Modul bündelt die Controller, Services und Abhängigkeiten, die zu einem bestimmten Feature gehören. Dekoriert wird es mit @Module(), dem Pendant zur Service-Registrierung in .NET. Das hat auch eine gewisse Ähnlichkeit zu Angular.

Ein konkretes Beispiel: Angenommen, wir bauen eine Nutzerverwaltung. In Nest.js sieht das zugehörige Modul so aus:

 

@Module({
   controllers: [UsersController],
   providers: [UsersService],
   exports: [UsersService],
 })
 export class UsersModule {}

 

Das controllers-Array legt fest, welche Controller zu diesem Modul gehören. providers sind die Services, die innerhalb des Moduls per Dependency Injection verfügbar sind. Und exports bestimmt, welche Services auch außerhalb des Moduls genutzt werden dürfen – ein Sichtbarkeitskonzept, das in .NET so nicht existiert, aber sofort einleuchtet: Was nicht explizit exportiert wird, bleibt modulintern. Das erzwingt saubere Grenzen zwischen fachlichen Domänen.

Das AppModule dient dabei als Root-Modul und importiert alle Feature-Module der Anwendung, vergleichbar mit dem Entry Point in einer .NET-Applikation. Der Unterschied: Während in .NET alle Services in einem globalen DI-Container landen, kapselt Nest.js sie modular. Das klingt nach einem kleinen Detail, hat in der Praxis aber spürbare Auswirkungen auf die Wartbarkeit großer Projekte.

Controller: Vertrautes Terrain

Controller in Nest.js sind die direkte Parallele zu ASP.NET-Core-Controllern. Ihre Aufgabe ist identisch: HTTP-Requests entgegennehmen, an die Geschäftslogik delegieren und eine Response zurückgeben. Das Routing funktioniert über Decorators, das Äquivalent zu den Routing-Attributen in .NET.

Sehen wir uns einen typischen CRUD-Controller an:

 

@Controller('users')
 export class UsersController {
   constructor(private usersService: UsersService) {}
   @Get()
   findAll() { return this.usersService.findAll(); }
   @Get(':id')
   findOne(@Param('id') id: string) {
     return this.usersService.findOne(id);
   }
   @Post()
   create(@Body() createUserDto: CreateUserDto) {
     return this.usersService.create(createUserDto);
   }
 }

 

Wer ASP.NET Core kennt, liest das fast wie Pseudocode: @Controller('users') entspricht [Route("api/users")], @Get() ist [HttpGet], @Post() ist [HttpPost], @Param() übernimmt die Rolle von [FromRoute] und @Body() die von [FromBody]. Die Mapping-Tabelle im Kopf füllt sich schnell. Einen wesentlichen Unterschied gibt es allerdings: Nest.js gibt den Rückgabewert einer Controller-Methode automatisch als JSON-Response zurück, ein explizites Ok() oder Json() wie in .NET ist nicht nötig. Weniger Boilerplate, gleiches Ergebnis.

Auch die Validierung folgt einem bekannten Muster. Statt DataAnnotations wie [Required] oder [MaxLength] nutzt Nest.js die Bibliothek class-validator mit Decorators wie @IsNotEmpty() oder @MaxLength(50) auf den DTO-Klassen. In Kombination mit einer globalen ValidationPipe werden ungültige Requests automatisch abgelehnt, genau wie es ModelState in ASP.NET Core umsetzt.

Services: Wo die Geschäftslogik lebt

In .NET ist die Trennung klar: Controller delegieren an Services, Services enthalten die Geschäftslogik. Dasselbe Prinzip gilt in Nest.js. Ein Service wird mit @Injectable() markiert, das Signal an den DI-Container, dass diese Klasse injizierbar ist. Das Pendant in .NET wäre die Registrierung via builder.Services.AddScoped<IUserService, UserService>().

 

@Injectable()
 export class UsersService {
   private users: User[] = [];
   findAll(): User[] { return this.users; }
   findOne(id: string): User { ... }
   create(dto: CreateUserDto): User { ... }
 }

 

Ein feiner Unterschied zur .NET-Welt: In ASP.NET Core arbeitet man typischerweise mit einem Interface (IUserService) und einer Implementierung (UserService). In Nest.js kann man das ebenfalls tun, aber der Standard-Ansatz verzichtet darauf. Der DI-Container arbeitet klassenbasiert, das Token für die Injection ist die Klasse selbst. Das reduziert Boilerplate, erfordert aber ein Umdenken bei der Testbarkeit. Für Unit-Tests bietet Nest.js dafür ein eigenes Testing-Modul, mit dem sich Services und ihre Abhängigkeiten sauber „mocken“ lassen.

Das Zusammenspiel: Vom Request zur Response

Denken wir den Request-Lifecycle einmal durch: Ein HTTP-GET auf /users trifft ein. Nest.js routet den Request anhand der Decorator-Konfiguration an die findAll()-Methode des UsersControllers. Der Controller ruft den UsersService auf, der die Daten liefert. Der Rückgabewert wird automatisch als JSON serialisiert und zurückgegeben. Statuscode 200, fertig.

Der Flow ist identisch mit ASP.NET Core, und das ist kein Zufall, sondern Designentscheidung. Auch das Repository-Pattern lässt sich in Nest.js eins zu eins umsetzen. Mit TypeORM oder Prisma als ORM stehen Werkzeuge zur Verfügung, die in Konzept und Handhabung an Entity Framework erinnern. Der Service ruft das Repository auf, das Repository abstrahiert den Datenbankzugriff. Wer in .NET mit Onion Architecture oder Clean Architecture arbeitet, kann diese Schichtung direkt übertragen.

Erwähnenswert ist auch die Request-Pipeline: Ähnlich wie ASP.NET-Core-Middleware bietet Nest.js Guards, Pipes und Interceptors. Guards übernehmen die Rolle von Authorization-Filtern, Pipes transformieren und validieren Eingabedaten, und Interceptors ermöglichen aspektorientierte Logik wie Logging oder Response-Mapping. Die Pipeline-Reihenfolge ist klar definiert und folgt dem gleichen Prinzip wie die ASP.NET-Core-Middleware-Pipeline, nur deklariert man sie per Decorator statt in der Program.cs.

Clean Architecture: Auch in Nest.js möglich

Nest.js schreibt keine bestimmte Architektur vor, aber es unterstützt die Patterns, die .NET-Entwickler kennen und schätzen. Wer mit Clean Architecture arbeitet, also der Trennung in Entities, Use Cases, Interface Adapters und Frameworks, kann dieses Schichtmodell in Nest.js abbilden. Module fungieren dabei als natürliche Grenze zwischen den Schichten. Feature-Module kapseln fachliche Domänen, Shared-Module stellen quer liegende Services wie Logging oder Konfiguration bereit.

Man könnte argumentieren, dass Nest.js durch sein Modulsystem sogar expliziter zur Schichttrennung zwingt als .NET, wo die Grenze zwischen Schichten oft nur konventionell über Projekt-Referenzen abgebildet wird. In Nest.js ist die Sichtbarkeit von Services architektonisch verankert: Nur was exportiert wird, ist verfügbar. Das ist ein kleines, aber wirksames Sicherheitsnetz gegen architektonische Erosion.

In der Praxis hat sich eine Ordnerstruktur bewährt, die jedes Feature-Modul in ein eigenes Verzeichnis mit Controller, Service, DTOs und gegebenenfalls Repository-Dateien kapselt. Anders als in .NET, wo Solution-Projekte als physische Trennungseinheit dienen, nutzt Nest.js Verzeichnisse und Module als logische Grenzen. Der CLI-Befehl nest generate resource erzeugt dabei ein komplettes Feature-Modul mit allen Dateien, vergleichbar mit dem dotnet new controller-Scaffolding, aber umfassender.

Bekannte Bausteine, neue Möglichkeiten

Wie man sehen kann: Nest.js verwendet keine neuartigen Konzepte, vielmehr sind es die vertrauten Bausteine aus der .NET-Welt, ausgedrückt in TypeScript-Syntax. Das mentale Mapping ist schnell erstellt: Modul entspricht Service-Registrierung, Controller bleibt Controller, Service bleibt Service. Die Unterschiede liegen im Detail: modulare Sichtbarkeit statt globalem DI-Container, klassenbasierte Tokens statt Interface-basierter Registrierung, automatische JSON-Serialisierung statt expliziter ActionResults.

Im nächsten Teil der Serie vertiefen wir genau diesen letzten Punkt: Dependency Injection in Nest.js. Wir zeigen, welche Scopes es gibt, wie Custom Providers funktionieren und wie sich das Pattern für testbare, wartbare Architekturen nutzen lässt, mit dem gewohnten Vergleich zu ASP.NET Core.

 

Neueste Beiträge

Was Developer in Europa wirklich wollen – und was sie nervt - European Transparent IT Job Market Report
Über 23.000 Stellenanzeigen, mehr als 1.300 befragte IT-Fachleute, sechs europäische Länder: Der Job-Market-Report liefert handfeste Zahlen zu Gehältern, Recruiting-Frust und dem wachsenden Einfluss von KI auf den Arbeitsalltag. Was Developer wirklich wollen – und wo Unternehmen noch deutlich Luft nach oben haben.
3 Minuten
19. Mai 2026
Security ist essenziell - Secure Boot: Sicherheit von Anfang an
Die beste Verschlüsselung wird ausgehebelt, wenn das Betriebssystem schon beim Bootvorgang kompromittiert wird.
8 Minuten
25. Mai 2026
HMAC mit C# und T-SQL - Neues in SQL Server 2025, Teil 3
Kompatible Signaturberechnung über Systemgrenzen hinweg.
4 Minuten
20. Mai 2026

Das könnte Dich auch interessieren

00:00
C# 14, Blazor und die Desktop-Frage - Was sind die Killer Features der aktuellen Versionen?
C# 14 bringt echte Verbesserungen für den Entwickleralltag – aber nicht jedes neue Feature ist ein Game Changer. Microsoft MVP Thomas-Claudius Huber sortiert, was in der Praxis zählt, erklärt, wann Blazor React schlägt, und warum WPF noch lange nicht zum alten Eisen gehört.
19. Mai 2026
00:00
Bluetooth, Biometrie und Multiplattform: Was .NET MAUI Hybrid wirklich kann - Die Möglichkeiten von .NET MAUI Blazor Hybrid verstehen
Codrina Merigo baut mit .NET MAUI Blazor Hybrid Apps für Android, iOS, macOS und Windows – und nutzt dafür das, was Web-Entwickler:innen sowieso schon können. Im Interview im Vorfeld der DWX 2026 erklärt sie, wie das geht, wo's hakt und warum das Framework im Enterprise-Umfeld eine ernste Option ist.
28. Apr 2026
Vom Python-Modell zur .NET-Anwendung - .NET, Python und KI, Teil 4
Am Szenario einer Sentiment-Analyse verdeutlicht ein durchgängiges Anwendungsbeispiel, wie aus einem isolierten Data-Science-Ergebnis eine konkret genutzte Funktion innerhalb einer .NET-Business-Anwendung entsteht.
7 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige