소프트웨어의 유연성을 위한 의존 관계 제어 - 「도메인 주도 설계 철저 입문」 7장
본 포스트 시리즈는 「도메인 주도 설계 철저 입문」책을 요약한 내용입니다.
이전 발행 글 보기
2024.09.02 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 1장
2024.09.04 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 2장 (1)
2024.09.05 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 2장 (2)
2024.09.09 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 3장 (1)
2024.09.09 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 3장 (1)
2024.09.11 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 3장 (2)
2024.09.30 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 4장
2024.09.30 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 5장 (1)
2024.09.30 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 5장 (2)
2024.09.30 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 5장 (3)
2024.10.05 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 6장 (1)
2024.10.05 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 6장 (2)
2024.10.06 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 6장 (3)
2024.10.06 - [DDD/도메인 주도 설계 철저 입문] - 도메인 주도 설계란? - 「도메인 주도 설계 철저 입문」 6장 (4)
07장 소프트웨어의 유연성을 위한 의존 관계 제어
소프트웨어 유연성을 위한 2단계
7장에서는 소프트웨어를 유연하게 만들기 위해서 1. DIP를 적용하고 2. IoC 패턴을 적용하는 방법을 설명한다.
DIP(Dependency Inversion Principle)란?
의존 관계 역전 원칙은 Robert Martin이 발명한 객체 지향적인 설계 원칙 중 하나다. “High-level 모듈이 Low-level 모듈에 의존하지 않는다. 둘 다 abstraction에 의존한다”는 원칙이다.
High-level과 Low-level이란?
High-level 모듈이란 다른 모듈에 의존하는 모듈을 말하고, Low-level 모듈은 다른 모듈이 의존하는 모듈을 말한다.
DIP의 적용
아래 코드에 드러나는 모듈들(클래스들)의 의존 관계를 그림으로 나타내면 다음과 같다.
public class UserApplicationService {
private readonly IUserRepository userRepository;
public UserApplicationService(IUserRepository userRepository){
this.userRepository = userRepository;
}
(...생략...)
}
High-level 모듈인 `UserApplicationService`가 Low-level 모듈인 `UserRepository`에 직접 의존하고 있으므로 DIP에 어긋난다. 둘 다 추상에 의존해야 한다. 즉, 인터페이스에 의존해야 한다. 수정하면 다음과 같다.
public class UserApplicationService {
private readonly IUserRepository userRepository;
public UserApplicationService(IUserRepository userRepository) {
this.userRepository = userRepository;
}
(...생략...)
}
IoC Container 패턴
IoC Container(DI Container라고도 부른다)는 의존성을 주입해주는 컨테이너를 말한다. 예를 들어 위 예시에서`UserApplicationService`는 `IUserRepository`를 필요로 한다. 이때 서비스가 리포지토리를 스스로 만들어 사용하는 게 아니라, IoC 컨테이너가 주입해주는 것이다.
생성자를 통한 주입
생성자를 통한 의존성 주입이란, 생성자의 인자로 필요한 객체를 받도록 구현하는 것을 말한다. `UserApplicationService`를 다음과 같이 구현하면 IoC 컨테이너로부터 의존성을 주입 받을 준비가 된 것이다.
public class UserApplicationService {
private readonly IUserRepository userRepository;
// 필요한 객체를 생성자의 인자로 받게 되어 있다.
public UserApplicationService(IUserRepository userRepository) {
this.userRepository = userRepository;
}
(...생략...)
}
IoC Container의 구현
IoC Container는 다음과 같이 구현할 수 있다.
import java.util.HashMap;
import java.util.Map;
class IoCContainer {
private Map<Class<?>, Object> container = new HashMap<>();
// 객체 등록
public <T> void register(Class<T> interfaceType, T implementationInstance) {
container.put(interfaceType, implementationInstance);
}
// 의존성 주입된 객체 반환
@SuppressWarnings("unchecked")
public <T> T resolve(Class<T> interfaceType) {
return (T) container.get(interfaceType);
}
}
느슨한 결합을 위해 모듈이 인터페이스(추상)에 의존하도록 인터페이스(key) : 구현체(value)를 저장해두고 있다. 인터페이스별로 구현체를 등록해두었다가, 해당 인터페이스에 의존하는 객체가 resolve를 요청하면, 등록된 구현체를 건네주는 것이다. IoC Container를 사용하는 방법은 다음과 같다.
public class Main {
public static void main(String[] args) {
// IoC Container 생성
IoCContainer container = new IoCContainer();
// 의존성 객체 등록
container.register(IUserRepository.class, new InMemoryUserRepository());
container.register(UserApplicationService.class, new UserApplicationService(container.resolve(IUserRepository.class)));
// IoC Container를 통해 필요한 인스턴스를 받아옴
UserApplicationService userService = container.resolve(UserApplicationService.class);
// 비즈니스 로직 실행
userService.registerUser("Alice");
}
}
`userService`는 인터페이스 `IUserRepository`에 의존하고, IoC Container로 부터 `IUserRepository`의 구현체인 `InMemoryUserRepository`를 주입 받는다. 이렇게 구현하는 경우 서비스 코드 수정 없이도 테스트를 위해 `MockUserRepository`를 주입할 수 있게 된다.
요약
1. DIP는 High-level 모듈이 Low-level 모듈에 의존하는 것이 아니라, 둘 다 추상에 의존해야 한다는 뜻이다. 2. IoC Container를 통해 의존성을 주입받을 수 있으면 느슨한 결합을 달성할 수 있다. |