계층형 아키텍처의 문제

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

전통적인 구조

  1. 맨 위의 웹 계층(UI)에서는 요청을 받아 도메인 혹은 비즈니스 계층에 있는 서비스로 요청을 보낸다.

  2. 서비스에서는 필요한 비즈니스로직을 수행하고, 도메인 엔티티의 현재 상태를 조회하거나 변경하기 위해 영속성 계층(Data Access)의 컴포넌트를 호출한다.

우리가 만드는 애플리케이션의 목적은 무엇인가?

우리는 보통 비즈니스를 관장하는 규칙이나 정책을 반영한 모델을 만들어서 사용자가 이러한 규칙과 정책을 더욱 편리하게 활용할 수 있게 한다.

이때 우리는 상태(state)가 아니라 행동(behavior)을 중심으로 모델링한다.

어떤 애플리케이션이든 상태가 중요한 요소이긴 하지만 행동을 상태를 바꾸는 주체이기 때문에 행동이 비즈니스를 이끌어 간다.

그렇다면 계층형 아키텍처의 문제점이 무엇일까?

계층형 아키텍처는 데이터베이스 주도 설계를 유도한다.

전통적인 계층형 아키텍처의 토대는 데이터베이스(영속성 계층)이다. 웹 계층은 도메인 계층에 의존하고, 도메인 계층은 영속성 계층에 의존하기 때문에 자연스레 데이터베이스에 의존하게 된다.

  • 그럼 왜 영속성 계층을 토대로 만들게 되는가?서비스는 도메인 로직이 아니라 영속성 계층 관련된 작업들을 대부분 하게 되고, 영속성 코드가 도메인 코드에 녹아 들어가면 둘 중 하나만 바꾸는 것이 어려워지며, 유연하고 선택의 폭을 넓혀주는 계층형 아키텍처의 목표와 정확히 반대가 된다.

  • 데이터베이스의 구조를 먼저 생각하고 도메인 로직을 구현하기 때문이다. 이렇게 되는 이유는 ORM(object-relational mapping, 객체 관계 매핑) 프레임워크를 사용하기 때문이다. ORM 프레임워크를 계층형 아키텍처와 결합하면 비즈니스 규칙을 영속성 관점과 섞고 싶어진다. 그러나 도메인 계층에서 데이터베이스 엔티티를 사용하는 것은 영속성 계층과의 강한 결합을 유발한다.

  • 그렇다면 어떻게 개발을 해야 하는가?

  • 애플리케이션에서는 도메인 로직을 먼저 만들고 도메인 로직이 맞았다고 생각될때, 이를 기반으로 영속성 계층과 웹 계층을 만들어야 한다.

지름길을 택하기 쉬워진다.

전통적인 계층형 아키텍처에서 전체적으로 적용되는 유일한 규칙은 특정한 계층에서는 같은 계층에 있는 컴포넌트나 아래에 있는 계층에만 접근 가능하다는 것이다.

  • 그럼 여기서 말한 지름길은?

  • 접근을 해야 하는 클래스가 있다면 단순히 하위 계층으로 내리는 것이다.

  • 그럼 문제는?

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

테스트하기 어려워 진다.

계층형 아키텍처를 사용할 때 일반적으로 나타나는 변화의 형태는 계층을 건너뛰는 것이다. 엔티티의 필드를 단 하나만 조작하면 되는 경우에 웹 계층에서 바로 영속성 계층에 접근하면 도메인 계층을 건드릴 필요가 없지 않을까? 도메인 계층을 건너뛰는 것은 도메인 로직을 코드 여기저기에 흩어지게 만든다는 것이다.

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

    • 유즈 케이스가 확장되면? 책임이 섞이고 핵심 도메인 로직들이 퍼질 확률이 높다.

  2. 웹 계층 테스트에서 도메인 계층뿐만 아니라 영속성 계층도 모킹해야 한다는 것이다.

    • 이렇게 되면 단위 테스트가 어려워 진다.

  3. 시간이 흘러 웹 컴포넌트의 규모가 커지면 다양한 영속성 컴포넌트에 의존성이 많이 쌓이면서 테스트의 복잡도를 높이며, 테스트 작성 및 이해시 의존성을 이해하고 목을 만든느데 더 많은 시간이 걸리게 된다.

유스케이스를 숨긴다.

기능을 추가하거나 변경할 적절한 위치를 찾는 일이 빈번하기 때문에 아키텍처는 코드를 빠르게 탐색하는 데 도움이 돼야 한다. 이런 관점에서 계층형 아키텍처는 어떻게 우리의 발목을 잡을까? 넓은 서비스는 코드 상에서 특정 유스케이스를 찾는 것을 어렵게 만든다.

넓은 서비스는 영속성 계층에 많은 의존성을 갖게 되고, 다시 웹 레이어의 많은 컴포넌트가 이 서비스에 의존하게 된다. 그럼 서비스를 테스트하기도 어려워지고 작업해야 할 유스케이스를 책임지는 서비스를 찾기도 어려워 진다.

💡 해결 방법은?

고도로 특화된 좁은 도메인 서비스가 유스케이스 하나씩만 담당하게 하는 것이다.

동시 작업이 어려워진다.

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

새로운 유스 케이스를 추가한다고 하고 개발자가 3명이 있다면 한명은 웹 계층에 필요한 기능을 추가하고, 다른 한명은 도메인 계층, 나머지 개발자는 영속성 계층을 추가할수 있는데 이렇게 할 수 있는가?

모든 것이 영속성 계층 위에 만들어지기 때문에 영속성 계층을 먼저 개발하고 도메인 계층 마지막으로 웹 계층을 만들어야 한다.

코드에 넓은 서비스가 있다면 서로 다른 기능을 동시에 작업하기가 더욱 어렵다. 같은 파일을 작업하기 때문에 merge conflict와 같은 문제가 생길 확률이 높다.

Last updated