무늬만 계층형 아키텍처

“DAO 단에서 화면에 필요한 데이터를 미리 다 준비해서 가져온다.”

Dao, Service, Controller가 별개의 클래스로 구성되어 있고 또 인터페이스까지 만들어 놨다고 해서 계층형 아키텍처를 지키고 있는 아니다.

나도 잘 몰랐거나 오해했던 내용을 사부님과 이야기 나누며 다시 개념을 정리하고 있다. 이야기의 시작은 OSIV 패턴을 사용하는게 게층형 아키텍처를 위반한 것이냐 아니냐 였다. 나는 위반했다는 입장이었고, 사부님은 아니라는 것이었다. 결론부터 말하자면 내가 틀렸다.

우선 나의 생각은 이랬다. 어떤 블로거의 글처럼, 뷰 단에서 DB 쿼리가 발생할 수 있으니 DAO 계층에서 할 일을 뷰 단에서 한 것이나 마찬가지이고, 그래서 계층형 아키텍처가 깨졌다고 주장했다.

하지만 사부님은 간단하게 반론을 제기했다. 그렇다면 Transparent Persistence(TP) 기술 자체가 계층형 아키텍처를 위반한 것이라는 건데, 예를 들어 서비스 계층에서 Lazy loading하는 것도 그럼 계층형 아키텍처 위반이냐?

오히려 내 생각에 반전을 일으키는 말씀을 하셨다. TP 때문에 오히려 계층형 아키텍처가 굳건해 진다는 것이다. TP가 없었다면 오히려 도메인 객체만 전달하는게 아니라, 서비스나 컨트롤러 혹은 뷰 단에서 필요한 데이터를 전달하게 되는 것이고, 그렇게 되면 계층 간에 필요한 데이터가 무엇인지 분명히 알고 있어야 하기 때문에 그로인한 결합도가 생긴다는 것이다. 맞는 말이다. 계층 간에 주고받는 데이터가 구체적일 수록 변경에 민감하게 반응하게 될 것이다. 결국 겉으로만 분리되어 있고 실제로는 데이터를 중심으로 강하게 결합된 구조가 된다. 화면에서 보여줄 데이터가 바뀌면 결국 컨트롤러, 서비스, DAO 전부 바뀌게 되는 것이다. 그런데 JPA를 사용하면 얼마든지 TP가 가능하기 때문에 도메인 객체만을 주고 받게 되고, 그렇게 되면 데이터 중심의 개발 방법에 비해서 변경될 여지가 적다. 결국 유지보수성이 뛰어나다.

결국 TP는 계층형 아키텍처의 취지에 완벽하게 부합하는 것이다. 그리고 OSIV는 TP를 뷰 랜더링 시점까지로 연장시켜줄 뿐, 어차피 거의 기본적으로 서비스 계층의 오퍼레이션 마다 TP가 적용되고 있다. 그리고 애초에 뷰단에서 쿼리가 날아갔을 때 뷰단에서 JDBC Connection 객체를 가지고 코딩한 것도 아니고 그저 도메인 객체 그래프를 가지고 네비게이션 했을 뿐 아닌가? 코드 레벨에서 전혀 계층형 아키텍처를 위반하지도 않았다.

이야기는 계속 이어져서, 데이터 중심 개발 방법과 도메인 중심 개발 방법의 차이점과 특징에까지 이르렀다. 그리고 도메인 중심 개발 방법에서도 도메인 계층을 벗어날 때 DTO를 사용하자는 집단과 그러지 말고 그냥 도메인 객체 그대로 쓰자는 집단의 주장과 흐름까지도 이야기를 들었다.

“화면에서 어떤 데이터를 보여줄지 DAO가 다 알고 그에 맞게 SQL을 미리 다 짠다면 이미 DAO가 뷰 단과 강한 결합이 생겼다는 증거다. 이런건 계층형 아키텍처가 아니다.”

데이터 중심 개발을 하면 위에서 예를 든 것처럼 계층간의 결합도가 높아서 변경될 여지가 많다. 하지만 모든 계층에서 도메인 객체를 사용한다면 이야기는 달라진다. 모든 데이터는 이미 도메인 객체에 들어가서 메모리에 들어가 있다는 가정하에 도메인 객체를 네비게이션 하면서 비즈니스 로직을 처리하고 뷰에서 랜더링을 하는 것이다. 따라서 필요한 데이터가 도메인 객체 네비게이션으로 참조할 수 있는 한도 내에서는 전혀 코드를 변경할 필요가 없다.

“DAO, Service, Controller, 뷰를 전부 도메인 모델만 공유한 상태에서 각자 다른 팀에서 개발한다고 가정하고 코딩을 하면 도메인 중심 개발 방법에 가장 잘 맞는 형태의 코드가 나올 것이다.”

결국 OSIV로 얻을 수 있는 장점은 다음과 같다.

1. 개발 속도 향상

2. 이상적인 계층 구조 확보

3. 적절한 엔티티 캐시 활용

그러나 전제가 더 중요하다. “도메인 중심 개발 방법”을 적용했을 때에야 이런게 의미가 있지, 앞서 말한것처럼 “데이터 중심 개발 방법”을 사용하면서 하이버네이트나 OSIV를 사용하겠다는 것은 권장하지 못할 시도인것 같다. 그래가지고는 오히려 하이버네이트에 대한 악담과 비화만 늘어날 뿐이다.

사부님이 권장하는 하이버네이트 개발 스타일은 다음과 같다.

