Facade pattern
복잡한 하위 시스템들 그리고 단순한 진입점
Facade 패턴은 복잡한 하위 시스템을 단순한 진입점 하나로 감싸서, 호출자에게 필요한 모델만 노출하는 구조다. Android Automotive OS(AAOS)는 이 패턴을 실제로 광범위하게 사용한다. 대표적인 예가 android.car.Car이며, 앱은 이 객체 하나를 통해 오디오, 전원, 사용자, 차량 속성 등 여러 자동차 기능에 접근한다. 이 문서는 Facade 패턴을 실제 서비스 설계에 적용하는 방법을 정리하고, AAOS의 Car.java -> CarPropertyManager -> ICarImpl -> CarPropertyService -> VHAL 흐름을 구체적 사례로 설명한다.
abstract
Facade 패턴은 복잡한 하위 시스템을 단순한 진입점 하나로 감싸서, 호출자에게 필요한 모델만 노출하는 구조다.
Android Automotive OS(AAOS)는 이 패턴을 실제로 광범위하게 사용한다.
대표적인 예가 android.car.Car 이며, 앱은 이 객체 하나를 통해 오디오, 전원, 사용자, 차량 속성 등 여러 자동차 기능에 접근한다.
이 문서는 Facade 패턴을 실제 서비스 설계에 적용하는 방법을 정리하고, AAOS의 Car.java -> CarPropertyManager -> ICarImpl -> CarPropertyService -> VHAL 흐름을 구체적 사례로 설명한다.
abstract
왜 Facade가 필요한가
차량 소프트웨어는 보통 다음 이유 때문에 빠르게 복잡해진다.
itemize leftmargin=1.4em
기능 단위가 많다. 전원, HVAC, 오디오, 사용자, 주행 상태, 속성(property), 진단이 모두 다른 하위 시스템이다.
구현 경계가 다르다. 일부는 앱 프로세스에 있고, 일부는 system service에 있고, 일부는 HAL/VHAL 뒤에 있다.
호출 규칙이 제각각이다. binder, permission, subscription, callback, timeout, cache가 섞인다.
itemize
Facade의 핵심 목적은 이 복잡성을 호출자 바깥으로 밀어내는 것이다.
호출자는 ``무슨 서비스를 어떻게 찾아야 하는가''보다 ``어떤 기능을 호출할 것인가''에 집중해야 한다.
Facade 패턴의 구조
실무에서 Facade는 단순 래퍼가 아니라 진입점 + 서비스 해석 + 수명주기 관리 + 권한/예외 경계 를 함께 가져가는 경우가 많다.
AAOS도 정확히 그 방향으로 설계되어 있다.
figure h
width= aaos-facade-flow.png
AAOS에서 관찰되는 Facade 흐름 예시
fig:aaos-facade-flow
figure
구성 요소
enumerate leftmargin=1.5em
Facade entry : 호출자가 가장 먼저 만나는 얇은 진입점이다.
Subsystem manager : 도메인별 API를 제공한다. 예: CarPropertyManager .
Backend service : 권한 검사, binder endpoint, HAL 연계를 처리한다.
Adapter/translation layer : 외부 모델을 내부 모델로, 혹은 내부 에러를 외부 예외로 변환한다.
enumerate
AAOS 실제 사례: Car.java는 상위 Facade다
AAOS의 android.car.Car 는 최상위 자동차 API 진입점이다.
AOSP 소스 자체가 이를 ``Top level car API''라고 설명하고 있고, 내부적으로는 manager class와 service name의 매핑 테이블을 유지한다 aospcarjava,aospcarlibreadme .
호출 흐름
앱 코드는 보통 아래 흐름으로 시작한다.
lstlisting style=mmrndcode,language=Java,caption= 앱이 보는 Facade 사용 예
Car car = Car.createCar(context);
CarPropertyManager propertyManager =
(CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
lstlisting
이 코드는 단순해 보이지만, 내부에서는 다음이 일어난다.
enumerate leftmargin=1.5em
Car 가 service name과 manager class 매핑을 유지한다.
getCarManager() 가 binder를 mService.getCarService(serviceName) 로 조회한다.
조회된 binder에 맞는 manager를 createCarManagerLocked() 에서 생성한다.
앱은 binder 상세나 서비스 등록 방식 대신 manager API만 사용한다.
enumerate
즉, Car 는 ``차량 기능들의 통합 진입점'' 역할을 수행하는 Facade다.
그 아래의 세부 서비스 구성을 감추고, API 소비자에게 일관된 접근 경로를 제공한다.
왜 이게 Facade인가
이 구조는 단순 service locator와 다르다.
Car 는 다음을 함께 제공한다.
itemize leftmargin=1.4em
manager instance cache
binder 조회 은닉
domain manager 생성 책임
연결 상태와 remote exception 경계 처리
itemize
즉, Car 는 단순히 ``이름으로 객체를 찾아주는 클래스''가 아니라, 앱이 자동차 하위 시스템 전체를 안전하게 쓰게 만드는 상위 Facade다.
CarPropertyManager는 도메인별 Facade다
Car.java 가 상위 Facade라면, CarPropertyManager 는 차량 속성 영역에 특화된 하위 Facade다.
앱은 속도, 기어, 배터리, HVAC, 주행 상태와 같은 다양한 vehicle property를 다룰 때 HAL이나 binder stub를 직접 다루지 않는다.
대신 CarPropertyManager API를 사용한다.
여기서 중요한 점은 서버 측 구현인 CarPropertyService 의 역할이다.
AOSP 주석은 이 클래스를 `` ICarProperty.aidl 의 binder interface 구현이며, 여러 manager가 vehicle properties를 다루기 쉽게 만든다''고 설명한다 aospcarpropertyservice .
즉, CarPropertyService 는 HAL 복잡도를 binder service 경계 안쪽으로 흡수한다.
실제 구현에서 배울 점
itemize leftmargin=1.4em
앱은 property ID와 callback만 본다.
manager는 binder call과 callback 등록 방식을 정리한다.
service는 권한, validation, subscription, HAL 변환을 처리한다.
HAL/VHAL은 마지막 단계에서만 등장한다.
itemize
이렇게 계층을 나누면 API 소비자는 ``차량 속성 조회''라는 의도만 표현하고, 나머지 기술적 복잡도는 Facade 뒤로 숨길 수 있다.
실무에서 Facade를 구현하는 방법
1. 진입점은 작게, 도메인 API는 명확하게
Facade entry는 기능을 직접 다 구현하면 안 된다.
해야 할 일은 아래 정도로 제한하는 것이 좋다.
itemize leftmargin=1.4em
적절한 하위 manager 선택
공통 연결 상태 확인
공통 예외 처리
공통 권한 정책 연결
itemize
도메인 로직은 각 manager나 backend service로 보내야 한다.
Car 가 모든 자동차 기능을 직접 구현하지 않고 CarAudioManager , CarPropertyManager , CarUserManager 로 분리하는 이유도 여기에 있다.
2. Facade가 도메인 모델을 통일해야 한다
Facade 도입의 가장 흔한 실패는 하위 시스템의 API 형태를 그대로 노출하는 경우다.
예를 들어 어떤 서비스는 callback, 어떤 서비스는 polling, 어떤 서비스는 raw binder token을 요구한다면 Facade가 아니라 중계에 가깝다.
좋은 Facade는 다음 질문에 일관되게 답해야 한다.
itemize leftmargin=1.4em
객체는 어떻게 얻는가
실패는 어떤 예외/결과 모델로 표현하는가
subscription과 해제는 어떻게 하는가
lifecycle 종료 시 무엇이 무효화되는가
itemize
3. backend service에서 복잡성을 흡수해야 한다
Facade의 효과는 ``호출자 코드가 짧아진다''에서 끝나지 않는다.
실제 가치의 대부분은 backend service가 validation, throttling, timeout, permission, caching을 흡수할 때 발생한다.
CarPropertyService 가 sync operation 개수를 제한하는 이유도 여기에 있다.
서비스 내부 주석은 binder thread가 모두 소모되면 callback delivery가 막혀 전체 작업이 timeout될 수 있다고 설명한다 aospcarpropertyservice .
이런 제어는 호출자에게 맡기면 안 되고, Facade 뒤쪽에서 흡수해야 한다.
Facade를 도입할 때 체크리스트
enumerate leftmargin=1.5em
외부 호출자가 알아야 하는 최소 개념만 남겼는가
binder, socket, HAL, SQL 같은 transport 세부사항이 새어나오지 않는가
manager 생성과 lifecycle 무효화 정책이 명확한가
예외와 permission 모델이 공통 규칙으로 정리되어 있는가
하위 시스템을 바꿔도 호출자 API를 유지할 수 있는가
enumerate
MMRND 스타일로 적용한다면
MMRND 서비스에서도 Facade는 충분히 유용하다.
예를 들어 하나의 ``VehiclePlatform'' 진입점이 아래 기능을 묶어 노출할 수 있다.
itemize leftmargin=1.4em
vehicle property access
power/session state
diagnostics
policy and permission checks
metrics and tracing
itemize
호출자는 VehiclePlatform.properties().get(...) 같은 API만 사용하고,
실제 CAN/VHAL adapter, cache, retry, permission mapping은 내부 서비스에서 처리하면 된다.
이렇게 하면 앱과 서비스의 결합도가 줄고, 추후 backend transport를 교체해도 상위 API를 유지하기 쉽다.
결론
Facade 패턴의 핵심은 ``단순한 입구'' 자체가 아니라, 복잡성을 어디에 가둘 것인가 에 대한 설계 결정이다.
AAOS의 Car.java 와 CarPropertyManager 는 이 원칙이 실제 대규모 제품 코드에서 어떻게 쓰이는지 잘 보여준다.
itemize leftmargin=1.4em
Car.java 는 상위 Facade다.
각 manager는 도메인별 Facade다.
backend service는 binder/HAL/permission/timeout 복잡성을 흡수한다.
itemize
Facade를 잘 설계하면 API 소비자는 간단해지고, 내부 구현은 더 자유롭게 진화할 수 있다.
그 점에서 AAOS는 Facade 패턴의 교과서적인 실전 사례에 가깝다.
thebibliography 9
aospcarjava
Android Open Source Project,
Car.java ,
https://android.googlesource.com/platform/packages/services/Car/+/master/car-lib/src/android/car/Car.java .
aospicarimpl
Android Open Source Project,
ICarImpl.java ,
https://android.googlesource.com/platform/packages/services/Car/+/master/service/src/com/android/car/ICarImpl.java .
aospcarpropertyservice
Android Open Source Project,
CarPropertyService.java ,
https://android.googlesource.com/platform/packages/services/Car/+/refs/heads/main/service/src/com/android/car/CarPropertyService.java .
aospcarlibreadme
Android Open Source Project,
Car API README ,
https://android.googlesource.com/platform/packages/services/Car/+/refs/tags/aml_sta_331511000/car-lib/README.md .
thebibliography