지금 진행 중인 프로젝트는 코어 모듈에 도메인별로 기능을 구현하고, 사용자 애플리케이션과 어드민에서 코어 모듈을 import 해 오픈된 인터페이스를 통해 도메인의 기능을 사용하게끔 구성했습니다. 이를 Onion Architecture라고 하는데, Onion Architecture는 다음 포스팅에서 다뤄보겠습니다.
이번 포스팅에서는 어드민을 개발하면서 동료 개발자분과 어드민 백엔드를 어떻게 구성할지 논의하면서 Facade Pattern과 API Composition의 차이가 궁금해져 알아보았습니다.
Facade Pattern
Facade Pattern은 디자인 패턴 중 하나로 복잡한 서브 시스템 혹은 서비스들을 간략한 인터페이스로 감싸서 클라이언트에 제공해줍니다.
Facade Pattern에서 클라이언트는 Facade의 인터페이스만을 알고 있고, 접근 가능하며 서브 시스템 혹은 서비스들을 알지 못합니다. 때문에 클라이언트의 코드는 간결해지고 알기 쉬워지며, 서브 시스템 혹은 서비스에 대한 의존성을 낮출 수 있습니다.
아래 예시로 Facade Pattern를 적용하기 전,후 코드를 비교해보도록 하겠습니다.
public class domainAService {
public void foo() { ... }
}
public class domainBService {
public void bar() { ... }
}
public class domainCService {
public void baz() { ... }
}
위와 같이 각 도메인별 서비스가 존재할 때, Facade Layer 없이 클라이언트에서 직접 도메인별 서비스를 호출하는 경우는 아래와 같습니다.
public class Client {
public final DomainAService domainAService;
public final DomainBService domainBService;
public final DomainCService domainCService;
public void doSomething() {
domainAService.foo();
domainBService.bar();
domainCService.baz();
}
}
만약 Facade Layer가 아래와 같이 추가된다면, 클라이언트의 코드는 좀 더 간단해질 것입니다.
public class Facade {
public final DomainAService domainAService;
public final DomainBService domainBService;
public final DomainCService domainCService;
public void doSomething() {
domainAService.foo();
domainBService.bar();
domainCService.baz();
}
}
public class Client {
public final Facade facade;
public void doSomething() {
facade.doSomething();
}
}
예제에서는 워낙 코드가 간단하기 때문에 확 와닿지는 않겠지만, 호출해야 하는 서비스가 늘어나고, 각 서비스의 응답 값을 다음 서비스 호출 시 활용하고, 여러 데이터가 조합된 응답 객체를 리턴해야 한다고 가정한다면 클라이언트에 그 코드를 작성하기에는 확실히 부담이 될 것입니다.
API Composition (API Aggregation)
MSA가 주목을 받으면서 서비스 간의 통신, API 호출이 증가했습니다. 서비스 간 API 호출 빈도가 높아짐에 따라 자연스레 오버헤드가 생기게 되는데, 서비스가 잘게 나뉘어질수록 클라이언트에서 호출해야 하는 API가 많아지면서 호출 빈도도 높아지고 클라이언트도 길어지는 코드에 부담을 느끼게 됩니다.
만약, 클라이언트에서 하나의 API만을 호출해서 잘게 쪼개진 API들의 응답을 모아서 받을 수 있다면 어떨까요?
API Composition이 바로 그런 역할을 합니다.
클라이언트에서 API Composer로 요청을 하면 API Composer에서 각각의 마이크로 서비스에 필요한 요청을 보내고, 응답값을 메모리에서 조인해 클라이언트에 반환합니다. 이렇듯 API Composition은 클라이언트 입장에서 간단하게 필요한 데이터를 쿼리 할 수 있다는 장점이 있지만, 인메모리 조인이기 때문에 대용량 데이터 조회 시 비효율적일 수 있다는 단점이 있습니다. 보통 대용량 데이터 조회 시에는 API Composition보다는 CQRS Pattern을 사용하는 것 같습니다.
API Composition의 위치
API Composition의 위치 또한 중요합니다. API Gateway 기준으로 앞에 있냐, 뒤에 있냐, 내부에 있냐에 따라 장단점이 모두 다르기 때문입니다.
API Gateway 앞에 있을 때
- Client → API Composition → API Gateway → Micro Services
- API Composition이 Client의 End point가 되어 모든 요청(composition이 필요하든 아니든)이 API Composition을 거쳐야하기 때문에 트래픽 부담이 높습니다.
- Composition의 필요 여부에 상관 없이 무조건 통과해야 합니다.
API Gateway 내부에 있을 때
- Client → API Gateway(API Composition) → Micro Services
- Gateway 내부에 존재하기 때문에 Network Hop이 존재하지 않습니다.
보안성 강화를 위해 TTL에서는 Hop 수를 제한하고, 그 기준치를 넘어가는 패킷은 처리하지 않기 때문에 서비스의 라우팅이 복잡하다면 고려할만한 이점입니다. - Gateway 구현이 복잡해지며 Composition 때문에 Gateway의 성능에 악영향을 줄 수 있습니다.
- Composition 로직이 변경되면 API Gateway를 배포&재구동 해야 하기 때문에 서비스에 영향을 줄 수 있습니다.
API Gateway 뒤에 있을 때
- Client → API Gateway → API Composition / Micro Services
- 가장 구성이 쉽습니다.
- API Composition이 하나의 Micro service가 되는 것과 같습니다.
마치면서
마치면서 DDD를 기반으로 Facade Pattern과 API Composition을 비교해보도록 하겠습니다.
앞서 지금 개발 중인 프로젝트가 Onion Architecture로 설계되었다고 말씀을 드렸었는데요, 코어 모듈을 import 하는 애플리케이션에서는 코어 모듈의 도메인별 인터페이스를 참조하게끔 되어있습니다.
그중에서 비즈니스 로직이 여러 도메인에 걸쳐있는 경우 Facade Layer를 추가해 비즈니스 로직을 구현했었는데, 어드민 개발을 위한 설계 중 일부 조회 메뉴에서 여러 도메인의 데이터를 조합해 조회하는 기능이 존재해 API Composition에 대해 알아보게 되었습니다.
그리고 아래와 같이 Facade와 API Composition을 비교 정리해보았습니다.
Facade
- 서로 다른 Aggregate를 조합하는 레이어
- 하나의 마이크로 서비스/BFF 안에 포함됩니다.
API Composition
- 서로 다른 Bounded Context를 조합하는 레이어
- 마이크로 서비스와 별개로 존재하며, 여러 개의 마이크로 서비스와 통신합니다.
References
'🏗️ ARCHITECTURE' 카테고리의 다른 글
[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 7장 : 분산 시스템을 위한 유일 ID 생성기 설계 (1) | 2024.11.18 |
---|---|
Onion Architecture란? (0) | 2022.03.20 |
[도메인 주도 설계] 2부 : 모델 주도 설계의 기본 요소 (0) | 2022.03.06 |
[도메인 주도 설계] 1부 : 동작하는 도메인 모델 만들기 (0) | 2022.02.01 |
[12 Factor App] 마이크로서비스 개발 전 읽어보세요 (0) | 2021.06.07 |