Qué son los módulos de Nest, una de las piezas fundamentales para la organización del código de las aplicaciones. Cómo crear módulos y cómo asociar artefactos de las aplicaciones NestJS, como controladores o servicios.
En este artículo del Manual de Nest vamos a comenzar a trabajar con módulos personalizados, creados para nuestras aplicaciones, de modo que consigamos una arquitectura un poco más avanzada.
Los módulos son clases que funcionan como contenedores de otras clases o artefactos, como son los controladores, servicios y otros componentes desarrollados con Nest. Los módulos sirven para agrupar elementos, de modo que una aplicación podrá tener varios módulos con clases altamente relacionadas entre sí. Todas las aplicaciones tienen al menos un módulo, que es el módulo raíz o módulo principal, y generalmente de este módulo principal dependerán otros módulos secundarios.
Hasta el momento hemos trabajado con un único módulo en la aplicación, que era el módulo principal instalado en el boilerplate inicial. A este módulo también le llamamos módulo raíz y siempre existirá en las aplicaciones. De él podemos hacer que cuelguen todos los artefactos (controladores, providers, etc.) que necesitemos, sin embargo es ideal que construyamos las aplicaciones usando diversos módulos que nos permitan organizar los componentes de una manera efectiva.
Ten en cuenta que una aplicación puede manejar decenas de entidades, para las que necesitarás controladores, servicios y otros componentes. Si todo ello se definiese en el módulo principal, sería un caos.
Decorador @Module()
Como otros componentes de las aplicaciones Nest, los módulos no son más que clases de programación orientada a objetos. Para conseguir que las clases se comporten como un módulo usamos el decorador @Module(), que nos permite asociar metadatos a una clase.
En el módulo principal ya pudimos ver el decorador @Module() en su código. Básicamente a la hora de definirlo se declaran determinados elementos de tipo array con los componentes que manejaba ese módulo.
En un módulo podremos definir los siguientes elementos:
- Controladores: Son las clases controlador que un módulo defina.
- Providers: Son los servicios, factorías y otros elementos injectables que son definidos por este módulo y que por tanto serán instanciados por el propio Nest para poder entregarlos allá donde se necesiten. Como dijimos, estos providers son instanciados una única vez y a lo largo del módulo se compartirá esa única instancia.
- Exports: Es una lista de providers que podrán ser usados desde otros módulos. No todos los providers declarados en un módulo deben de ser conocidos fuera de él. Por supuesto, los providers seguirán siendo "singleton" aunque se usen desde otros módulos externos donde se puedan compartir.
- Imports: es una lista de otros módulos cuyos providers podemos usar desde éste módulo.
El dominio de la aplicación y los módulos
El propio modelo de dominio de la aplicación es el que generalmente definirá qué módulos vas a implementar, lo que nos permite una organización muy coherente de las piezas de software que se desarrollarán.
Para quien no lo sepa, el modelo del dominio consiste en la definición de todos los elementos con los que una aplicación necesita trabajar y sus relaciones. Por ejemplo, en una aplicación de facturación tendríamos elementos como clientes, facturas, productos, etc. Un cliente puede tener diversas facturas, una factura puede tener diversos productos, y así. Definir correctamente el modelo de dominio es la primera tarea que normalmente nos planteamos para desarrollar una aplicación. Cabe aclarar que el modelo de dominio no tiene que ver con el diagrama de entidad relación de la base de datos, aunque principalmente permite ordenar las ideas y muchas veces los conceptos principales que manejará serán las propias entidades de la base de datos, pero no únicamente, ya que en el modelo de dominio expresamos muchas otras ideas que al final no acaban siendo tablas de la base de datos. Tampoco es un diagrama de clases, un motivo similar.
Por poner un ejemplo concreto, en el Manual de NestJS hasta el momento solamente tenemos una entidad llamada productos. Realmente hemos desarrollado sobre la marcha un par de piezas para poder implementar el trabajo con los productos, pero podrían haber sido más. Todas esas piezas forman parte del dominio de produto y por lo tanto lo lógico será que las agrupemos en un módulo.
Cada módulo se encargará de de una única funcionalidad, permitiendo que sea muy clara la localización del código relativa a dicha funcionalidad. Esto permitirá que la organización del proyecto sea sencilla y que todos los desarrolladores del equipo puedan saber dónde va cada cosa o donde se encuentra cada cosa.
Cómo crear un módulo en NestJS
Como es habitual, usaremos el CLI de NestJS para crear nuevos módulos en la aplicación. Para ello usamos el comando "generate module", seguido del nombre del módulo que queremos crear.
nest generate module products
Si lo preferimos, podemos usar el shortcut siguiente:
nest g mo products
Este comando creará el archivo "products.module.ts
", con el módulo llamado "ProductsModule
", que meterá dentro de la carpeta "products
". El código del módulo generado viene básicamente vacío.
import { Module } from '@nestjs/common';
@Module({})
export class ProductsModule {}
Además, al crearse el módulo también se edita el archivo del módulo principal "AppModule", realizando el import del módulo recién creado y agregando dicho módulo en el array "imports" del decorador @Module().
// [...]
import { ProductsModule } from './products/products.module';
@Module({
imports: [ProductsModule],
// [...]
})
Por supuesto, podríamos crear nosotros el módulo a mano, creando la carpeta products y el archivo "products.module.ts". Sin embargo, en este caso tendríamos que modificar también a mano el módulo principal de la aplicación.
Colocar el controlador, provider e interfaz de producto en el módulo
Ahora, en nuestro módulo de productos vamos a ir colocando todos los archivos que antes pertenecían al módulo principal, para conseguir una correcta organización.
El código de nuestro módulo de productos quedará más o menos así:
import { Module } from '@nestjs/common';
import { ProductsController } from './products.controller';
import { ProductsService } from './products.service';
@Module({
controllers: [ProductsController],
providers: [ProductsService]
})
export class ProductsModule {}
La estructura de carpetas y archivos que estamos manejando ahora sería como la que vemos en la siguiente imagen:
Creando componentes dentro de un módulo
El CLI de Nest se comporta de una manera bastante inteligente a la hora de situar los componentes que vayamos creando dentro de los módulos correctos.
Vamos a suponer que tenemos otra entidad en nuestra aplicación llamada "Tag", que consiste en una etiqueta. Más adelante, los productos podrán tener una o más etiquetas.
Para gestionar esta nueva entidad de nuestro modelo de dominio vamos a crear un módulo nuevo.
nest g mo tags
Ahora apreciarás que se crea la carpeta "tags", con la clase del módulo dentro. Además verás que se añade correctamente ese módulo dentro de los imports del módulo principal de la aplicación.
Ahora vamos a crear un controlador dentro del módulo de Tags. Lo conseguimos con el siguiente comando.
nest g co tags
Nest comprobará que el controlador se llama exactamente igual que el módulo recién creado para los tag y por tanto, lo coloca dentro del módulo correcto, colocando el componente en la carpeta "tags", tal como se esperaría.
Lo mismo pasaría con los servicios, que podríamos crear con el siguiente comando.
nest g s tags
Ahora, nuestro módulo tendrá declarados un controller y un service como provider.
import { Module } from '@nestjs/common';
import { TagsController } from './tags.controller';
import { TagsService } from './tags.service';
@Module({
controllers: [TagsController],
providers: [TagsService]
})
export class TagsModule {}
Ahora bien, si se trata de una interfaz, como las interfaces no se publican dentro de los módulos, tendremos que especificar la ruta completa donde queremos que se genere. Si queremos que la interface de Tag esté dentro de la carpeta "tags", lo haríamos de esta manera.
nest g interface tags/tag
Ahora te dejamos a ti como tarea aportar algo de código al controlador, servicio e interfaz de los tag. Esperamos que con los conocimientos adquiridos hasta el momento seas capaz de conseguirlo.
Conclusión sobre los módulos en Nest
Los módulos son fundamentales en las aplicaciones Nest. Toda aplicación nace con un único módulo, pero a medida que vayamos incorporando funcionalidad iremos agregando módulos. No te preocupes si hemos dejado cosas en el tintero y si necesitas más información para sentirte cómodo al crear nuevos módulos en Nest, porque volveremos sobre ellos más adelante, a medida que vayamos aprendiendo más cosas y haciendo que nuestra aplicación de ejemplo se haga más compleja.
En el siguiente artículo cambiaremos de tercio y comenzaremos a hablar de validaciones en Nest. El tema tiene bastante jugo por lo que necesitaremos ir poco a poco. Comenzaremos el camino hablando de lo que son los Pipes en Nest.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...