만들면서 배는 클린 아키텍처를 읽고 공부한 내용을 정리해 보자.
계층형 아키텍처
계층으로 구성된 웹 애플리케이션은 누구나 개발해 본 적 있을 것이다.
계층일 이용하는 사고방식은 컴퓨터 과학 수업이나 튜토리얼, 모범사례를 통해 주입되어 왔다.
사실 계층형 아키텍처는 견고한 아키텍처 패턴이다. 계층을 잘 이해하고 구성한다면 웹 계층이나 영속성 계층에 독립적으로 도메인 로직을 작성할 수 있다. 기존 기능에 영향을 주지 않고 새로운 기능을 추가할 수도 있다. 잘 만들어진 계층형 아키텍처는 선택의 폭을 넓히고 , 변화하는 요구사항과 외부 요인에 빠르게 적용할 수 있게 해 준다. 로버튼 마틴에 의하면 이것이 바로 아키텍처의 전부다(클린 아키텍처)
그렇다면 계층형의 문제점은 무엇일까? 계층형 아키텍처는 코드에 나쁜 습관들이 스며들기 쉽게 만들고 시간이 지날수록 소프트웨어를 점점 더 변경하기 어렵게 만드는 허점들을 노출한다.
데이터베이스 주도 설계를 유도
정의에 따르면 계층형 아키텍처의 토대는 데이터베이스다. 웹 계층은 도메인 계층에 의존하고, 도메인 계층은 영속성 계층에 의존하기 때문에 자연스레 데이터베이스에 의존하게 된다. 모든 것이 영속성 계층을 토대로 만들어진다. 이런 방식은 다양한 이유로 문제를 초래한다.
우리가 만드는 애플리케이션의 대부분의 목적은 무엇인가, 바로 비즈니스를 관장하는 규칙이나 정책을 반영한 모델을 만들어서 사용자가 이러한 규칙과 정책을 더욱 편리하게 활용할 수 있게 한다. 이때 우리는 상태가 아니라 행동을 중심으로 모델링한다. 상태가 중요한 요소이긴 하지만 행동이 상태를 바꾸는 주체이기 때문에 행동이 비즈니스를 이끌어간다.
그렇다면 왜 도메인 로직이 아닌 데이터베이스를 토대로 아키텍처를 만드는 걸까? 그동안 만들어 본 애플리케이션의 유스케이스를 도메인 로직이 아니라 영속성 계층을 먼저 구현했을 것이다. 데이터베이스의 구조를 먼저 생각하고 이를 토대로 도메인 로직을 구현했을 것이다.
계층형 아키텍처에서는 합리적인 방법이다. 의존성에 방향에 따라 자연스럽게 구현한 것이기 때문이다. 하지만 비즈니스 관점에서는 전혀 맞지 않는 방법이다. 먼저 도메인 로직을 만들어야 한다. 그래야 우리가 로직을 제대로 이해했는지 확인할 수 있으며, 도메인 로직이 맞다는 것을 확인한 후에 이를 기반으로 영속성 계층과 웹 계층을 만들어야 한다.
데이터베이스 중심 아키텍처가 만들어지는 가장 큰 원인은 ORM 프레임워크를 사용하기 때문이다. ORM 프레이워크가 나쁘다는 것이 아니라 ORM 프레임워크를 계층형 아키텍처와 결합하면 비즈니스 규칙을 영속성 관점과 섞고 싶은 유혹을 쉽게 받는다.
도메인 계층에서 이러한 영속성 계층 속 엔티티에 접근할 수 있으며 사용되기 마련이다. 이렇게 되면 영속성 계층과 도메인 계층 사이에 강한 결합이 생긴다. 서비스는 영속성 모델을 비즈니스 모델처럼 사용하게 되고 이로 인해 도메인 로직뿐만 아니라 즉시로딩/지연로딩, 데이터베이스 트랜잭션, 캐시 플러시 등등 영속성 계층과 관련된 작업들을 해야만 한다. 영속성 코드가 사실상 도메인 코드에 녹아들어 가서 둘 중 하나만 바꾸는 것이 어려워진다.
지름길을 택하기 쉬워진다.
계층형 아키텍처에서 전체적으로 적용되는 유일한 규칙은, 특정한 계층에서는 같은 계층에 있는 컴포넌트나 아래에 있는 계층에만 접근 가능하다는 것이다. 만약 상위 계층에 컴포넌트에 접근해야 한다면 간단하게 해당 컴포넌트를 계층아래로 내려버리면 된다. 딱 한번 이렇게 하는 것은 괜찮을 수 있지만 보통 영속성 계층은 수년에 걸친 개발과 유지보수로 아래 그림처럼 될 가능성이 높다.
영속성 계층에서는 모든 것에 접근 가능하기 때문에 시간이 지나면 점점 비대해진다. 영속성 계층은 컴포넌트를 아래 계층으로 내릴수록 비대해진다. 어떤 계층에도 속하지 않는 것처럼 보이는 헬퍼 컴포넌트나 유틸리티 컴포넌트들이 이처럼 아래 계층을 내릴 가능성이 큰 후보다.
테스트하기 어려워진다.
계층형 아키텍처를 사용할 때 일반적으로 나타나는 변화의 형태는 계층을 건너뛰는 것이다. 엔티티의 필드를 단 하나만 조작하면 되는 경우에 웹 계층에서 바로 영속성 계층에 접근하면 도메인 계층을 건드릴 필요가 없지 않을까?
도메인 계층을 건너뛰는 것은 도메인 로직을 코드 여기저기에 흩어지게 만든다. 웹 계층 쪽 유스케이스가 확장되는 경우 아무리 간단한 것에 불과하더라도 도메인 로직을 웹 계층에 구현하게 된다. 따라서 애플리케이션 전반에 걸쳐 책임이 섞이고 핵심 도메인 로직들이 퍼져나갈 확률이 높다.
유스케이스를 숨긴다.
기능을 추가하거나 변경할 적절한 위치를 찾는 일이 빈번하기 때문에 아키텍처는 코드를 빠르게 탐색하는 데 도움이 돼야 한다.
하지만 계층형 아키텍처는 말했듯이 도메인 로직이 여러 계층에 걸쳐 흩어지기 쉽다. 이럴 경우 새로운 기능을 추가할 적당한 위치를 찾는 일이 어려워진 상태이다. 계층형 아키텍처는 도메인 서비스의 ‘너비’에 관한 규칙을 강제하지 않기 때문에 시간이 지나면 여러 개의 유스케이스를 담당하는 아주 넓은 서비스가 만들어지기도 한다.
넓은 서비스는 영속성 계층에 많은 의존성을 갖게 되고, 다시 웹 레이어의 많은 컴포넌트가 이 서비스에 의존하게 된다. 그럼 서비스를 테스트하기도 어려워지고 작업해야 할 유스케이스를 책임지는 서비스를 찾기도 어려워진다. 고도로 특화된 좁은 도메인 서비스가 유스케이스 하나씩만 담당하게 한다면 이런 작업들이 얼마나 수월해질까? UserService에서 사용자 등록 유스케이스를 찾는 대신 RegisterUserService를 바로 열어서 작업을 시작하는 것처럼 말이다.
동시 작업이 어려워진다.
계층형 아키텍처는 동시 작업 측면에서는 그다지 도움이 되지 않는다. 계층형 아키텍처에서는 모든 것이 영속성 계층 위에 만들어지기 때문에 특정 기능은 동시에 한 명의 개발자만 작업할 수 있다. 인터페이스를 먼저 같이 정의 후 작업을 할 순 있지만 영속성 로직과 도메인 로직이 뒤섞여서 각 측면을 개별적으로 작업하기 힘들고, 또 코드에 넓은 서비스가 있다면 서로 다른 기능을 동시에 작업하기는 더욱 어렵다.
유지보수 가능한 소프트웨어를 만드는데 어떻게?
물론 계층형 아키텍처도 올바르게 구축하고 몇 가지 추가적인 규칙들을 적용하면 유지보수가 쉬워지며 코드를 쉽게 변경하거나 추가할 수 있다. 하지만 앞에서 살펴봤듯이 잘못된 방향으로 흘러가도록 쉽고 용인하는 구조이다.
따라서 계층형 아키텍처로 만들든 다른 아키텍처 스타일로 만들든, 계층형 아키텍처의 문제점들을 염두에 두면 지름길을 택하지 않고 유지보수하기에 더 쉬운 솔루션을 만드는 데 도움이 될 것이다.
'design & development' 카테고리의 다른 글
[만들면서 배우는 클린 아키텍처] 3. 코드 구성하기 (1) | 2024.11.14 |
---|---|
[만들면서 배우는 클린 아키텍처] 2. 의존성 역전하기 (4) | 2024.11.13 |
DDD? 클린 아키텍처? 헥사고날 아키텍처? (1) | 2024.11.11 |
캐시 전략 (1) | 2024.10.30 |
[디자인 패턴] 템플릿 메소드 패턴 (0) | 2024.07.18 |