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

Die ganze Welt der Softwareentwicklung
Ein riesiges Angebot an Wissen, das von Expert:innen lebendig vermittelt wird, gewürzt mit Kontakt zu Gleichdenkenden – das ist der Kern der DWX.
6 Minuten
19. Feb 2026
Nest.js: Warum sich der Blick über den Tellerrand für .NET-Entwickler lohnt - Nest.js für .NET-Entwickler, Teil 1
In modernen Softwareprojekten steht das C#-Backend längst neben einem Frontend, das in TypeScript lebt. Zwei Sprachen, zwei Ökosysteme, zwei Denkweisen. Was, wenn das Backend dieselbe Sprache sprechen könnte – ohne auf Enterprise-Patterns zu verzichten? Nest.js zeigt, dass genau das geht.
6 Minuten
18. Feb 2026

Das könnte Dich auch interessieren

Version 30 von List & Label mit neuen Cloud-Funktionen - Reportgenerator
Die neue Version des Reporting-Tools List & Label unterstützt Entwickler:innen mit neuen Features, die speziell für Cloud- und Webumgebungen konzipiert sind.
2 Minuten
21. Okt 2024
SignalRC braucht LTE - Der DDC-Truck, Teil 2
Das LTE-Netz als Transportkanal digitaler Steuerungsdaten bei RC-Modellen.
6 Minuten
28. Jan 2026
SignalRC – in Echtzeit ans Steuer - Der DDC-Truck: Auf in die Welt mit SignalR, Raspberry Pi und sechs Rädern
Ein vernetztes Fahrzeug, gesteuert per Weboberfläche und LTE. SignalR sorgt dabei für millisekundenschnelle Kommunikation – direkt, stabil und skalierbar.
16 Minuten
21. Jan 2026
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige