본문 바로가기
Architecture

[Architecture] 1. 계층형 아키텍처의 문제

by jn4624 2024. 4. 9.
반응형
'만들면서 배우는 클린 아키텍처' 책을 통해 클린 아키텍처를 공부하면서 알게된 내용을 기록하고 정리하기 위해 시리즈를 작성하려고 한다.
해당 챕터에서는 사내 솔루션의 아키텍처인 계층형 아키텍처의 문제에 대해 이야기한다.

 

1. 계층형 아키텍처의 문제는 무엇일까?

웹, 도메인, 영속성 계층으로 구성된 전통적인 웹 어플리케이션 구조

 

일반적인 3계층 아키텍처는 웹 계층에서 요청을 받아 도메인 혹은 비즈니스 계층에 있는 서비스로 요청을 보내 필요한 비즈니스 로직을 수행하고, 도메인 엔티티의 상태 조회 혹은 변경을 위해 영속성 계층의 컴포넌트를 호출한다.

 

계층형 아키텍처는 견고한 아키텍처 패턴이다.

계층을 잘 이해하고 구성한다면 웹 계층이나 영속성 계층에 독립적으로 도메인 로직 작성이 가능하고, 도메인 로직에 영향을 주지 않고 웹 계층과 영속성 계층에 사용된 기술의 변경도 가능하며, 기존 기능에 영향을 주지 않고 새로운 기능 추가도 가능하다.

 

잘 만들어진 계층형 아키텍처는 선택의 폭을 넓히고, 변화하는 요구사항과 외부 요인에 빠르게 적응할 수 있게 해준다.

 

그렇다면 계층형 아키텍처의 문제점은?

계층형 아키텍처는 코드에 나쁜 습관들이 스며들기 쉽고, 시간이 지날수록 소프트웨어를 점점 더 변경하기 어렵게 만드는 수 많은 허점들을 노출한다.

 

2. 데이터베이스 주도 설계 유도

전통적인 계층형 아키텍처의 토대는 데이터베이스다.

웹 계층은 도메인 계층에 의존하고, 도메인 계층은 영속성 계층에 의존하기 때문에 자연스레 데이터베이스에 의존하게 된다.

 

우리가 만드는 대부분의 어플리케이션 목적은?

비즈니스를 관장하는 규칙이나 정책을 반영한 모델을 만들어 사용자의 편리성 증대를 목적으로 한다.

이를 위해 우리는 상태가 아니라 행동을 중심으로 모델링하고, 행동이 비즈니스를 주도한다.

 

도메인이 아닌 데이터베이스가 토대인 이유?

우리는 그동안 데이터베이스의 구조를 먼저 생각하고, 도메인 로직을 구현했다.

이는 의존성의 방향에 따라 자연스럽게 구현한 것이기 때문에, 전통적인 계층형 아키텍처에서는 합리적인 방법이다.

 

하지만 비즈니스 관점에서는 비합리적인 방법이다.

다른 무엇보다도 도메인 로직을 먼저 구현해야 로직을 제대로 이해했는지 확인할 수 있고, 확인한 후에 이를 기반으로 영속성 계층과 웹 계층을 만들어야 한다.

 

데이터베이스 중심 아키텍처 생성의 가장 큰 원인

ORM(object-relationalmapping, 객체 관계 매핑) 프레임워크를 사용하기 때문이다.

ORM 프레임워크를 계층형 아키텍처와 결합하면 비즈니스 규칙을 영속성 관점과 섞고 싶은 유혹을 쉽게 받는다.

 

영속성 계층과의 강한 결합을 유발하는 도메인 계층에서의 데이터베이스 엔티티 사용

 

ORM에 의해 관리되는 엔티티는 일반적으로 영속성 계층에 둔다.

계층은 아래 방향으로만 접근이 가능하기 때문에 도메인 계층에서 엔티티에 접근이 가능해지고 엔티티에 접근이 가능해지면 분명 사용되기 마련이다.

이렇게 되면 영속성 계층과 도메인 계층 사이에 강한 결합이 유발된다.

 

