계층형 아키텍처는 견고한 아키텍처 패턴이다.
웹 계층에서는 요청을 받아 도메인 혹은 비즈니스 계층에 있는 서비스로 요청을 보낸다.
서비스에서는 필요한 비즈니스 로직을 수행하고, 도메인 엔티티의 현재 상태를 조회하거나 변경하기 위해 영속성 계층의 컴포넌트를 호출한다.
장점 (계층을 잘 이해하고 구성한다면)
웹 계층이나 영속성 계층에 독립적으로 도메인 로직을 작성할 수 있다.
도메인 로직에 영향을 주지 않고 웹 계층과 영속성 계층에 사용된 기술을 변경할 수 있다.
기존 기능에 영향을 주지 않고 새로운 기능을 추가할 수도 있다.
잘 만들어진 계층형 아키텍처는 선택의 폭을 넓히고, 변화하는 요구사항과 외부 요인에 빠르게 적응할 수 있게 해 준다.
문제점
코드에 나쁜 습관들이 스며들기 쉽게 만들고 시간이 지날수록 소프트웨어를 점점 변경하기 어렵게 만드는 많은 허점들을 노출한다.
계층형 아키텍처는 데이터베이스 주도 설계를 유도한다
정의에 따르면 전통적인 계층형 아키텍처의 토대는 데이터베이스다. 모든 것이 영속성 계층을 토대로 만들어진다.
웹 계층은 -(의존)-> 도메인 계층 -(의존)-> 영속성 계층
자연스레 데이터베이스에 의존하게 된다.
애플리케이션의 목적
비즈니스를 관장하는 규칙이나 정책을 반영한 모델을 만들어서 사용자가 이러한 규칙과 정책을 더욱 편리하게 활용할 수 있게 한다.
상태가 아니라 행동을 중심으로 모델링한다. 행동이 상태를 바꾸는 주체이기 때문에 행동이 비즈니스를 이끌어간다.
전통적인 계층형 아키텍처에서는 의존성 방향에 따라 데이터베이스의 구조를 먼저 생각하고, 이를 토대로 도메인 로직을 구현했을 것이다.
하지만 비즈니스 관점에서는 도메인 로직을 먼저 만들어야 한다. 그리고 도메인 로직이 맞다는 것을 확인한 후에 이를 기반으로 영속성 계층과 웹 계층을 만들어야 한다.
데이터베이스 중심적인 아키텍처가 만들어지는 가장 큰 원인은 ORM 프레임워크를 사용하기 때문이다.
ORM에 의해 관리되는 엔티티들은 일반적으로 영속성 계층에 둔다. 계층은 아래 방향으로만 접근 가능하기 때문에 도메인 계층에서는 엔티티에 접근할 수 있고, 접근할 수 있다면 사용되기 마련이다. 하지만 이렇게 되면 계층 간의 강한 결합이 생긴다.
서비스는 영속성 모델을 비즈니스 모델처럼 사용하게 되고 도메인 로직뿐만 아니라 즉시로딩/지연로딩, 데이터베이스 트랜잭션, 캐시 플러시 등등 영속성 계층과 관련된 작업들을 해야만 한다.
영속성 코드가 도메인 코드에 들어가서 둘 중 하나만 바꾸는 것이 어려워지고 계층형 아키텍처의 목표와 반대되는 상황이 된다.
지름길을 택하기 쉬워진다
전통적인 계층형 아키텍처에서 특정한 계층에서는 같은 계층에 있는 컴포넌트나 아래에 있는 계층에만 접근 가능하다.
만약 상위 계층에 위치한 컴포넌트에 접근해야 한다면 간단하게 컴포넌트를 계층 아래로 내려버리면 된다. 그러면 접근 가능하게 되고, 문제가 해결된다. 하지만 이러한 지름길을 계속해서 선택한다면 점점 영속성 계층이 비대해진다. 어떤 계층에도 속하지 않는 것처럼 보이는 헬퍼 컴포넌트나 유틸리티 컴포넌트들이 아래 계층으로 내릴 가능성이 큰 후보다.
아키텍처의 지름길을 없애고 싶다면 추가적인 아키텍처 규칙을 강제하여 규칙이 깨졌을 때 빌드가 실패하도록 만들어야 한다.
테스트하기 어려워진다
계층형 아키텍처를 사용할 때 일반적으로 나타나는 변화의 형태는 계층을 건너뛰는 것이다.
엔티티의 필드를 단 하나만 조작하게 되는 경우 웹 계층에서 바로 영속성 계층에 접근하면 도메인 계층을 건드리지 않아도 된다. 하지만 이런 일이 자주 일어난다면 문제점이 생긴다.
- 도메인 로직을 웹 계층에 구현하게 된다. 핵심 도메인 로직들이 퍼져나갈 확률이 높다.
- 웹 계층 테스트에서 도메인 계층뿐만 아니라 영속성 계층도 모킹해야 한다. 단위 테스트의 복잡도가 올라가게 되고 그러면 테스트를 작성하지 않게 된다.
유스케이스를 숨긴다
새로운 코드를 짜는 것을 선호하지만 실제로는 기존 코드를 바꾸는 데 더 많은 시간을 쓴다.
기능을 추가하거나 변경할 적절한 위치를 찾는 일이 빈번하기 때문에 아키텍처는 코드를 빠르게 탐색하는 데 도움이 돼야 한다.
도메인 로직이 여러 계층에 거쳐 흩허지기 쉽다. 이런 경우 새로운 기능을 추가할 적당한 위치를 찾는 일은 이미 어려워진 상태다.
계층형 아키텍처는 도메인 서비스의 너비에 관한 규칙을 강제하지 않는다. 그렇기 때문에 넓은 서비스가 만들어지기도 한다.
넓은 서비스
영속성 계층에 많은 의존성을 갖고 있으며, 웹 레이어의 많은 컴포넌트가 이 서비스를 의존하게 됨
서비스를 테스트하기 어려워지고 작업해야 할 유스케이스를 책임지는 서비스를 찾기도 어려워짐
동시 작업이 어려워진다
계층형 아키텍처에서는 새로운 유스케이스를 추가하는 경우
여러 개발자가 계층별로 나뉘어 웹 계층, 도메인 계층, 영속성 계층에 기능을 추가할 수 없다.
모든 것이 영속성 계층 위에 만들어지기 때문에 영속성 계층을 먼저 개발해야 하고, 그다음에 도메인 계층, 마지막으로 웹 계층을 만들어야 한다. 그렇기 때문에 특정 기능은 동시에 한 명의 개발자만 작업할 수 있다.
인터페이스를 먼저 같이 정의하고 실제 구현을 기다릴 필요 없이 인터페이스들로 작업하는 것은 데이터베이스 주도 설계를 하지 않은 경우에만 가능하다. 데이터베이스 주도 설계는 영속성 로직이 도메인 로직과 너무 섞여서 각 측면을 개별적으로 작업할 수 없기 때문이다.
넓은 서비스가 있는 경우에는 서로 다른 기능을 동시에 작업하기가 어렵다. 다른 유스케이스에 대한 자겁을 하게 되면 같은 서비스를 동시에 편집하는 상황이 발생하며 병합 충돌과 잠재적으로 이전 코드로 되돌려야 하는 문제를 야기하기 때문이다.
유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?
계층형 아키텍처는 잘못된 방향으로 흘러가도록 용인한다. 시간이 지날수록 품질이 저하되고 유지보수하기가 어려워진다.
어떤 아키텍처 스타일로 만들든 계층형 아키텍처의 함정을 염두에 두면 지름길을 택하지 않고 유지보수하기에 더 쉬운 설루션을 만드는 데 도움이 될 것이다.
'📙 > Study' 카테고리의 다른 글
[만들면서 배우는 클린 아키텍처] 03. 코드 구성하기 (2) | 2024.10.06 |
---|---|
[만들면서 배우는 클린 아키텍처] 02. 의존성 역전하기 (2) | 2024.10.05 |
[Effective Java 3/E] Item 5. 의존 객체 주입 (0) | 2021.01.10 |
[Effective Java 3/E] Item 4. private 생성자 (0) | 2021.01.10 |
[Effective Java 3/E] Item 3. private 생성자나 열거 타입으로 싱글턴 만들기 (0) | 2021.01.06 |