본문 바로가기

Java/클린 아키텍처

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

1. 계층형 아키텍처란?

출처: https://blog.naver.com/fbfbf1/222762059059

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

(2) 도메인
- 서비스에서는 필요한 로직을 수행한다.

(3) 영속성
- 도메인 엔티티의 현재 상태를 조회하거나 변경하기 위해 영속성 계층의 컴포넌트를 호출한다.


2. 계층형 아키텍처의 장점

- 계층형 아키텍처는 계층을 잘 이해하고 구성한다면 웹 계층이나 영속성 계층에 독립적으로 도메인 로직을 작성할 수 있다.
- 도메인 로직에 영향을 주지 않고 웹 계층과 영속성 계층에 사용된 기술을 변경할 수 있다.
- 기존 기능에 영향을 주지 않고 새로운 기능을 추가할 수 있다.

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

하지만 이렇게 장점이 많은 계층형 아키텍처에도 문제점은 존재한다.


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

- 웹 계층은 도메인 계층에 의존함.
- 도메인 계층은 영속성 계층에 의존함.
- 영속성 계층은 데이터베이스에 의존하게 된다.

=> 즉, 자연스레 데이터베이스에 의존한다.

출처: https://velog.io/@kangukii97/%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-Layered-Architecture-%EA%B3%84%EC%B8%B5%ED%98%95-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98

데이터베이스에 의존하는 코드과연 어떤 문제를 가져올까?


4. 모델링

우리는 상태(State)가 아닌 행동(Behavior)을 중심으로 모델링한다. 행동이 상태를 바꾸는 주체이기 때문에 행동이 비지니스를 이끌어간다.

우리는 "데이터를 어떻게 저장하는 가"가 아닌, "어떻게 동작해야 하는가"에 대해 생각한다.

"비즈니스 로직(행동)"이 중심이다.

하지만, 우리는 왜 '도메인 로직'이 아닌 '데이터베이스'를 토대로 아키텍처를 만들었던걸까?


5. 전통적인 계층형 아키텍처

데이터베이스를 토대로 아키텍처를 만드는 방법은 전통적인 계층형 아키텍처에서는 합리적인 방법이기 때문이다.

보통 우리는 평소에 데이터베이스의 구조를 먼저 생각하고, 이를 토대로 도메인 로직을 구현했을 것이다. 의존성의 방향에 따라 자연스럽게 구현한 것이기 때문이다.

데이터베이스 중심적인 아키텍처가 만들어지는 가장 큰 원인은 ORM(object-relational mapping) 프레임워크를 사용하기 때문이다.


6. ORM (object-relational mapping,객체 관계 매핑)

ORM에 의해 관리되는 엔티티들은 일반적으로 영속성 계층(데이터를 영구적으로 저장하고, 조회하고, 갱신하며, 삭제하는 역할을 담당하는 계층)에 둔다

계층은 아래 방향으로만 접근 가능하기 때문에 도메인 계층에서는 이러한 엔티티에 접근할 수 있다.

하지만 이렇게 되면 영속성 계층과 도메인 계층 사이에 강한 결합이 생긴다.

서비스는 영속성 모델을 비지니스 모델처럼 사용하게 되고 이로 인해 도메인 로직뿐만 아니라 즉시 로딩, 지연로딩, 데이터베이스 트랜잭션, 캐시 플러시 등등 영속성 계층과 관련된 작업들을 해야만 한다.


7. 깨진 창문 이론

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

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

출처: https://blog.naver.com/fbfbf1/222762059059

어떤 계층에도 속하지 않는 것처럼 보이는 헬퍼 컴포넌트나 유틸리티 컴포넌트들이 이처럼 아래 계층으로 내릴 가능성이 크다.

하지만 영속성 계층은 컴포넌트를 아래 계층으로 내릴수록 더 비대해지는 결과를 가져온다.


8. 테스트가 어려워진다.

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