서비스는 영속성 모델을 비즈니스 모델처럼 사용하게 되고, 이로 인해 도메인 로직뿐 아니라, 즉시 로딩(eager loading) / 지연 로딩(lazy loading), 트랜잭션, 캐시 플러시(flush) 등 영속성 계층 관련 작업을 처리해야만 한다.

 

영속성 코드가 사실상 도메인 코드에 녹아들어가 둘 중 하나만 변경하는 것이 어려워진다. 이는 유연하고 선택의 폭을 넓혀준다던 계층형 아키텍처의 목표와 정확히 반대되는 상황이다.

 

3. 쉬운 지름길 선택

전통적인 계층형 아키텍처에서 전체적으로 적용되는 유일한 규칙은,

특정한 계층에서는 같은 계층에 있는 컴포넌트나 아래에 있는 계층에만 접근이 가능하다는 것이다.

 

만약 상위 계층에 위치한 컴포넌트에 접근해야 한다면, 컴포넌트를 계층 아래로 내려버리면 접근이 가능하게 되고 문제도 깔끔히 해결된다.

하지만 이는 개발자들이 이러한 지름길 모드를 키도록 유발하게 된다.

 

비대함을 유발하는 영속성 계층의 접근성

 

영속성 계층(일반적인 용어로 최하단 계층)은 컴포넌트를 아래 계층으로 내릴수록 비대해지며, 어떤 계층에도 속하지 않은 것처럼 보이는 헬퍼 컴포넌트나 유틸리티 컴포넌트들이 아래 계층으로 내려질 가능성이 큰 후보들이다.

 

지름길 모드를 끄고 싶다면?

적어도 추가적인 아키텍처 규칙을 강제하지 않는 한 계층은 최선의 선택은 아니다.

여기서 '강제한다'란, 시니어 개발자의 코드 리뷰를 의미하는 것이 아니라, 해당 규칙이 깨졌을 때 빌드가 실패하도록 만드는 규칙을 의미한다.

 

4. 어려운 테스트

계층형 아키텍처를 사용할 때 일반적으로 나타나는 변화의 형태는 계층을 건너뛰는 형태이다.

엔티티의 필드를 단 하나만 조작하면 되는 경우, 웹 계층에서 바로 영속성 계층에 접근하면 도메인 계층을 건드릴 필요가 없다.

 

도메인 로직을 코드 여기저기 흩어지게 만드는 계층을 건너뛰는 형태

 

이런 일이 자주 일어난다면 두가지의 문제점이 발생한다.

 

첫번째

단 하나의 필드를 조작하는 것에 불과하더라도 도메인 로직을 웹 계층에 구현하게 된다.

만약 유스케이스가 확장된다면 더 많은 도메인 로직을 웹 계층에 추가하여 애플리케이션 전반에 걸쳐 책임이 섞이고 핵심 도메인 로직들이 퍼져나갈 확률이 높다.

 

두번째

웹 계층 테스트시 도메인 계층뿐만 아니라 영속성 계층의 모킹(mocking)이 일어나 단위 테스트의 복잡도가 올라간다.

이렇게 되면 웹 컴포넌트의 규모가 커지면 다양한 영속성 컴포넌트에 의존성이 많이 쌓이면서 테스트의 복잡도를 높여 테스트 코드를 작성하는 것보다 종속성을 이해하고 목(mock)을 만드는데 더 많은 시간을 소요하게 된다.

 

5. 숨어버린 유스케이스

개발자들은 새로운 유스케이스를 구현하는 새로운 코드를 짜는 것을 선호하지만 실제로는 새로운 코드를 짜는데 시간을 소요하기 보다는 기존 코드를 바꾸는데 더 많은 시간을 소요한다.

기능을 추가하거나 변경할 적절한 위치를 찾는 일이 빈번하기 때문에 아키텍처는 코드를 빠르게 탐색하는데 도움이 되어야 한다.

 

이런 관점에서 계층형 아키텍처의 문제는?

계층형 아키텍처에서는 도메인 로직이 여러 계층으로 흩어지기 쉽다.

