계층형 설계는 소프트웨어를 여러 추상화 계층으로 나누어 구성하는 아키텍처 패턴입니다. 이를 통해 코드의 재사용성을 높이고 복잡도를 관리할 수 있습니다.
계층형 설계에서 직접 구현은 중요한 원칙 중 하나입니다. 이 원칙은 함수의 시그니처가 나타내는 문제를 함수 본문에서 적절하게 구체화해야 한다는 것을 의미
구분 | 단일 책임 원칙(SRP) | 직접 구현 원칙 |
---|---|---|
범위 | 클래스/모듈의 책임 개수 | 계층 간 의존성 방향 |
목적 | 변경의 이유를 하나로 제한 | 의존성을 한 단계로 제한 |
적용 대상 | 클래스/모듈 레벨 | 아키텍처 레벨 |
중점사항 | 무엇을 하는가 | 어떻게 하는가 |
하위 계층의 코드가 잘못 설계되면 상위 계층까지 연쇄적으로 수정이 필요한 상황이 발생할 수 있습니다.
// Kotlin
// 1. 인터페이스 분리
interface OrderCreator {
fun createOrder(request: OrderRequest): Order
}
interface OrderFinder {
fun findById(id: String): Order?
}
// 2. 의존성 역전
interface OrderRepository {
fun save(order: Order)
fun findById(id: String): Order?
}
class OrderService(
private val orderRepository: OrderRepository // 구체적 구현체가 아닌 인터페이스에 의존
) : OrderCreator, OrderFinder {
override fun createOrder(request: OrderRequest): Order {
val order = Order.from(request)
return orderRepository.save(order)
}
override fun findById(id: String): Order? {
return orderRepository.findById(id)
}
}
// JavaScript
// 1. 인터페이스 분리
class OrderCreator {
createOrder(request) {
throw new Error("Not implemented");
}
}
class OrderFinder {
findById(id) {
throw new Error("Not implemented");
}
}
// 2. 의존성 역전
class OrderService extends OrderCreator {
constructor(orderRepository) { // 인터페이스 주입
super();
this.orderRepository = orderRepository;
}
async createOrder(request) {
const order = Order.from(request);
return await this.orderRepository.save(order);
}
async findById(id) {
return await this.orderRepository.findById(id);
}
}
-- Haskell
-- 1. 인터페이스 분리
class OrderCreator m where
createOrder :: OrderRequest -> m Order
class OrderFinder m where
findById :: String -> m (Maybe Order)
-- 2. 의존성 역전
class OrderRepository m where
save :: Order -> m Order
findById :: String -> m (Maybe Order)
data OrderService m = OrderService {
orderRepo :: OrderRepository m
}
instance (Monad m, OrderRepository m) => OrderCreator (OrderService m) where
createOrder request = do
let order = orderFromRequest request
save (orderRepo order)