높은 의존성은 모듈의 재사용을 감소: 독립적이지 않으므로 다른 곳에 사용하기 어려움
모든 소스코드 의존성은 반드시 outer에서 inner로, 저수준에서 고수준 정책을 향해야 한다.
고수준 : 상위 수준의 개념, 추상화된 개념 ex. 업무로직: 데이터를 저장한다, 견적을 계산한다 저수준 : 추상화된 개념을 실제 어떻게 구현할지에 대한 세부적인 개념 ex. 세부사항: MySQL에 데이터를 저장한다, 선택 프로젝트의 견적을 계산한다
고수준의 모듈은 저수준의 모듈에 의존하면 안된다. 이 두 모듈 모두 다른 추상화된 것에 의존 해야 한다.
추상화 된 것은 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상화된 것에 의존해야 한다.
자주 변경되는 구현 클래스에 의존하지 마라. 만약 어떤 클래스의 참조를 가져야 한다면, 참조 대상이 되는 클래스를 추상 클래스로 만들어라. 만약 어떤 함수를 호출해야 한다면, 호출 되는 함수를 추상 함수로 만들어라.
일반적으로, 추상 클래스와 인터페이스는 자신에게서 유도된 구체적인 클래스 보다 훨씬 덜 변한다.
이러한 의존성 규칙을 지키기 위해서는 "What data crosses the boundaries", "Crossing boundaries"와 같이 각 계층의 경계에서 어떤 것을 주의해야하는 지를 알아야 한다.
: "경계간의 테이터를 전달할 때 무엇을 전달해야하는가"
: "제어의 흐름은 내부에서 외부로 향할 수 있는데 이는 의존성 규칙을 위배하게 되므로 의존성 역전의 원칙을 이용하여 이를 해결해야한다"
//수정필요
B 클래스에서
A 클래스를 내부에 변수로 사용하게 됨으로써
B 클래스는 A 클래스에 의존관계가 생기게 됩니다.
내부가 아닌 외부에서 객체를 생성해서 넣어주는 것
의존성 주입은 하나의 패턴 : 의존성들을 인자들로 전달해준다면 모듈안에서 의존성들을 불러오거나 새로 만드는 것을 피할 수 있음.
참조 : https://velog.io/@moongq/Dependency-Injection
아래코드는 의존성 주입 전 일반적인 코드이다.
//user-service.js
const User = require('./User_);
const UsersRepository = require('./users-repository);
async function getUsers() {
return UserRepository.findAll();
}
async function addUser(userData) {
const user = new User(userData);
return UserRepository.addUser(user):
}
module.exports = {
getUsers,
addUser
}
위 코드에서 user-service.js는 비즈니스 로직, user repository는 데이터들에 대해 책임지고 있다. 하지만 두가지의 문제점이 있다.
service가 특정 repository와 연결되어있다.
다른 repository로 바꾸게 되면 코드를 모두 수정해야 함 > 확장성이 떨어짐을 의미
모듈의 테스트가 힘들어진다.
테스트를 위해 외부라이브러리를 사용해야함 > 의존성 주입은 외부라이브러리 이용을 최소화 할 수 있게 해줌
아래 코드는 의존성 주입(userRepository를 인자로 전달하는)을 쓴 코드이다.
const User = require('./User);
function UsersService(usersRepository) {
async function getUsers() {
return usersRepository.findAll();
}
async function addUser(userData) {
const user = new User(userData);
return usersRepository.addUser(user);
}
return {
getUsers,
addUser
};
}
module.exports = UsersService
참조 : https://velog.io/@moongq/Dependency-Injection
: 클래스의 생성자(constructor)에 의존성 주입, 개별의존성을 개별인자로 전달하는 것보다 객체로 감싸서 한번에 전달이 좋음
class UsersService {
constructor({ usersRepository, mailer, logger }) {
this.usersRepository = usersRepository;
this.mailer = mailer;
this.logger = logger;
}
async findAll() {
return this.usersRepository.findAll();
}
async addUser(user) {
await this.usersRepository.addUser(user);
this.logger.info(`User created ${user}`);
await this.mailer.sendConfirmationLink(user);
this.logger.info(`Confirmation link sent!: ${user}`);
}
}
module.exports = UsersService;
//ex. userController.js
require('./userService.js)
const usersService = new UsersService({
usersRepository,
mailer,
logger
});
export const userService = ({ usersRepository, mailer, logger }) => {
const fildAll = () => usersRepository.findAll();
addUser = (user) => {
await usersRepository. addUser(user);
logger.info(`User created: ${user}`);
await mailer.sendConfirmationLink(user);
logger.info(`Confirmation link sent ${user}`);
};
return {
findAll,
addUser
};
}
const service = usersService({
usersRepository,
mailer,
logger})
의존성 주입의 단점은 이용하려는 의존성들을 모두 미리 세팅, 구조화 해둬야한다는 것이다.
아래 코드와 같이 userService를 생성하기 전에 의존성들을 모아두는 곳을 흔히 container라고 부른다.
//container.js
const UsersRepository = require('./users-repository');
const Mailer = require('./mailer');
const Logger = require('./logger');
const UsersService = require('./users-service');
const InMemoryDataSource = require('./users-repository/data-source/in-memory');
const logger = new Logger({
level: process.env || 'dev'
});
const dataSource = new InMemoryDataSource();
const mailer = new Mailer({
templates: '/emails',
logger
});
const usersRepository = new UsersRepository({
logger,
dataSource
});
const usersService = new UsersService({
usersRepository,
mailer,
logger
});
module.exports = {
usersService
}
위 코드와 같이 container를 일일이 세팅하는 것이 번거로울 때는 알아서 의존성을 찾고 가져와주는 Awilix
나 TypeDI
와 같은 라이브러리를 사용 할 수도 있다.
추상 클래스, 구현클래스 정의 : https://zetawiki.com/wiki/%EC%B6%94%EC%83%81%ED%81%B4%EB%9E%98%EC%8A%A4,_%EA%B5%AC%EC%B2%B4%ED%81%B4%EB%9E%98%EC%8A%A4
- 추상 클래스 (abstrate class)
: 추상적인 클래스. 인스턴스화 불가능
: ex. 이동수단- 구현 클래스 (concrete class)
: 구체적인 클래스. 인스턴스화 가능
: ex. 트럭, 자전거, 배, 비행기...
https://medium.com/@jang.wangsu/di-dependency-injection-%EC%9D%B4%EB%9E%80-1b12fdefec4f
https://getchan.github.io/til/node_DI/
https://velog.io/@moongq/Dependency-Injection
https://getchan.github.io/til/node_DI/