Inyección de Dependencias en Arquitectura de Software
Resumen
Es muy sencillo crear un servicio en NestJS, inyectarlo en un componente y utilizar su lógica. A pesar de esto, siempre es recomendable entender cómo lo está haciendo y qué sucede por detrás en tu aplicación.
Patrones de diseño en NestJS
NestJS utiliza varios Patrones de Diseño para permitir que esto funcione. Te presentamos dos para tener en cuenta:
Inyección de dependencias
Imagínate que tienes un Servicio A que utiliza el Servicio B y este a su vez utiliza el Servicio C. Si tuvieses que instanciar el Servicio A, primero deberías instanciar el C para poder instanciar el B y luego sí hacerlo con el A. Se vuelve confuso y poco escalable si en algún momento también tienes que instanciar el Servicio D o E.
La inyección de dependencias llega para solucionar esto, resolver las dependencias de una clase por nosotros. Cuando instanciamos en el constructor el Servicio A, NestJS por detrás genera automáticamente la instancia del servicio B y C sin que nosotros nos tengamos que preocupar por estos.
Singleton
La inyección de dependencias no es el único patrón de diseño que NestJS utiliza con sus servicios. También hace uso del patrón Singleton para crear una instancia única de cada servicio. Así es como, si tienes un servicio que se utiliza en N cantidad de componentes (u otros servicios) todos estos estarán utilizando la misma instancia del servicio, compartiendo el valor de sus variables y todo su estado.
Precauciones utilizando servicios
Un servicio puede ser importado en muchos componentes u otros servicios a la vez. Puedes inyectar la cantidad de servicio que quieras en un componente, siempre de una forma controlada y coherente.
Solo debes tener cuidado con las dependencias circulares. Cuando un servicio importa a otro y este al anterior. NestJS no sabrá cuál viene primero y tendrás un error al momento de compilar tu aplicación.
Nicolas, en este video tuviste un error de concepto. El Singleton Pattern no hace parte de los principios SOLID (S: Single Responsibility)... de hecho, este patrón está incluido dentro de los STUPID principles (Singleton, Tight Coupling, Untestability, Premature Optimization, Indescriptive Naming, Duplication), que es lo opuesto a SOLID.
Incluso, el Singleton viola el OPEN/CLOSE principle.
Si tienes razón me confundí con los dos conceptos, gracias por reportarlo 🙂
ya me quede mas confundido conceptualmente con esos patrones :D
Entendiendo la inyección de dependecias
Patrón de Inyección de dependencias:
Es un principio de arquitectura donde nos permite desacoplar las cosas y simplemente un controlador por medio de su constructor puede decir que utiliza el servicio A o el servicio B
Un controlador puede inyectar mas de un servicio, tantos como quiera
Esto se logra a través del patrón singleton, lo que nos permite que una vez creada nuestra clase (servicio) la instancia de nuestro servicio se pueda utilizar para los demás controladores, sin necesidad de crear varias instancias del mismo servicio.
Decorador @inyectable
Para que lo anterior funcione en NestJS todos nuestros controladores deben tener el decorador @inyectable que le indica a nestJSque debe manejar esto como una dependencia y cumplir con el patrón singleton
Un servicio solo pertenece a un Modulo
@Injectable
Los controladores no se inyectan, los que se inyectan son los servicios.
El tema de la inyección de dependencias puede dar para un curso entero sobre patrones de diseño, principios SOLID y patrones de arquitectura.
Creo que el profe hizo un resumen lo mas corto posible para entenderlo y no verlo tan abstracto, pero me gustaría aclarar lo siguientes:
El origen es por los principios SOLID, tenemos la S que se refiere a ‘Dependency Inversion’ (Inversion de dependecias), que en pocas palabras quieres decir que un sistema flexible es aquel que se refiere a abstracciones.
La ‘Dependency Injection’ (Inyeccion de dependencias) es un patron de diseño que implementa el principio de ‘Dependency Inversion’ de los principios SOLID 😉
El problema de la referencia circular se puede resolver usando la función forwardRef(), pero como el profesor lo menciona, es una situación que se debe tratar evitar
Documentación
@Nicolas Molina no entiendo esa parte de inyectar varios servicios en un solo controlador, hasta donde voy entendiendo, los controladores cumplen el papel de las rutas, es decir, se puede añadir lógica en un controlador, pero yo supongo que seria mucho mejor hacerlo en el mismo servicio, donde se maneja la lógica del negocio, entonces, en que casos, tal vez un ejemplo, en el que podamos añadir varios servicios en un controlador?
Tengo exactamente la misma duda, en el capitulo anterior lo agrego en el servicio porque no directamente en el controlador como sugieres en este video? o al menos poder saber en que caso inyectarlo en el servicio o el controlador. Esta me tiene muy confundido :(
Los servicios normalmente contienen la lógica del negocio. Es decir, el negocio establece qué datos mostrar, y cómo, cuando se recibe una petición. Encapsular la conexión a una base de datos, y todas las validaciones/operaciones que se realiza con esos datos previo a entregarlos al cliente, si se añaden a la capa de ruteo (controladores) volvería todo más complejo.
No tengo experiencia de algún proyecto de producción, pero puedo imaginarme que los arcihvos de servicios (.service.ts) pueden volverse bastante grandes y por ello algún desarrollador podría separarlo por petición, por ejemplo: separadas las peticioas GET, POST, UPDATE, DELETE. Todos esos archivos, cada uno convertido en un servicio (una clase) diferente, puedo inyectarlo en un solo controlador.
Pregunta:
Si no puede ser circular el servicio de user puede solicitar dame todos los productos de este usuario pero en products no puede decir dame todos los usuarios que compraron este producto ¿? me perdí en esto.
Si exacto no se podría porque que entonces tendrías un error de dependencia circular, recuerda que igual lo puede solucionar con un módulo padre o directamente por código puedes ver más acerca de Circular dependency en la doc, en donde con un decorador lo podrías solucionar, si necesitas ese caso.
super recomendacion de esta documentación estrella para patrones de diseño
En NestJS, la inyección de dependencias (DI) se utiliza para proporcionar objetos y servicios a una clase que los necesita. En lugar de que la clase cree estos objetos y servicios directamente, se le proporcionan a través de la inyección de dependencias.
Los decoradores @Injectable y @Inject se utilizan para indicar que una clase es una dependencia que puede ser inyectada en otras clases a través de la inyección de dependencias.
Para realizar una inyección de dependencias eficiente se usa el patrón de diseño Singleton, el cual es un patrón que garantiza que solo haya una instancia de una clase en todo el sistema.
El uso de Singleton nos permite ahorrar memoría ya que evita tener que crear una instancia del servicio cada vez que lo usemos.
Para los que de pronto sientan dudas con el patrón singleton aquí dejo el enlace de una clase con el buen Mauro, que en excelente Singleton Platzi Que hace parte del curso de buenas practicas para escribir código.
Buena explicación, Nicolas :D
Typescript siempre me hace pensar mucho en Java, y es genial!!
El patrón Singleton se enfoca en garantizar que una clase solo tenga una instancia y proporcionar un punto de acceso global a ella. No entra de los principios SOLID.