Actualmente estoy creando algunos proyectos experimentales con nodejs. He programado muchas aplicaciones web Java EE con Spring y aprecié la facilidad de la inyección de dependencia allí.
Ahora tengo curiosidad: ¿cómo hago la inyección de dependencia con el nodo? O: ¿lo necesito? ¿Existe un concepto de reemplazo, porque el estilo de programación es diferente?
Estoy hablando de cosas simples, como compartir un objeto de conexión de base de datos, hasta ahora, pero no he encontrado una solución que me satisfaga.
Respuestas:
En resumen, no necesita un contenedor de inyección de dependencia o un localizador de servicios como lo haría en C # / Java. Como Node.js aprovecha el
module pattern
, no es necesario realizar una inyección de constructor o propiedad. Aunque todavía puedes.Lo mejor de JS es que puede modificar casi cualquier cosa para lograr lo que desea. Esto es útil cuando se trata de pruebas.
He aquí mi ejemplo inventado muy cojo.
MyClass.js
:MyClass.test.js
:¿Notan cómo
MyClass
depende delfs
módulo? Como @ShatyemShekhar mencionó, de hecho puede hacer una inyección de constructor o propiedad como en otros idiomas. Pero no es necesario en Javascript.En este caso, puedes hacer dos cosas.
Puede desactivar el
fs.readdirSync
método o puede devolver un módulo completamente diferente cuando llamerequire
.Método 1:
Método 2:
La clave es aprovechar el poder de Node.js y Javascript. Tenga en cuenta que soy un tipo de CoffeeScript, por lo que mi sintaxis JS puede ser incorrecta en alguna parte. Además, no estoy diciendo que esta sea la mejor manera, pero es una forma. Los gurús de Javascript podrían intervenir con otras soluciones.
Actualizar:
Esto debería abordar su pregunta específica con respecto a las conexiones de la base de datos. Crearía un módulo separado para que encapsule la lógica de conexión de su base de datos. Algo como esto:
MyDbConnection.js
: (asegúrese de elegir un nombre mejor)Entonces, cualquier módulo que necesite una conexión de base de datos incluiría su
MyDbConnection
módulo.SuperCoolWebApp.js
:No siga este ejemplo literalmente. Es un ejemplo lamentable al tratar de comunicar que aprovecha el
module
patrón para administrar sus dependencias. Esperemos que esto ayude un poco más.fuente
require('my_logger_library')
, las personas que usan mi componente tendrán que anular el requisito de usar su propia biblioteca. En cambio, puedo permitir que las personas pasen una devolución de llamada que envuelva una implementación del registrador en el componente "constructor" o método "init". Ese es el propósito de DI.require
es la forma de administrar dependencias en Node.js y seguramente es intuitivo y efectivo, pero también tiene sus limitaciones.Mi consejo es que eche un vistazo a algunos de los contenedores de inyección de dependencia disponibles hoy para que Node.js tenga una idea de cuáles son sus pros / contras. Algunos de ellos son:
Sólo para nombrar unos pocos.
Ahora la verdadera pregunta es, ¿qué se puede lograr con un contenedor DI Node.js, en comparación con un simple
require
?Pros:
Contras:
require
definitivamente se siente como si se estuviera desviando de la forma de pensar de Node.Al igual que con cualquier cosa relacionada con el desarrollo de software, elegir entre DI o
require
depende de sus requisitos, la complejidad de su sistema y su estilo de programación.fuente
Sé que este hilo es bastante antiguo en este punto, pero pensé que me gustaría intervenir con mis pensamientos sobre esto. El TL; DR es que debido a la naturaleza dinámica y sin tipo de JavaScript, en realidad puede hacer mucho sin recurrir al patrón de inyección de dependencia (DI) o usar un marco DI. Sin embargo, a medida que una aplicación se hace más grande y más compleja, DI definitivamente puede ayudar a la mantenibilidad de su código.
DI en C #
Para entender por qué DI no es una necesidad tan grande en JavaScript, es útil mirar un lenguaje fuertemente tipado como C #. (Disculpas a aquellos que no conocen C #, pero debería ser lo suficientemente fácil de seguir). Digamos que tenemos una aplicación que describe un automóvil y su bocina. Definirías dos clases:
Hay pocos problemas al escribir el código de esta manera.
Car
clase está estrechamente vinculada a la implementación particular de la bocina en laHorn
clase. Si queremos cambiar el tipo de bocina utilizada por el automóvil, tenemos que modificar laCar
clase a pesar de que su uso de la bocina no cambia. Esto también dificulta las pruebas porque no podemos probar laCar
clase aisladamente de su dependencia, laHorn
clase.Car
clase es responsable del ciclo de vida de laHorn
clase. En un ejemplo simple como este no es un gran problema, pero en aplicaciones reales las dependencias tendrán dependencias, que tendrán dependencias, etc.Car
clase debería ser responsable de crear el árbol completo de sus dependencias. Esto no solo es complicado y repetitivo, sino que viola la "responsabilidad única" de la clase. Debería centrarse en ser un automóvil, no en crear instancias.Ahora, refactoricemos esto para usar un patrón de inyección de dependencia.
Hemos hecho dos cosas clave aquí. Primero, hemos introducido una interfaz que
Horn
implementa nuestra clase. Esto nos permite codificar laCar
clase en la interfaz en lugar de la implementación particular. Ahora el código podría tomar cualquier cosa que implementeIHorn
. En segundo lugar, hemos sacado la instanciación de la bocina y la hemos pasadoCar
. Esto resuelve los problemas anteriores y deja a la función principal de la aplicación administrar las instancias específicas y sus ciclos de vida.Lo que esto significa es que podría introducir un nuevo tipo de bocina para que el automóvil use sin tocar la
Car
clase:El principal podría simplemente inyectar una instancia de la
FrenchHorn
clase en su lugar. Esto también simplifica dramáticamente las pruebas. Puede crear unaMockHorn
clase para inyectar en elCar
constructor para asegurarse de que está probando solo elCar
clase de forma aislada.El ejemplo anterior muestra la inyección manual de dependencia. Por lo general, DI se realiza con un marco (por ejemplo, Unity o Ninject en el mundo C #). Estos marcos harán todo el cableado de dependencia por usted al recorrer su gráfico de dependencia y crear instancias según sea necesario.
La forma estándar de Node.js
Ahora veamos el mismo ejemplo en Node.js. Probablemente dividiríamos nuestro código en 3 módulos:
Debido a que JavaScript no está tipificado, no tenemos el mismo acoplamiento estrecho que teníamos antes. No hay necesidad de interfaces (ni existen) ya que el
car
módulo solo intentará llamar alhonk
método en cualquierhorn
que exporta módulo.Además, debido a que Node's
require
almacena en caché todo, los módulos son esencialmente singletons almacenados en un contenedor. Cualquier otro módulo que realice unrequire
en elhorn
módulo obtendrá exactamente la misma instancia. Esto hace que compartir objetos singleton como conexiones de bases de datos sea muy fácil.Ahora todavía existe el problema de que el
car
módulo es responsable de recuperar su propia dependenciahorn
. Si quisieras que el auto usara un módulo diferente para su bocina, tendrías que cambiar larequire
declaración en elcar
módulo. Esto no es algo muy común, pero causa problemas con las pruebas.La forma habitual en que las personas manejan el problema de las pruebas es con proxyquire . Debido a la naturaleza dinámica de JavaScript, proxyquire intercepta las llamadas para exigir y devuelve los resguardos / simulacros que proporcione en su lugar.
Esto es más que suficiente para la mayoría de las aplicaciones. Si funciona para tu aplicación, ve con ella. Sin embargo, en mi experiencia a medida que las aplicaciones se hacen más grandes y complejas, mantener un código como este se vuelve más difícil.
DI en JavaScript
Node.js es muy flexible. Si no está satisfecho con el método anterior, puede escribir sus módulos utilizando el patrón de inyección de dependencia. En este patrón, cada módulo exporta una función de fábrica (o un constructor de clase).
Esto es muy análogo al método C # anterior en que el
index.js
módulo es responsable, por ejemplo, de los ciclos de vida y el cableado. Las pruebas unitarias son bastante simples, ya que puede pasar simulaciones / apéndices a las funciones. Nuevamente, si esto es lo suficientemente bueno para su aplicación, hágalo.Marco Bolus DI
A diferencia de C #, no hay marcos DI estándar establecidos para ayudarlo con su gestión de dependencias. Existen varios marcos en el registro npm, pero ninguno tiene una adopción generalizada. Muchas de estas opciones ya se han citado en las otras respuestas.
No estaba particularmente satisfecho con ninguna de las opciones disponibles, así que escribí mi propio bolo llamado . Bolus está diseñado para funcionar con código escrito en el estilo DI anterior e intenta ser muy SECO y muy simple. Usando exactamente lo mismo
car.js
y loshorn.js
módulos anteriores, puede reescribir elindex.js
módulo con bolo como:La idea básica es crear un inyector. Registra todos sus módulos en el inyector. Entonces simplemente resuelves lo que necesitas. Bolus recorrerá el gráfico de dependencia y creará e inyectará dependencias según sea necesario. No se ahorra mucho en un ejemplo de juguete como este, pero en aplicaciones grandes con árboles de dependencia complicados, los ahorros son enormes.
Bolus admite un montón de características ingeniosas como dependencias opcionales y pruebas globales, pero hay dos beneficios clave que he visto en relación con el enfoque estándar de Node.js. Primero, si tiene muchas aplicaciones similares, puede crear un módulo npm privado para su base que cree un inyector y registre objetos útiles en él. Luego, sus aplicaciones específicas pueden agregar, anular y resolver según sea necesario, de manera similar a cómo AngularJSinyector funciona. En segundo lugar, puede usar el bolo para administrar varios contextos de dependencias. Por ejemplo, podría usar middleware para crear un inyector secundario por solicitud, registrar la identificación de usuario, la identificación de sesión, el registrador, etc. en el inyector junto con los módulos que dependen de ellos. Luego resuelva lo que necesita para atender las solicitudes. Esto le proporciona instancias de sus módulos por solicitud y evita tener que pasar el registrador, etc. a cada llamada de función del módulo.
fuente
proxyquire
como lassinon
que le permiten hacer simulacros muy concisos, por ejemplo,let readFileStub = sinon.stub(fs, 'readFile').yields(new Error('something went wrong'));
y luego las llamadas posterioresfs.readFile
devolverán un error hasta que revierta el código auxiliarreadFileStub.restore()
. Personalmente, encuentro DI de uso cuestionable porque siento que casi requiere el uso de clases / objetos, lo cual es una suposición dudosa dadas las inclinaciones funcionales de JavaScript.jest
,rewire
,proxyquire
, etc.? Gracias.También he escrito un módulo para lograr esto, se llama rewire . Solo usa
npm install rewire
y luego:Me inspiré en la inyección de Nathan MacInnes, pero utilicé un enfoque diferente. No uso
vm
para evaluar el módulo de prueba, de hecho, uso el propio require del nodo. De esta manera, su módulo se comporta exactamente como si estuviera usandorequire()
(excepto sus modificaciones). También la depuración es totalmente compatible.fuente
Construí Electrolyte solo para este propósito. Las otras soluciones de inyección de dependencia por ahí eran demasiado invasivas para mis gustos, y jugar con lo global
require
es un agravio particular mío.Electrolyte abarca módulos, específicamente aquellos que exportan una función de "configuración" como se ve en el middleware Connect / Express. Esencialmente, estos tipos de módulos son solo fábricas para algún objeto que devuelven.
Por ejemplo, un módulo que crea una conexión de base de datos:
Lo que ve en la parte inferior son anotaciones , un bit adicional de metadatos que Electrolyte usa para crear instancias e inyectar dependencias, conectando automáticamente los componentes de su aplicación.
Para crear una conexión de base de datos:
Electrolyte atraviesa transitivamente las
@require
dependencias 'd' e inyecta instancias como argumentos para la función exportada.La clave es que esto es mínimamente invasivo. Este módulo es completamente utilizable, independiente del propio electrolito. Eso significa que las pruebas unitarias pueden probar solo el módulo bajo prueba , pasando objetos simulados sin necesidad de dependencias adicionales para volver a cablear los componentes internos.
Cuando se ejecuta la aplicación completa, Electrolyte interviene en el nivel entre módulos, conectando todo sin necesidad de globales, soldaduras o plomería excesiva.
fuente
connect()
lanza una llamada ? Aunque no estoy familiarizado con la API MySql para Node, esperaría que esta llamada sea asíncrona, por lo que la ilustración no está del todo clara.ioc.create
desde una prueba unitaria. Una prueba unitaria debe probar solo el módulo bajo prueba y no incluir otras dependencias, incluido Electrolyte. Siguiendo este consejo, lo haríasobjToTest = require('modulename')(mockObj1, mockObj2);
Lo investigué yo mismo. No me gusta introducir bibliotecas de utilidades de dependencia mágica que proporcionan mecanismos para secuestrar las importaciones de mi módulo. En su lugar, se me ocurrió una "directriz de diseño" para que mi equipo establezca explícitamente qué dependencias se pueden burlar al introducir una exportación de funciones de fábrica dentro de mis módulos.
Hago un uso extenso de las funciones de ES6 para los parámetros y la desestructuración con el fin de evitar algunas repeticiones y proporcionar un mecanismo de anulación de dependencia con nombre.
Aquí hay un ejemplo:
Y aquí hay un ejemplo de su uso.
Disculpe la sintaxis de ES6 para aquellos que no están familiarizados con ella.
fuente
Recientemente revisé este hilo por la misma razón que el OP: la mayoría de las bibliotecas que he encontrado reescriben temporalmente la declaración require. He tenido diversos grados de éxito con este método, por lo que terminé usando el siguiente enfoque.
En el contexto de una aplicación express, envuelvo app.js en un archivo bootstrap.js:
El mapa de objetos pasado al cargador se ve así:
Entonces, en lugar de llamar directamente requiere ...
Si no se encuentra ningún alias en el cargador, entonces solo se requerirá de forma predeterminada. Esto tiene dos ventajas: puedo intercambiar en cualquier versión de la clase, y elimina la necesidad de usar nombres de ruta relativos en toda la aplicación (por lo tanto, si necesito una biblioteca personalizada debajo o encima del archivo actual, no necesito recorrer , y require cacheará el módulo contra la misma clave). También me permite especificar simulacros en cualquier punto de la aplicación, en lugar de en el conjunto de pruebas inmediato.
Acabo de publicar un pequeño módulo npm por conveniencia:
https://npmjs.org/package/nodejs-simple-loader
fuente
La realidad es que puede probar su node.js sin el contenedor IoC porque JavaScript es un lenguaje de programación realmente dinámico y puede modificar casi todo en tiempo de ejecución.
Considera lo siguiente:
Por lo tanto, puede anular el acoplamiento entre componentes en tiempo de ejecución. Me gusta pensar que deberíamos aspirar a desacoplar nuestros módulos JavaScript.
La única forma de lograr un desacoplamiento real es eliminando la referencia a
UserRepository
:Esto significa que en otro lugar deberá hacer la composición del objeto:
Me gusta la idea de delegar la composición del objeto a un contenedor de IoC. Puede obtener más información sobre esta idea en el artículo El estado actual de la inversión de dependencia en JavaScript . El artículo intenta desacreditar algunos "mitos del contenedor JavaScript IoC":
Si también le gusta la idea de usar un contenedor IoC, puede echar un vistazo a InversifyJS. La última versión (2.0.0) admite muchos casos de uso:
Puede obtener más información al respecto en InversifyJS .
fuente
Para ES6 desarrollé este contenedor https://github.com/zazoomauro/node-dependency-injection
Luego puede establecer, por ejemplo, la elección del transporte en el contenedor:
Esta clase ahora es mucho más flexible, ya que ha separado la opción de transporte de la implementación al contenedor.
Ahora que el servicio de correo está en el contenedor, puede inyectarlo como una dependencia de otras clases. Si tiene una clase NewsletterManager como esta:
Al definir el servicio newsletter_manager, el servicio de correo aún no existe. Use la clase Referencia para indicarle al contenedor que inyecte el servicio de correo cuando inicializa el administrador de boletines:
También puede configurar el contenedor con archivos de configuración como archivos Yaml, Json o JS
El contenedor de servicios se puede compilar por varias razones. Estas razones incluyen la verificación de posibles problemas, como referencias circulares y hacer que el contenedor sea más eficiente.
fuente
Depende del diseño de su aplicación. Obviamente, puede hacer una inyección similar a Java donde crea un objeto de una clase con la dependencia pasada en el constructor de esta manera.
Si no está haciendo OOP en javascript, puede hacer una función init que configure todo.
Sin embargo, hay otro enfoque que puede adoptar que es más común en un sistema basado en eventos como node.js. Si puede modelar su aplicación para que solo (la mayoría de las veces) actúe sobre eventos, entonces todo lo que necesita hacer es configurar todo (lo que generalmente hago llamando a una función init) y emitir eventos desde un stub. Esto hace que las pruebas sean bastante más fáciles y legibles.
fuente
Siempre me gustó la simplicidad del concepto de IoC: "No tiene que saber nada sobre el medio ambiente, alguien lo llamará cuando sea necesario"
Pero todas las implementaciones de IoC que vi hicieron exactamente lo contrario: saturan el código con aún más cosas que sin él. Entonces, creé mi propio IoC que funciona como me gustaría que sea: permanece oculto e invisible el 90% del tiempo .
Se utiliza en el marco web de MonoJS http://monojs.org
Se hace así: registre el componente una vez en la configuración.
Y úsalo en cualquier lugar
Puede ver el código de definición de componente completo (con DB Connection y otros componentes) aquí https://github.com/sinizinairina/mono/blob/master/mono.coffee
Este es el único lugar donde tiene que decirle a IoC qué hacer, después de eso, todos esos componentes se crearán y cablearán automáticamente y ya no tendrá que ver el código específico de IoC en su aplicación.
El IoC en sí https://github.com/alexeypetrushin/miconjs
fuente
Creo que todavía necesitamos la inyección de dependencias en Nodejs porque afloja las dependencias entre los servicios y hace que la aplicación sea más clara.
Inspirado por Spring Framework , también implemento mi propio módulo para admitir la inyección de dependencia en Nodejs. Mi módulo también puede detectar el
code changes
yauto reload
los servicios sin reiniciar la aplicación.Visita mi proyecto en: Buncha - contenedor IoC
¡Gracias!
fuente
Eche un vistazo a las inmersiones (un marco de administración de entidad (archivo) de inyección de dependencia y entidad simple pero potente para Node.js)
https://github.com/devcrust/node-dips
fuente
Trabajé con .Net, PHP y Java durante mucho tiempo, así que también quería tener una inyección de dependencia conveniente en NodeJS. La gente dijo que el DI incorporado en NodeJS es suficiente, ya que podemos obtenerlo con el Módulo. Pero no me satisfizo bien. Quería mantener un Módulo no más que una Clase. Además, quería que el DI tuviera un soporte completo para la gestión del ciclo de vida del módulo (módulo singleton, módulo transitorio, etc.) pero con el módulo Node, tuve que escribir el código manual con mucha frecuencia. Por último, quería hacer que Unit Test sea más fácil. Es por eso que creé una Inyección de dependencia para mí.
Si estás buscando una DI, pruébalo. Se puede encontrar aquí: https://github.com/robo-creative/nodejs-robo-container . Está completamente documentado. También aborda algunos problemas comunes con DI y cómo resolverlos en forma POO. Espero eso ayude.
fuente
Recientemente creé una biblioteca llamada circuitbox que te permite usar la inyección de dependencia con node.js. Realiza una verdadera inyección de dependencia frente a muchas de las bibliotecas basadas en la búsqueda de dependencia que he visto. Circuitbox también admite rutinas asincrónicas de creación e inicialización. A continuación se muestra un ejemplo:
Suponga que el siguiente código está en un archivo llamado consoleMessagePrinter.js
Suponga que lo siguiente está en el archivo main.js
Circuitbox le permite definir sus componentes y declarar sus dependencias como módulos. Una vez que se inicializa, le permite recuperar un componente. Circuitbox inyecta automáticamente todos los componentes que requiere el componente de destino y se lo proporciona para su uso.
El proyecto está en versión alfa. Sus comentarios, ideas y comentarios son bienvenidos.
¡Espero eso ayude!
fuente
Creo que otras publicaciones han hecho un gran trabajo en el argumento para usar DI. Para mi las razones son
Inyectar dependencias sin conocer sus caminos. Esto significa que si cambia la ubicación de un módulo en el disco o lo cambia por otro, no necesita tocar todos los archivos que dependen de él.
Hace que sea mucho más fácil burlarse de las dependencias para las pruebas sin la molestia de anular la
require
función global de una manera que funcione sin problemas.Le ayuda a organizar y razonar sobre su aplicación como módulos sueltos.
Pero me costó mucho encontrar un marco DI que mi equipo y yo podamos adoptar fácilmente. Así que recientemente construí un marco llamado deppie basado en estas características
require
módulosfuente
Debe ser flexible y simple como este:
He escrito un artículo sobre la inyección de dependencias en node.js.
Espero que pueda ayudarte con esto.
fuente
Node.js requiere DI tanto como cualquier otra plataforma. Si está construyendo algo grande, DI facilitará burlarse de las dependencias de su código y probarlo a fondo.
Sus módulos de capa de base de datos, por ejemplo, no solo deberían ser necesarios en sus módulos de código comercial porque, cuando la unidad prueba estos módulos de código comercial, los datos se cargarán y se conectarán a la base de datos.
Una solución sería pasar las dependencias como parámetros del módulo:
De esta manera, las dependencias se pueden burlar de forma fácil y natural, y puede concentrarse en probar su código, sin usar ninguna biblioteca de terceros complicada.
Existen otras soluciones (broadway, arquitecto, etc.) que pueden ayudarlo con esto. aunque pueden hacer más de lo que quieres o usar más desorden.
fuente
He desarrollado una biblioteca que maneja la inyección de dependencia de una manera simple, que disminuye el código repetitivo. Cada módulo está definido por un nombre único y una función de controlador. Los parámetros del controlador reflejan las dependencias del módulo.
Lea más sobre KlarkJS
Breve ejemplo:
myModuleName1
es el nombre del módulo$nodeModule1
es una biblioteca externa denode_module
. El nombre se resuelve ennode-module1
. El prefijo$
indica que es un módulo externo.myModuleName2
es el nombre de un módulo interno.myModuleName1
.fuente
Descubrí esta pregunta mientras respondía a un problema en mi propio módulo DI preguntando por qué uno necesitaría un sistema DI para la programación de NodeJS.
La respuesta fue claramente a las que se dan en este hilo: depende. Hay compensaciones para ambos enfoques y leer las respuestas de esta pregunta les da una buena forma.
Entonces, la verdadera respuesta a esta pregunta, debería ser que en algunas situaciones, usaría un sistema DI, en otras no.
Dicho esto, lo que desea como desarrollador es no repetirse y reutilizar sus servicios en sus diversas aplicaciones.
Esto significa que deberíamos escribir servicios que estén listos para usarse en el sistema DI pero que no estén vinculados a las bibliotecas DI. Para mí, significa que deberíamos escribir servicios como este:
De esa manera, su servicio funciona sin importar si lo usa con o sin una herramienta DI.
fuente
TypeDI es el más dulce de todos los mencionados aquí, mira este código en TypeDI
Mira este código también:
fuente