이럴 경우 새로운 기능을 추가할 적당한 위치를 찾는 일은 이미 어려워진 상태다.

또한 계층형 아키텍처는 도메인 서비스의 '너비'에 관한 규칙을 강제하지 않기 때문에 여러 개의 유스케이스를 담당하는 아주 넓은 서비스가 만들어질 우려도 있다.

 

특정 유스케이스를 찾기 어렵게 만드는 넓은 서비스

 

넓은 서비스는 영속성 계층에 많은 의존성을 갖게 되고, 웹 레이어의 많은 컴포넌트가 이 서비스에 의존하게 된다.

이로 인해 서비스를 테스트하기 어려워지고, 작업해야 할 유스케이스를 책임지는 서비스를 찾기도 어려워진다.

 

6. 어려운 동시 작업

특정 날짜까지 소프트웨어가 완성되어야 한다는 것은 여러 작업을 동시에 해야 한다는 것을 의미한다.

'맨먼스 미신'을 읽지 않았더라도 아래의 유명한 결론은 알 것이다.

 

"지연되는 소프트웨어 프로젝트에 인력을 더하는 것은 개발을 늦출 뿐이다."
- 맨머스 미신: 소프트웨어공학에 관한 에세이, 프레더릭P.브룩스 -

 

모든 상황에서 50명 정도 되는 큰 규모의 개발팀이 10명 정도 되는 작은 규모의 개발팀보다 5배 빠를 거라고 기대할 수 없다.

하지만 적절한 규모에서는 프로젝트에 인원이 더 투입될 경우 확실히 더 빨라진다고 기대할 수 있다.

 

이러한 기대를 충족시키려면 아키텍처가 동시 작업을 지원해야 하지만 이렇게 하기란 쉽지 않을 뿐더러 계층형 아키텍처는 이런 측면에서는 그다지 도움이 되지 않는다.

 

계층형 아키텍처는 왜 도움이 되지 않는가?

계층형 아키텍처는 모든 것이 영속성 계층 위에 만들어지기 때문에 영속성 계층을 먼저 개발해야 하고, 그 다음으로 도메인 계층을, 마지막으로 웹 계층을 만들어야 한다. 그렇기 때문에 특정 기능은 동시에 한 명의 개발자만 작업이 가능하다.

 

인터페이스를 먼저 정의하고, 실제 구현을 기다릴 필요 없이 인터페이스를 토대로 작업하면 된다고 이야기할 수도 있지만, 이는 데이터베이스 주도 설계를 하지 않는 경우에만 가능하다. 데이터베이스 주도 설계는 영속성 로직이 도메인 로직과 뒤섞여 각 측면을 개별적으로 작업할 수 없다.

 

코드에 넓은 서비스가 있다면 서로 다른 기능을 동시에 작업하기 더욱 어렵다. 서로 다른 유스케이스에 대한 작업을 하게 된다면 같은 서비스를 동시에 편집하는 상황이 발생하고, 병합 충돌(merge conflict)과 잠재적으로 이전 코드로 돌려야 하는 문제를 야기하기 때문이다.

 

7. 결론

올바르게 구축하고 추가적인 규칙들을 적용하면 계층형 아키텍처는 유지보수가 쉬워지며 코드를 변경하거나 추가할 수 있게 된다.

하지만 계층형 아키텍처는 많은 것들이 잘못된 방향으로 흘러가도록 용인한다.

 

계층형 아키텍처의 함정을 염두에 두면 지름길을 택하지 않고 유지보수하기에 더 쉬운 솔루션을 만드는데 도움이 될 것이다.

 

Reference.

만들면서 배우는 클린 아키텍처

https://wikibook.co.kr/clean-architecture-ebook/

 

만들면서 배우는 클린 아키텍처 (ebook): 자바 코드로 구현하는 클린 웹 애플리케이션

우리 모두는 낮은 개발 비용으로 유연하고 적응이 쉬운 소프트웨어 아키텍처를 구축하고자 한다. 그러나 불합리한 기한과 쉬워보이는 지름길은 이러한 아키텍처를 구축하는 것을 매우 어렵게

wikibook.co.kr

 

반응형