1. Entity 캐싱을 하고 join fetch 없이 개발

2. 매번 같이 다니는 연관 관계면 아에 매핑을 eager fetching으로 튜닝

3. 가끔씩 같이 다닌다면 런타임시 join fetch

도메인 중심 개발을 할 때 DTO를 쓰자는 입장은 화면단에서 도메인 객체에 setter 호출로 데이터를 조작할 수 있다는 것을 위험하다고 여겨서 함수형 언어에서 주로 사용하는 immutable한 객체를 만들어 넘기도록 DTO를 권장했다. 심지어 OSIV를 사용하는 경우에는 PT에서 도메인 객체를 수정하면 DB에 update 쿼리가 날아가기까지 했었다. 하지만.. 아드리안 콜리어가 등장해서 Aspectj 한줄로 뷰단에서는 setter 호출 불가, DAO 호출 불가 정책을 만들어 적용할 수 있다는 사실을 전파하자. 요즘은 DTO 사용하자는 집단이 조용해짐. 그냥 도메인 객체를 여러곳에서 사용하는 것으로… 고고씽…

위의 내용은 토비의 스프링 9장에 잘 나와있기도 하다. 722페이지부터 읽어보면 될듯하다. 이미 책으로 써주셨는데, 채팅으로 저자 직강을 듣고나니 감회가 새롭다. 문제다. @_@;; 점점 바보가 되가나봐…

[DDD] Whiteship’s DDD 아키텍처

기대는 하지 마시구요. @_@ 기존 틀(빈약한 도메인 객체 + 두꺼운 서비스 계층)에서 벗어난다는게 이렇게 머리 아픈 일인지 몰랐습니다. DDD 공부라도 제대로 했었어야 하는데.. 역시 개념없는 코드와 코드 없는 개념은 아무것도 아닌 것 같아요. 힘 없는 정의와 정의없는 힘처럼… 각설하고.. 오늘 구현해볼 아키텍처를 그려봤습니다.

일단 Web 계층에서는 Application 계층에 있는 Thin Facade 내지 서비스를 호출할 겁니다.

Thin Facade는 도메인 객체로 대부분의 역할을 위임할 겁니다.

도메인 계층에서는 Thin Facade로 부터 어떤 일을 위임 받은 도메인 객체는 비즈니스 로직을 담당할 것이고
– 컬렉션에 저장, 조회 등의 작업을 할 때는 계층에 위치한 Repository를 이용할 겁니다. 여기서 Repository는 DAO와는 개념이 다르다(참조: http://aeternum.egloos.com/1160846)는 것에 주의해 주세요.
– Repository는 모든 Entity가 아닌, Entry Point에 해당하는 Entity에 대한 것만 만들 겁니다.
– Email, JMS 등의 기능이 필요하다면 Infrastructure에서 제공하는 서비스를 이용할 겁니다.

시나리오 1: User


UserController가 요청을 받으면 UserFacade가 이 요청을 받아서 User의 특정 메서드를 호출하고, User는 UserRepository를 사용하여 원하는 작업을 한다.

시나리오 2: User->Address


위 상황에서 Value 타입인 Address가 추가 됐을 때 모습으로 별반 달라진 건 없음. Value 타입이 Entity 타입과 어떤 차이가 있는지 보여주는 시나리오.

시나리오 3: User->Address, Order


Order라는 새로운 Entity이자 Entry Type이 추가되자 Controller, Facde, Repository, Dao가 추가된다. User와 Order는 양방향 관계로 서로가 서로를 참조한다. 비즈니스 요구사항에 따라 방향성은 달라질 수 있다. 또한 현재는 각자가 자신의 Repository만 참조하고 있는데, 이 모습도 비즈니스 요구사항에 따라 달라질 수 있다. 중요한 건, 비즈니스 요구사항에 따라 도메인 객체만 변경하면 될 뿐, 나머진 그대로라는 것이다.

주의 할 것은 전체 아키텍처에서도 그렸듯이 도메인 계층에서 Application 계층을 참조하지 않는다는 것이다. Application 계층을 참조할 일이 있다는 것은 도메인 계층이 다시 Application 계층으로 일을 넘긴다는 것인데, 그렇게 되면 Thin Facade라는 가정이 어긋나게 되고 비즈니스 로직이 Application 계층쪽으로 새어나가게 될 것이다.

시나리오 4: User->Address, Order->OrderLine


이번에는 새로운 Entity OrderLine을 추가했다. 하지만 이 Entity는 Entry Point가 아니라 Order에 종속된 생명주기를 지나고 있다. 따라서 Order가 OrderLine까지 같이 관리하며 OrdeLineRepository는 만들지 않는다. 대신 Infrastructure에 OrderLineDao를 추가하여 OderRepository가 OrderLine을 DB에 넣을 때 이용할 수 있게 도와준다.

이제 구현해보는 일만 남았군요. 캬~~ 코딩하기 전에 그림 그려보는 것도 좀 도움이 되네요. 코딩하면서 계속 디자인을 바꾸니깐.. 테스트고 뭐고.. @_@ 일단 저렇게 큰그림 잡고 조금씩 테스트 해 가면서 기왕이면 TDD로 ㄱㄱ 해보렵니다! 파이팅!! 오늘은 이거 못 만들면 퇴근 못하니깐…ㄷㄷㄷㄷㄷ… 내일 스터디는 밤새고 가야할지도…??? 헤헷 후딱 하고 퇴근해버려야지~