❗ 문제 1: 도메인 로직이 웹 계층에 흩어진다

- 원래 도메인 계층이 담당해야 할 "핵심 규칙"을 컨트롤러나 서비스에서 직접 처리하게 된다.

❗ 문제 2: 유스케이스가 커지면 웹 계층이 복잡해진다

- 이렇게 기능이 확장되면서 웹 계층에 비즈니스 로직이 계속 추가된다.
- 결국 "컨트롤러 + 서비스"가 모든 책임을 지게 되고, 로직이 이곳저곳에 흩어진다.
- 이러면 유지보수가 점점 어려워진다.

❗ 문제 3: 테스트가 어려워진다

  • 웹 계층에서 로직을 처리하다 보니 도메인 계층뿐 아니라 DB 접근 코드까지 mock 해야 한다.
  • 즉, 테스트할 범위가 커지고 복잡도도 높아지게 되는 것이고, 단위 테스트가 단위답지 않게 된다.

❗ 문제 4: 유스케이스가 코드에서 사라진다

  • 실제로 "무엇을 하는 기능"인지 파악이 어렵고, 비즈니스 흐름이 명확히 드러나지 않게 된다.
  • 행동 중심의 도메인 설계가 사라지고, 단순한 CRUD 중심의 코드만 남게 된다.

9. 유스케이스가 코드에서 사라지면 어떤 문제가 발생할까?

 

  • 유스케이스란 정확히 무엇인가?
  • 기능을 추가하거나 변경할 적절한 위치를 찾을 때 계층형 아키텍처는 우리의 발목을 잡는다.
    • 계층형은 도메인 로직이 여러 계층에 걸쳐 흩어지기 쉽다.
    • 이럴 경우 새로운 기능을 추가할 적당한 위치를 찾는 일은 이미 어려워진 상태다
  • 계층형 아키텍처는 도메인 서비스너비에 관한 규칙을 강제하지 않는다.
    • 아래 그림처럼 여러 개의 유스케이스를 담당하는 아주 넓은 서비스가 만들어지기도 한다.
    • 넓은 서비스는 영속성 계층에 많은 의존성을 갖게 되고, 다시 웹 레이어의 많은 컴포넌트가 이 서비스에 의존하게 된다.
      • 서비스를 테스트하기도 어려워지고 작업해야 할 유스케이스를 책임지는 서비스를 찾기도 어려워진다.

10. 동시 작업이 어려워진다.

  • 계층형 아키텍처는 동시 작업 면에서 도움이 되지 않는다.
  • 계층형 아키텍처에서는 모든 것이 영속성 계층 위에 만들어지기에 영속성 계층 -> 도메인 -> 웹 계층을 만들어야 한다. 그렇기에 특정 기능은 동시에 한 명의 개발자만 작업할 수 있다.
  • 데이터베이스 주도 설계를 하지 않는다면 인터페이스를 먼저 같이 정의하고, 각 개발자들이 실제 구현을 기다릴 필요 없이 이 인터페이스들로 작업하면 된다고 생각할 수 있다.
  • 코드에 넓은 서비스가 있다면 서로 다른 기능을 동시에 작업하기가 더 어렵다.
    • 서로 다른 유스케이스에 대한 작업을 하게 되면 같은 서비스를 동시에 편집하는 상황이 발생하고, 이는 merge conflict와 잠재적으로 이전 코드로 되돌려야 하는 문제를 야기하기 때문이다.

11. 유지보수 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?

  • 올바르게 구축하고 몇 가지 추가적인 규칙들을 적용하면 계층형 아키텍처는 유지보수가 매우 쉬워지고 코드를 쉽게 변경하거나 추가할 수 있게 된다.
  • 계층형 아키텍처로 만들든 다른 아키텍처 스타일로 만들든, 계층형 아키텍처의 함정을 염두에 두면 지름길을 택하지 않고 유지보수하기에 더 쉬운 솔루션을 만드는 데 도움이 될 것이다.

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