NodeJS에서 의존성 주입이 필요합니까? 아니면 ...을 처리하는 방법이 필요합니까?
저는 현재 nodejs로 실험 프로젝트를 만들고 있습니다.저는 Spring으로 많은 Java EE 웹 애플리케이션을 프로그래밍했고 거기서 의존성 주입의 용이성에 감사했습니다.
이제 궁금하군요.노드와의 종속성 주입은 어떻게 합니까?아니면: 제가 그것이 필요한가요?프로그래밍 스타일이 다르기 때문에 대체 개념이 있습니까?
지금까지 데이터베이스 연결 객체 공유와 같은 간단한 것들을 이야기하고 있지만, 저는 저를 만족시키는 해결책을 찾지 못했습니다.
간단히 말해서, C#/Java와 같은 종속성 주입 컨테이너나 서비스 로케이터가 필요하지 않습니다.부터는 Node.js 후로이를 합니다.module pattern생성자 또는 속성 주입을 수행할 필요가 없습니다.그래도 할 수는 있겠지만요.
JS의 좋은 점은 원하는 것을 달성하기 위해 거의 모든 것을 수정할 수 있다는 것입니다.이것은 테스트할 때 유용합니다.
저의 아주 보잘것없는 날조된 예를 보십시오.
MyClass.js:
var fs = require('fs');
MyClass.prototype.errorFileExists = function(dir) {
var dirsOrFiles = fs.readdirSync(dir);
for (var d of dirsOrFiles) {
if (d === 'error.txt') return true;
}
return false;
};
MyClass.test.js:
describe('MyClass', function(){
it('should return an error if error.txt is found in the directory', function(done){
var mc = new MyClass();
assert(mc.errorFileExists('/tmp/mydir')); //true
});
});
방법에목의 방법에 하십시오.MyClass는 에달있습다니에 따라 .fs? @Shatyem처럼셰카르가 언급한 바에 따르면, 당신은 실제로 다른 언어에서와 같이 건설자나 재산 주입을 할 수 있습니다.하지만 자바스크립트에서는 필요하지 않습니다.
이 경우 두 가지 작업을 수행할 수 있습니다.
스텁할 수 있습니다.fs.readdirSync방법 또는 당신은 당신이 전화할 때 완전히 다른 모듈을 반환할 수 있습니다.require.
방법 1:
var oldmethod = fs.readdirSync;
fs.readdirSync = function(dir) {
return ['somefile.txt', 'error.txt', 'anotherfile.txt'];
};
*** PERFORM TEST ***
*** RESTORE METHOD AFTER TEST ****
fs.readddirSync = oldmethod;
방법 2:.
var oldrequire = require
require = function(module) {
if (module === 'fs') {
return {
readdirSync: function(dir) {
return ['somefile.txt', 'error.txt', 'anotherfile.txt'];
};
};
} else
return oldrequire(module);
}
핵심은 Node.js와 Javascript의 힘을 활용하는 것입니다.참고로, 저는 CoffeeScript 사람이기 때문에 JS 구문이 어딘가 잘못되었을 수 있습니다.또한, 저는 이것이 최선의 방법이라고 말하는 것이 아니라, 방법입니다.Javascript gurus는 다른 솔루션과 연동할 수 있습니다.
업데이트:
데이터베이스 연결과 관련된 특정 질문을 해결합니다.데이터베이스 연결 로직을 캡슐화하기 위해 별도의 모듈을 만듭니다.이와 같은 것:
MyDbConnection.js 더 좋은
var db = require('whichever_db_vendor_i_use');
module.exports.fetchConnection() = function() {
//logic to test connection
//do I want to connection pool?
//do I need only one connection throughout the lifecyle of my application?
return db.createConnection(port, host, databasename); //<--- values typically from a config file
}
그런 다음, 데이터베이스 연결이 필요한 모듈에는 다음과 같은 모듈이 포함됩니다.MyDbConnection모듈.
SuperCoolWebApp.js:
var dbCon = require('./lib/mydbconnection'); //wherever the file is stored
//now do something with the connection
var connection = dbCon.fetchConnection(); //mydbconnection.js is responsible for pooling, reusing, whatever your app use case is
//come TEST time of SuperCoolWebApp, you can set the require or return whatever you want, or, like I said, use an actual connection to a TEST database.
이 예를 그대로 따르지 마십시오.그것은 당신이 그것을 활용한다는 것을 소통하려고 노력하는 서투른 예입니다.module종속성을 관리하는 패턴입니다.이것이 조금 더 도움이 되기를 바랍니다.
저는 이 시점에서 이 스레드가 꽤 오래되었다는 것을 알고 있지만, 저는 이것에 대한 제 생각과 일치해야겠다고 생각했습니다.TL;DR은 JavaScript의 유형화되지 않은 동적 특성으로 인해 DI(의존성 주입) 패턴이나 DI 프레임워크를 사용하지 않고도 실제로 많은 작업을 수행할 수 있습니다.그러나 응용프로그램이 점점 더 커지고 복잡해짐에 따라 DI는 코드의 유지관리를 확실히 도울 수 있습니다.
DI(C#)
자바스크립트에서 DI가 그다지 필요하지 않은 이유를 이해하기 위해서는 C#과 같은 강력한 유형의 언어를 살펴보는 것이 도움이 됩니다. (C#을 모르는 사람들에게 사과하지만 충분히 쉽게 따를 수 있어야 합니다.)자동차와 경적을 설명하는 앱이 있다고 가정해 보겠습니다.두 개의 클래스를 정의합니다.
class Horn
{
public void Honk()
{
Console.WriteLine("beep!");
}
}
class Car
{
private Horn horn;
public Car()
{
this.horn = new Horn();
}
public void HonkHorn()
{
this.horn.Honk();
}
}
class Program
{
static void Main()
{
var car = new Car();
car.HonkHorn();
}
}
이런 식으로 코드를 작성하는 데는 몇 가지 문제가 없습니다.
- 그
Car는 래스는경특구정밀결접합다니됩게하현과클적의 한 .Horn는 약만우자동사가경용종바싶적류다수합니다, ▁the▁used▁class▁modify▁to를 수정해야 합니다.Car비록 경적의 사용이 변하지 않더라도 클래스.이것은 또한 우리가 테스트할 수 없기 때문에 테스트를 어렵게 만듭니다.Car종성으로격리계급된터부속▁in,계급,Horn학생들 - 그
Car는 클스는라책다니임집을의 .Horn클래스. 이와 같은 간단한 예에서는 큰 문제가 되지 않지만, 실제 애플리케이션에서는 종속성, 종속성 등이 있습니다. 그Car클래스는 종속성의 전체 트리를 생성하는 책임이 있어야 합니다.이것은 복잡하고 반복적일 뿐만 아니라 학급의 "단일 책임"을 위반합니다.그것은 사례를 만드는 것이 아니라 자동차가 되는 것에 초점을 맞춰야 합니다. - 동일한 종속성 인스턴스를 재사용할 수 없습니다.이 장난감 응용 프로그램에서는 중요하지 않지만 데이터베이스 연결을 고려해 보십시오.일반적으로 애플리케이션 간에 공유되는 단일 인스턴스가 있습니다.
이제, 의존성 주입 패턴을 사용하기 위해 이것을 재팩터링해 보겠습니다.
interface IHorn
{
void Honk();
}
class Horn : IHorn
{
public void Honk()
{
Console.WriteLine("beep!");
}
}
class Car
{
private IHorn horn;
public Car(IHorn horn)
{
this.horn = horn;
}
public void HonkHorn()
{
this.horn.Honk();
}
}
class Program
{
static void Main()
{
var horn = new Horn();
var car = new Car(horn);
car.HonkHorn();
}
}
우리는 여기서 두 가지 중요한 일을 했습니다.저희가인 우는우리사가용인는소다니습개했터를스이먼페하저리▁that▁interface▁our▁firstduced다▁an.Horn학급 용구이를 통해 코드화할 수 있습니다.Car특정 구현 대신 인터페이스에 클래스를 지정합니다.는 이제코모것구수있현다습니할을든을 하는 모든 을 취할 수 .IHorn두 번째로, 우리는 경적의 인스턴스화를 제거했습니다.Car대신 전달했습니다.이렇게 하면 위의 문제가 해결되고 특정 인스턴스와 해당 라이프사이클을 관리하는 애플리케이션의 기본 기능에 맡겨집니다.
이것이 의미하는 바는 우리가 새로운 유형의 경음기를 도입할 수 있다는 것입니다. 자동차가 손을 대지 않고도 사용할 수 있습니다.Car 명령어:
class FrenchHorn : IHorn
{
public void Honk()
{
Console.WriteLine("le beep!");
}
}
은 메은단한주수있습다니입할를예인의 를 주입할 수 .FrenchHorn 테스트를 합니다.이는 또한 테스트를 획기적으로 간소화합니다.다음을 생성할 수 있습니다.MockHorn에 Car컨스트럭터는 당신이 단지 테스트를 하고 있는지 확인합니다.Car격리 계급
위의 예에서는 수동 종속성 주입을 보여 줍니다.일반적으로 DI는 프레임워크(예: C# 월드의 Unity 또는 Ninject)로 수행됩니다.이러한 프레임워크는 종속성 그래프를 걷고 필요에 따라 인스턴스를 생성하여 모든 종속성 배선을 수행합니다.
표준 Node.js 방식
이제 Node.js의 동일한 예를 살펴보겠습니다.코드를 세 개의 모듈로 나눌 수 있습니다.
// horn.js
module.exports = {
honk: function () {
console.log("beep!");
}
};
// car.js
var horn = require("./horn");
module.exports = {
honkHorn: function () {
horn.honk();
}
};
// index.js
var car = require("./car");
car.honkHorn();
JavaScript가 입력되지 않았기 때문에 이전과 같은 긴밀한 결합이 없습니다.도 않고 .car은 " 모은단호시것입니다할도출을지"를 호출하려고 합니다.honk무엇이든 간에 방법.horn모듈 수출
의 추으로적 에, 가노드.require모든 것을 캐시합니다. 모듈은 기본적으로 컨테이너에 저장된 싱글톤입니다.에 "" "를 하는 모듈require에서.horn모듈이 동일한 인스턴스를 가져옵니다.따라서 데이터베이스 연결과 같은 싱글톤 개체를 매우 쉽게 공유할 수 있습니다.
도 문제가 있습니다.car은 자체 을 합니다.horn차량에서 경적에 다른 모듈을 사용하기를 원한다면, 차량의 경적을 변경해야 합니다.require에 있는 .car모듈. .이 작업은 일반적인 작업은 아니지만 테스트에 문제가 발생합니다.
사람들이 테스트 문제를 처리하는 일반적인 방법은 프록시 쿼리입니다.JavaScript의 동적 특성으로 인해 프록시는 요구할 호출을 가로채고 대신 제공한 스텁/모크를 반환합니다.
var proxyquire = require('proxyquire');
var hornStub = {
honk: function () {
console.log("test beep!");
}
};
var car = proxyquire('./car', { './horn': hornStub });
// Now make test assertions on car...
이는 대부분의 애플리케이션에 충분합니다.당신의 앱에서 작동한다면, 그것을 사용하세요.그러나 애플리케이션이 점점 더 커지고 복잡해짐에 따라 이러한 코드를 유지하는 것이 더욱 어려워졌습니다.
자바스크립트의 DI
Node.js는 매우 유연합니다.위의 방법이 만족스럽지 않으면 종속성 주입 패턴을 사용하여 모듈을 작성할 수 있습니다.이 패턴에서 모든 모듈은 공장 기능(또는 클래스 생성자)을 내보냅니다.
// horn.js
module.exports = function () {
return {
honk: function () {
console.log("beep!");
}
};
};
// car.js
module.exports = function (horn) {
return {
honkHorn: function () {
horn.honk();
}
};
};
// index.js
var horn = require("./horn")();
var car = require("./car")(horn);
car.honkHorn();
이것은 이전의 C# 방법과 매우 유사합니다.index.js모듈은 인스턴스 수명 주기 및 배선을 담당합니다.장치 테스트는 모크/스텁을 기능에 전달하기만 하면 되므로 매우 간단합니다.다시 말씀드리지만, 이것이 당신의 애플리케이션에 충분하다면 그것을 따르세요.
볼루스 DI 프레임워크
C#과 달리 종속성 관리를 지원하는 확립된 표준 DI 프레임워크는 없습니다.npm 레지스트리에는 많은 프레임워크가 있지만 널리 채택된 프레임워크는 없습니다.이러한 옵션 중 많은 부분이 이미 다른 답변에서 언급되었습니다.
저는 이용 가능한 옵션 중 어떤 것도 특별히 만족하지 않아서 bolus라고 불리는 저만의 것을 썼습니다.Bolus는 위의 DI 스타일로 작성된 코드로 작업하도록 설계되었으며 매우 건조하고 매우 단순하려고 노력합니다.동일한 것을 사용합니다.car.js그리고.horn.js모듈은 위모듈들, 은다쓸수있다니습시신을 다시 할 수 .index.js볼러스가 있는 모듈:
// index.js
var Injector = require("bolus");
var injector = new Injector();
injector.registerPath("**/*.js");
var car = injector.resolve("car");
car.honkHorn();
기본적인 아이디어는 인젝터를 생성하는 것입니다.인젝터에 모든 모듈을 등록합니다.그런 다음 필요한 것을 간단히 해결할 수 있습니다.Bolus는 종속성 그래프를 걷고 필요에 따라 종속성을 만들고 주입합니다.이와 같은 장난감의 예에서는 많은 비용을 절약할 수 없지만, 복잡한 종속성 트리가 있는 대규모 애플리케이션에서는 비용을 크게 절감할 수 있습니다.
Bolus는 옵션 종속성 및 테스트 글로벌과 같은 다양한 기능을 지원하지만 표준 Node.js 접근 방식과 관련하여 두 가지 주요 이점이 있습니다.먼저, 유사한 애플리케이션이 많은 경우, 인젝터를 생성하고 유용한 객체를 등록하는 개인 npm 모듈을 기반으로 생성할 수 있습니다.그러면 특정 앱이 Angular 방식처럼 필요에 따라 추가, 재정의 및 해결할 수 있습니다.JS의 인젝터가 작동합니다.둘째, 다양한 의존성 컨텍스트를 관리하기 위해 bolus를 사용할 수 있습니다.예를 들어 미들웨어를 사용하여 요청별로 하위 인젝터를 생성하고, 인젝터에 사용자 ID, 세션 ID, 로거 등과 함께 이러한 모듈을 등록할 수 있습니다.그런 다음 요청을 처리하는 데 필요한 항목을 해결합니다.이렇게 하면 요청당 모듈 인스턴스가 제공되며 로거 등을 모든 모듈 함수 호출에 전달할 필요가 없습니다.
require() Modules (ES 모듈import)는 Node.js의 종속성을 관리하는 방법이며, 확실히 직관적이고 효과적이지만 한계도 있습니다.
현재 Node.js에서 사용할 수 있는 종속성 주입 컨테이너 중 일부를 검토하여 장단점을 파악하는 것이 좋습니다.그 중 일부는 다음과 같습니다.
몇 가지 예를 들자면요.
한 Node.js DI 컨테이너와 하여 Node할 수 것입니다.require()또는import?
찬성:
- 테스트 가능성 향상: 모듈은 종속성을 입력으로 받아들입니다.
- 제어 전환: 응용 프로그램의 메인 코드를 건드리지 않고 모듈을 연결하는 방법을 결정합니다.
- 모듈을 해결하기 위한 사용자 정의 가능한 알고리즘: 종속성에는 "가상" 식별자가 있으며, 일반적으로 파일 시스템의 경로에 바인딩되지 않습니다.
- 향상된 확장성: IoC 및 "가상" 식별자에 의해 활성화됩니다.
- 다른 멋진 것들이 가능합니다.
- 비동기 초기화
- 모듈 수명 주기 관리
- DI 컨테이너 자체의 확장성
- 보다 높은 수준의 추상화(예: AOP)를 쉽게 구현할 수 있습니다.
단점:
- Node.js "경험"과는 다릅니다. DI를 사용하는 것은 확실히 노드의 사고 방식에서 벗어나는 것처럼 느껴집니다.
- 종속성과 그 구현 사이의 관계가 항상 명시적인 것은 아닙니다.종속성은 런타임에 해결되고 다양한 매개 변수의 영향을 받을 수 있습니다.코드를 이해하고 디버깅하기가 더 어려워집니다.
- 시작 시간이 느림
- 대부분의 DI 컨테이너는 Browserify 및 Webpack과 같은 모듈 번들러에서 제대로 작동하지 않습니다.
개발과 또는 소프웨어개관발모련된든과는또것로마 DI 중 를 선택하는 것.require()/import사용자의 요구 사항, 시스템 복잡성 및 프로그래밍 스타일에 따라 달라집니다.
저는 이것을 달성하기 위해 모듈을 작성했습니다. 그것은 리와이어라고 불립니다.그냥 사용하기npm install rewire다음과 같은 경우:
var rewire = require("rewire"),
myModule = rewire("./path/to/myModule.js"); // exactly like require()
// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123
// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
readFile: function (path, encoding, cb) {
cb(null, "Success!");
}
});
myModule.readSomethingFromFileSystem(function (err, data) {
console.log(data); // = Success!
});
저는 네이선 매킨즈의 주사기에서 영감을 받았지만 다른 접근법을 사용했습니다.사용 안 함vm테스트 모듈을 평가하기 위해 노드 자체 요구 사항을 사용합니다.이렇게 이 이한방모다같동이다작을 사용하는 것과 합니다.require()(수정 사항은 제외).디버깅도 완벽하게 지원됩니다.
저는 단지 이 목적을 위해 전해질을 만들었습니다.다른 의존성 주사 해결책들은 내 취향에 너무 침해적이었고, 세계를 어지럽혔습니다.require저의 특별한 불만 사항입니다.
전해질은 모듈, 특히 Connect/Express 미들웨어에서 볼 수 있는 "설정" 기능을 내보내는 모듈을 포함합니다.기본적으로 이러한 유형의 모듈은 반환되는 일부 개체를 위한 공장일 뿐입니다.
예를 들어 데이터베이스 연결을 만드는 모듈은 다음과 같습니다.
var mysql = require('mysql');
exports = module.exports = function(settings) {
var connection = mysql.createConnection({
host: settings.dbHost,
port: settings.dbPort
});
connection.connect(function(err) {
if (err) { throw err; }
});
return connection;
}
exports['@singleton'] = true;
exports['@require'] = [ 'settings' ];
하단에 보이는 것은 주석, 즉 의존성을 인스턴스화하고 주입하는 데 사용되는 메타데이터의 추가 비트로, 애플리케이션의 구성 요소를 자동으로 배선합니다.
데이터베이스 연결을 만드는 방법
var db = electrolyte.create('database');
전해액이 과도적으로 통과합니다.@require 의존성, 함수에 를 삽입합니다d 속종이며내보를인스삽다입니합턴스로수인한대성낸함수에▁'▁as다'▁to니삽합입d▁and,enciess▁depend▁inject▁function▁instancesd▁arguments▁exported종속를스.
중요한 것은 이것이 최소한의 침습성이라는 것입니다.이 모듈은 전해액 자체와 관계없이 완전히 사용할 수 있습니다.즉, 유닛 테스트는 내부 배선을 재연결할 추가적인 종속성 없이 모의 개체를 전달하여 테스트 대상 모듈만 테스트할 수 있습니다.
전체 응용 프로그램을 실행할 때 전해액은 모듈 간 수준에서 단계를 밟아 글로벌, 싱글톤 또는 과도한 배관 없이 물건을 배선합니다.
제가 직접 조사해 봤습니다.모듈 가져오기를 가로채는 메커니즘을 제공하는 마법 의존성 유틸리티 라이브러리를 도입하는 것을 싫어합니다.대신 모듈 내에 공장 기능 내보내기를 도입하여 어떤 종속성을 조롱할 수 있는지를 팀이 명시적으로 설명할 수 있도록 "설계 지침"을 마련했습니다.
저는 일부 보일러 플레이트를 피하고 명명된 종속성 오버라이드 메커니즘을 제공하기 위해 매개 변수 및 파괴에 ES6 기능을 광범위하게 사용합니다.
다음은 예입니다.
import foo from './utils/foo';
import bob from './utils/bob';
// We export a factory which accepts our dependencies.
export const factory = (dependencies = {}) => {
const {
// The 'bob' dependency. We default to the standard 'bob' imp if not provided.
$bob = bob,
// Instead of exposing the whole 'foo' api, we only provide a mechanism
// with which to override the specific part of foo we care about.
$doSomething = foo.doSomething // defaults to standard imp if none provided.
} = dependencies;
return function bar() {
return $bob($doSomething());
}
}
// The default implementation, which would end up using default deps.
export default factory();
그리고 여기 그것의 사용 예가 있습니다.
import { factory } from './bar';
const underTest = factory({ $bob: () => 'BOB!' }); // only override bob!
const result = underTest();
ES6 구문에 익숙하지 않은 사용자를 위해 양해 부탁드립니다.
저는 최근에 OP와 거의 같은 이유로 이 스레드를 확인했습니다. 제가 만난 대부분의 libs는 일시적으로 require 문을 다시 작성합니다.저는 이 방법으로 여러 가지 성공을 거두었고, 결국 다음과 같은 접근법을 사용하게 되었습니다.
express 애플리케이션의 맥락에서 - app.js를 bootstrap.js 파일로 래핑합니다.
var path = require('path');
var myapp = require('./app.js');
var loader = require('./server/services/loader.js');
// give the loader the root directory
// and an object mapping module names
// to paths relative to that root
loader.init(path.normalize(__dirname), require('./server/config/loader.js'));
myapp.start();
로더에 전달된 개체 맵은 다음과 같습니다.
// live loader config
module.exports = {
'dataBaseService': '/lib/dataBaseService.js'
}
// test loader config
module.exports = {
'dataBaseService': '/mocks/dataBaseService.js'
'otherService' : {other: 'service'} // takes objects too...
};
그럼 직접 전화하기 보다는...
var myDatabaseService = loader.load('dataBaseService');
로더에 별칭이 없으면 기본적으로 일반적인 요구 사항으로 설정됩니다.두 가지 이점이 있습니다.클래스의 모든 버전에서 스왑할 수 있으므로 응용 프로그램 전체에서 상대 경로 이름을 사용할 필요가 없습니다(따라서 현재 파일 아래 또는 위에 사용자 지정 lib가 필요한 경우에는 이동할 필요가 없으며 동일한 키에 대해 모듈을 캐시합니다).또한 즉각적인 테스트 스위트가 아닌 앱의 어느 지점에서든 모의실험을 지정할 수 있습니다.
편의를 위해 작은 npm 모듈을 방금 게시했습니다.
https://npmjs.org/package/nodejs-simple-loader
현실은 자바스크립트가 정말 동적인 프로그래밍 언어이고 런타임에 거의 모든 것을 수정할 수 있기 때문에 IoC 컨테이너 없이도 node.js를 테스트할 수 있다는 것입니다.
다음 사항을 고려합니다.
import UserRepository from "./dal/user_repository";
class UserController {
constructor() {
this._repository = new UserRepository();
}
getUsers() {
this._repository.getAll();
}
}
export default UserController;
따라서 런타임에 구성 요소 간의 결합을 재정의할 수 있습니다.저는 우리가 자바스크립트 모듈을 분리하는 것을 목표로 해야 한다고 생각합니다.
실질적인 디커플링을 달성하는 유일한 방법은 다음에 대한 참조를 제거하는 것입니다.UserRepository:
class UserController {
constructor(userRepository) {
this._repository = userRepository;
}
getUsers() {
this._repository.getAll();
}
}
export default UserController;
즉, 다른 곳에서 개체 구성을 수행해야 합니다.
import UserRepository from "./dal/user_repository";
import UserController from "./dal/user_controller";
export default new UserController(new UserRepository());
사물 구성을 IoC 컨테이너에 위임하는 아이디어가 마음에 듭니다.이 아이디어에 대한 자세한 내용은 JavaScript의 종속성 반전의 현재 상태 기사에서 확인할 수 있습니다.이 기사는 일부 "JavaScript IoC 컨테이너 신화"를 폭로하려고 합니다.
신화 1: JavaScript에는 IoC 컨테이너를 위한 공간이 없습니다.
신화 2: 우리는 IoT 컨테이너가 필요하지 않습니다. 우리는 이미 모듈 로더를 가지고 있습니다!
신화 3: 의존성 역전 === 주입 의존성
IoC 컨테이너를 사용하는 것에 대한 아이디어도 마음에 든다면 Inversify를 검토할 수 있습니다.JS. 최신 릴리스(2.0.0)는 다음과 같은 다양한 사용 사례를 지원합니다.
- 커널 모듈
- 커널 미들웨어
- 종속성 식별자로 클래스, 문자열 리터럴 또는 기호 사용
- 상수 값 주입
- 클래스 생성자 주입
- 공장투입
- 자동차 공장
- 공급자 주입(비동기화 공장)
- 활성화 처리기(프록시 주입에 사용됨)
- 다중 주사
- 태그가 지정된 바인딩
- 사용자 지정 태그 장식기
- 명명된 바인딩
- 상황에 맞는 바인딩
- 우호적인 예외(예: 순환 종속성)
자세한 내용은 Inversify에서 확인할 수 있습니다.제이에스
ES6를 위해 이 컨테이너를 개발했습니다. https://github.com/zazoomauro/node-dependency-injection
import {ContainerBuilder} from 'node-dependency-injection'
let container = new ContainerBuilder()
container.register('mailer', 'Mailer')
그런 다음 컨테이너에서 전송 선택 항목을 설정할 수 있습니다.
import {ContainerBuilder} from 'node-dependency-injection'
let container = new ContainerBuilder()
container
.register('mailer', 'Mailer')
.addArgument('sendmail')
이 클래스는 이제 전송 선택을 구현에서 컨테이너로 분리했기 때문에 훨씬 더 유연해졌습니다.
이제 메일러 서비스가 컨테이너에 있으므로 다른 클래스의 종속성으로 주입할 수 있습니다.다음과 같은 뉴스레터 관리자 클래스가 있는 경우:
class NewsletterManager {
construct (mailer, fs) {
this._mailer = mailer
this._fs = fs
}
}
export default NewsletterManager
newsletter_manager 서비스를 정의할 때 메일러 서비스가 아직 존재하지 않습니다.참조 클래스를 사용하여 뉴스레터 관리자를 초기화할 때 컨테이너에 메일러 서비스를 주입하도록 지시합니다.
import {ContainerBuilder, Reference, PackageReference} from 'node-dependency-injection'
import Mailer from './Mailer'
import NewsletterManager from './NewsletterManager'
let container = new ContainerBuilder()
container
.register('mailer', Mailer)
.addArgument('sendmail')
container
.register('newsletter_manager', NewsletterManager)
.addArgument(new Reference('mailer'))
.addArgument(new PackageReference('fs-extra'))
Yaml, Json 또는 JS 파일과 같은 구성 파일로 컨테이너를 설정할 수도 있습니다.
서비스 컨테이너는 다양한 이유로 컴파일될 수 있습니다.이러한 이유에는 순환 참조와 같은 잠재적인 문제를 확인하고 컨테이너를 보다 효율적으로 만드는 것이 포함됩니다.
container.compile()
응용프로그램 설계에 따라 다릅니다.이렇게 생성자에 전달된 종속성으로 클래스의 개체를 만드는 Java와 같은 주입을 수행할 수 있습니다.
function Cache(store) {
this._store = store;
}
var cache = new Cache(mysqlStore);
javascript에서 OOP를 하지 않을 경우, 모든 것을 설정하는 init 함수를 만들 수 있습니다.
그러나 node.js와 같은 이벤트 기반 시스템에서 더 일반적인 다른 접근 방식을 취할 수 있습니다.대부분의 경우 이벤트에 대한 작업만 수행하도록 응용 프로그램을 모델링할 수 있는 경우 모든 작업을 설정하고(보통 init 함수를 호출하여 수행함) 스텁에서 이벤트를 내보내는 것만 하면 됩니다.이를 통해 테스트를 상당히 쉽고 쉽게 읽을 수 있습니다.
저는 항상 IoC 개념의 단순함을 좋아했습니다. "환경에 대해 아무것도 알 필요가 없습니다. 필요할 때 누군가에게 호출될 것입니다."
하지만 제가 본 모든 IOC 구현은 정반대였습니다. 코드가 없는 것보다 훨씬 더 많은 것으로 코드를 혼란스럽게 했습니다.그래서 저는 제가 원하는 대로 작동하는 IoC를 만들었습니다. 90%의 시간 동안 숨겨진 채로 유지됩니다.
MonoJS 웹 프레임워크 http://monojs.org 에서 사용됩니다.
지금까지 데이터베이스 연결 객체 공유와 같은 간단한 것들을 이야기하고 있지만, 저는 저를 만족시키는 해결책을 찾지 못했습니다.
구성에서 구성 요소를 한 번 등록합니다.
app.register 'db', ->
require('mongodb').connect config.dbPath
어디서나 사용할 수 있습니다.
app.db.findSomething()
전체 구성요소 정의 코드(DB Connection 및 기타 구성요소 포함)는 https://github.com/sinizinairina/mono/blob/master/mono.coffee 에서 확인할 수 있습니다.
이것은 IoC에게 무엇을 해야 하는지 알려줘야 하는 유일한 장소입니다. 그 후에는 모든 구성 요소가 자동으로 생성되고 유선 연결되어 애플리케이션에서 IoC 특정 코드를 더 이상 볼 필요가 없습니다.
IoC 자체 https://github.com/alexeypetrushin/miconjs
서비스 간의 의존성을 완화하고 애플리케이션을 보다 명확하게 하기 때문에 여전히 Nodejs에 의존성 주입이 필요하다고 생각합니다.
Spring Framework에서 영감을 받아 Nodejs에서 종속성 주입을 지원하는 자체 모듈도 구현합니다.내 모듈은 또한 다음을 감지할 수 있습니다.code changes그리고.auto reload응용 프로그램을 다시 시작하지 않고 서비스를 제공합니다.
감사해요!
Node.js는 다른 플랫폼과 마찬가지로 DI가 필요합니다.만약 당신이 큰 것을 만들고 있다면, DI는 당신의 코드의 의존성을 조롱하고 당신의 코드를 철저히 테스트하는 것을 더 쉽게 만들 것입니다.
예를 들어, 비즈니스 코드 모듈에 데이터베이스 계층 모듈이 필요할 뿐만 아니라, 이러한 비즈니스 코드 모듈을 장치에서 테스트할 때 daos가 로드되어 데이터베이스에 연결되기 때문입니다.
한 가지 해결책은 종속성을 모듈 매개 변수로 전달하는 것입니다.
module.exports = function (dep1, dep2) {
// private methods
return {
// public methods
test: function(){...}
}
}
이러한 방식으로 종속성을 쉽고 자연스럽게 조롱할 수 있으며 까다로운 타사 라이브러리를 사용하지 않고도 코드 테스트에 집중할 수 있습니다.
이 문제를 해결하는 데 도움이 될 수 있는 다른 솔루션(브로드웨이, 설계자 등)이 있습니다.비록 그들이 당신이 원하는 것 이상을 하거나 더 많은 잡동사니를 사용할지도 모릅니다.
제 DI 모듈에서 NodeJS 프로그래밍에 DI 시스템이 필요한 이유를 묻는 문제에 답변하는 동안 이 질문을 발견했습니다.
답은 분명히 이 스레드에서 주어진 것들에 대한 경향이 있었습니다: 그것은 의존합니다.두 가지 접근 방식 모두에 대한 절충이 있으며 이 질문의 답변을 읽으면 좋은 모양을 얻을 수 있습니다.
그래서, 이 질문에 대한 진정한 대답은, 어떤 상황에서는 DI 시스템을 사용하고, 어떤 상황에서는 그렇지 않다는 것입니다.
즉, 개발자로서 원하는 것은 반복하지 않고 다양한 애플리케이션에서 서비스를 재사용하는 것입니다.
이것은 DI 시스템에서 사용할 준비가 되어 있지만 DI 라이브러리에 연결되지 않은 서비스를 작성해야 한다는 것을 의미합니다.이는 우리가 다음과 같은 서비스를 작성해야 한다는 것을 의미합니다.
module.exports = initDBService;
// Tells any DI lib what it expects to find in it context object
// The $inject prop is the de facto standard for DI imo
initDBService.$inject = ['ENV'];
// Note the context object, imo, a DI tool should bring
// services in a single context object
function initDBService({ ENV }) {
/// actual service code
}
그렇게 하면 DI 도구와 함께 사용하든 그렇지 않든 서비스가 작동하는 것은 문제가 되지 않습니다.
같이 일했어요.Net, PHP, Java를 오랫동안 사용했기 때문에 노드에서 편리한 Dependency Injection을 하고 싶었습니다.JS도요. 사람들은 노드에 내장된 DI가모듈로 받을 수 있기 때문에 JS로도 충분합니다.하지만 그것은 저를 만족시키지 못했습니다.저는 모듈을 클래스 이상으로 유지하고 싶었습니다.또한, 저는 DI가 모듈 수명 주기 관리(싱글톤 모듈, 과도 모듈 등)를 완벽하게 지원하기를 원했지만, Node 모듈을 사용하여 수동 코드를 매우 자주 작성해야 했습니다.마지막으로 유닛 테스트를 더 쉽게 하고 싶었습니다.그래서 저를 위한 의존성 주입을 만들었습니다.
만약 당신이 DI를 찾고 있다면, 시도해 보세요.https://github.com/robo-creative/nodejs-robo-container 에서 확인할 수 있습니다.완전히 문서화되어 있습니다.또한 DI와 관련된 몇 가지 일반적인 문제와 OOP 방식으로 해결하는 방법도 설명합니다.도움이 되길 바랍니다.
유형 DI는 여기 언급된 모든 것 중에서 가장 달콤합니다. 유형에서 이 코드를 보십시오.DI
import "reflect-metadata";
import {Service, Container} from "typedi";
@Service()
class SomeClass {
someMethod() {
}
}
let someClass = Container.get(SomeClass);
someClass.someMethod();
이 코드도 확인하십시오.
import {Container, Service, Inject} from "typedi";
// somewhere in your global app parameters
Container.set("authorization-token", "RVT9rVjSVN");
@Service()
class UserRepository {
@Inject("authorization-token")
authorizationToken: string;
}
딥(Node.js를 위한 간단하면서도 강력한 종속성 주입 및 엔티티(파일) 관리 프레임워크)에 대해 살펴봅니다.
https://github.com/devcrust/node-dips
디아이를 사용한 논쟁에서 다른 게시물들이 잘했다고 생각합니다.나에게 그 이유는
경로를 모르는 상태에서 종속성을 주입합니다.즉, 디스크에서 모듈 위치를 변경하거나 다른 모듈과 스왑할 경우 종속된 모든 파일을 터치할 필요가 없습니다.
을 무시하는 고통 없이 테스트를 위해 하는 것을 훨씬 쉽게 만듭니다.
require문제 없이 작동하는 방식으로 작동합니다.응용프로그램을 느슨하게 결합된 모듈로 구성하고 추론하는 데 도움이 됩니다.
하지만 저는 제 팀과 제가 쉽게 채택할 수 있는 DI 프레임워크를 찾는 데 정말 어려움을 겪었습니다.그래서 저는 최근에 이러한 특징들을 바탕으로 debie라고 불리는 틀을 만들었습니다.
- 몇 분 안에 학습할 수 있는 최소한의 API
- 추가 코드/구성/주석이 필요하지 않습니다.
- 매핑직접에 대한 일대일 맵핑
require설정 - 기존 코드로 작업하기 위해 부분적으로 채택할 수 있습니다.
다음과 같이 유연하고 단순해야 합니다.
var MyClass1 = function () {}
var MyClass2 = function (myService1) {
// myService1.should.be.instanceof(MyClass1);
}
container.register('myService1', MyClass1);
container.register('myService2', MyClass2, ['myService1']);
저는 node.js에 의존성 주입에 대한 기사를 썼습니다.
이것에 도움이 되길 바랍니다.
저는 종속성 주입을 간단한 방법으로 처리하는 라이브러리를 개발했는데, 이 라이브러리는 상용어구 코드를 줄여줍니다.각 모듈은 고유한 이름과 컨트롤러 기능으로 정의됩니다.컨트롤러의 매개 변수는 모듈의 종속성을 반영합니다.
간단한 예:
KlarkModule(module, 'myModuleName1', function($nodeModule1, myModuleName2) {
return {
log: function() { console.log('Hello from module myModuleName1') }
};
});
myModuleName1는 모듈의 이름입니다.$nodeModule1는 의외라리다니입의 외부 입니다.node_module이름은 다음으로 결정됩니다.node-module1두사접$외부 모듈임을 나타냅니다.myModuleName2는 내부 모듈의 이름입니다.- 할 때 모듈에서 됩니다.
myModuleName1.
응용 프로그램을 잘 테스트하려면 대부분의 경우 실행/테스트 시 제어 반전 도구를 사용하여 원하는 개체를 주입하는 것이 좋습니다.따라서 모듈에서 직접 required 또는 import를 사용하지 않는 것이 좋습니다.대신 DI 컨테이너를 호출하여 필요한 개체를 가져옵니다.
타사 라이브러리를 사용하지 않으려는 경우 사용자 정의 DI 컨테이너를 만들어 IoC 도구의 동작을 모방할 수 있습니다.테스트 시 DI 컨테이너를 조롱하고 테스트 목적으로 가짜 모듈을 주입할 수 있습니다.다음은 사용자 지정 DI 컨테이너의 예입니다.이 컨테이너 버전은 인터페이스를 지원하지 않습니다.
나의 Dependency.js.
const myDependecy = {};
export default myDependecy;
myDependecy.myTestFunction = () => {
console.log("this is as test function.");
};
diContainer.js
import myDependecy from "./myDependecy.js";
const diContainer = {};
export default diContainer;
diContainer.myDependecy = myDependecy;
나의 모듈.js.
import diContainer from "./diContainer.js";
function myFunction() {
diContainer.myDependecy.myTestFunction();
}
나는 최근에 node.js와 함께 의존성 주입을 사용할 수 있는 circuitbox라는 라이브러리를 만들었습니다.제가 본 많은 의존성 검색 기반 라이브러리에 비해 진정한 의존성 주입이 가능합니다.또한 회로 상자는 비동기 생성 및 초기화 루틴을 지원합니다.다음은 예입니다.
다음 코드가 consoleMessagePrinter.js라는 파일에 있다고 가정합니다.
'use strict';
// Our console message printer
// deps is injected by circuitbox with the dependencies
function ConsoleMessagePrinter(deps) {
return {
print: function () {
console.log(deps.messageSource.message());
}
};
}
module.exports = ConsoleMessagePrinter;
다음이 main.js 파일에 있다고 가정합니다.
'use strict';
// our simple message source
// deps is injected by circuitbox with the dependencies
var simpleMessageSource = function (deps) {
return {
message: function () {
return deps.message;
}
};
};
// require circuitbox
var circuitbox = require('../lib');
// create a circuitbox
circuitbox.create({
modules: [
function (registry) {
// the message to be used
registry.for('message').use('This is the message');
// define the message source
registry.for('messageSource').use(simpleMessageSource)
.dependsOn('message');
// define the message printer - does a module.require internally
registry.for('messagePrinter').requires('./consoleMessagePrinter')
.dependsOn('messageSource');
}
]
}).done(function (cbx) {
// get the message printer and print a message
cbx.get('messagePrinter').done(function (printer) {
printer.print();
}, function (err) {
console.log('Could not recieve a printer');
return;
});
}, function (err) {
console.log('Could not create circuitbox');
});
회로 상자를 사용하여 구성 요소를 정의하고 해당 종속성을 모듈로 선언할 수 있습니다.초기화되면 구성 요소를 검색할 수 있습니다.회로 상자는 대상 구성 요소에 필요한 모든 구성 요소를 자동으로 주입하고 사용자에게 제공합니다.
그 프로젝트는 알파 버전입니다.귀하의 의견, 아이디어 및 피드백을 환영합니다.
도움이 되길 바랍니다!
언급URL : https://stackoverflow.com/questions/9250851/do-i-need-dependency-injection-in-nodejs-or-how-to-deal-with
'programing' 카테고리의 다른 글
| 셸에 정의된 기능을 나열하려면 어떻게 해야 합니까? (0) | 2023.05.22 |
|---|---|
| WPF 툴킷 데이터 그리드에서 데이터 그리드 템플릿 열을 정렬하려면 어떻게 해야 합니까? (0) | 2023.05.22 |
| 제안/자동 완성을 통한 Excel 데이터 검증 (0) | 2023.05.22 |
| C# 어레이를 단일 값으로 채우는/인스턴스하는 방법은 무엇입니까? (0) | 2023.05.22 |
| xsd에서 .NET 4.0 클래스를 생성하는 방법은 무엇입니까? (0) | 2023.05.22 |