본문 바로가기
가치관 쌓기/개발 돌아보기

Layered Architecture 의 단점이 무엇이라고 생각하는가?

by simplify-len 2021. 7. 26.

들어가기

 이전 포스팅의 내용이였던 DDD-Lite 동영상 시청 리뷰 의 내용을 정리하다가 문득 궁금증이 생겼습니다.

정명주 강사님께서 `Layered Architecture 의 단점으로 인해, Hexagonal(Onion) Architecture 가 나왔다.`

그럼 어떤 단점이냐? 바로 ~

계층형 아키텍쳐의 단점은 무엇이냐면? 결국에는 도메인이 인프라에 의존하게 됩니다. 이 말은 즉, 도메인 관심사와 기술적 관심사가 섞이게 된다는 것을 의미합니다.

라고 동영상 중간에 말했습니다.

 

여기서 저는 왜? 도데체 왜 섞이지? 라는 생각을 하게 되었습니다.

 

LayeredArchitecture 가 무엇인가?

우리가 흔히 알고 있는 LayeredArchitecture 는 여러 군데에서 사실 발견할 수 있습니다.

제가 처음 목격했던 LayeredArchitecture 는 아마도 DDD에서 말하는 설계를 의미했습니다. 

 

그림 1 - DDD Layered Architecture

이런 형태를 띄우고 있습니다. 위에서 아래로 의존관계를 가지면서 UserInterface, Application, Domain, Infrastructure 총 4가지의 Layer 로 나눠져 있습니다. 이번 포스트의 목적은 LayeredArchitecture의 단점을 논하기 때문에 DDD에서 말하는 레이어 구성의 각 계층에 대한 내용은 논하지 않겠습니다.

 

다만, 왜 이렇게 했는지에 대해서는 짚고 넘어가야 하기에, DDD 에서 위와같은 레이어를 말한 이유는 여러가지 이유가 있습니다. 그리고 그 여러가지 이유는 한 가지 이유로 수렴한다고 생각합니다.

 

바로 관심사의 분리. 서로 다른 계층에서 자신이 해야될 책임과 역할을 명확히 한다면- 시스템의 전체적인 결합도를 낮추고 인지 과부화를 줄임으로써 재사용성을 높여 유지보수성을 향상시킬 수 있을 것입니다.

 

좋아요! 여기서 다시 본론으로 돌아가서, LayeredArchitecture 의 단점은 무엇일까? 왜 hexagonal가 나왔을까?

 

그래서 편지를 보내봤습니다. DDD-lite 를 강의해주신 정명주님에게...

메일 답장중... 출처- 나의 메일함

 

 질문을 메일로 보내놓고,혼자서 이런저런 생각을 하면서 왜 단점일까? 긴 고민을 했었습니다.

 

먼저 메일의 답변을 요약하면 흔히 3-Tier 아키텍쳐라 불리오는 Presenstaion Layer/User Interface <---> Business Logic Layer <---> Data Access Layer 로 이뤄진 아키텍쳐에서 Persistence에서 POJO Repository와 실제 인프라에 의존하는(DB 등) 기술이 섞여있는 Repository가 하나의 계층에 혼재되어 있다 라는 말의 의미였습니다.

그림 2 [3-tier]

 저는 그림 2과 같은 아키텍처를 잘 몰랐어서, 아~ 이런게 있구나 싶었는데- 또 어찌 저의 궁금증을 정확히 캐치하시고, 위에서 보여드렸던 DDD에서 말하는 아키텍처도 언급해주시더라구요. 'DDD에서 말하는 LayeredArchitecture 에서는 중심이 도메인이 아니라 Infra인 것 처럼 보이기도 하구요. 결정적으로 도메인 계층이 인프라 계층에 의존하는 것처럼 보이는 화살표도 있구요.' 라는 말씀에 순간 아차싶었습니다. 도메인이 Repository 에 의존하는 화살표 보이시나요?

 

마음속으로는 '도메인은 순수하게 유지되어야만 해!' 작고 소중한 도메인, 이라고 생각하고 살았는데- 막상 DDD에서의 LayeredArchitecture 는 그렇지 않았습니다.

 

그래서 단점은 무엇인가요?

 

의존성의 방향이 아래로 향하는게 확인되었다면- 

 

도메인 코드를 간략히 살펴보면,

@Entity
public class Employee {

  @Id
  @GeneratedValue
  private Long id;
  private String name;
	...
  public Long getId() {
      return id;
   }
	...
  
  @Override
  public String toString() {
	  return "Employee [id=" + id + ", name=" + name + ", address=" + address + ", sex=" + sex + "]";
}
import org.springframework.stereotype.Repository;
import com.example.layerdArchitechture.entity.Employee;
 
@Repository
public interface EmployeeRepositiry extends CrudRepository<Employee, Long> {
 
}

도메인 영역임에도 불구하고, Repository 를 위한 Annotation 을 붙여줘야합니다. 도메인이 인프라를 의존하는 형태를 띄우게 됩니다.

 

위에서 명주님께서 말씀하신 것처럼 Infra의 영역인 Repository 가 주인공일까요? 우리가 말하는 도메인 주도 설계의 중심에는 도메인이 존재해야 합니다. 그리고 그 도메인은 우리가 해결하고자 하는 문제의 실마리로 가득하죠. 즉 비지니스로직이 많이 있습니다. 다시 말해, 도메인은 고수준 모듈로 점차 만들어질것입니다.

 

만약 여기서 InfraStructure 의 교체하고 싶다면 어떻게 해야될까요? 도메인 영역의 코드가 인프라를 의존하고 있기 때문에- 변경해야될 코드가 많을테고, 그에 따른 테스트 코드도 어려질 것입니다.

 

정리해서 이야기하면, 도메인 영역이 Repository 을 의존하는 것으로 고수준의 모듈이 저수준의 모듈을 의존한다 라고 보여집니다.

 

여기서 이야기할 부분중 하나로 이제 의존성 역전 원칙(Dependency Inversion Principle) 이 드디어 등장합니다. 

 

저수준 모듈이 고수준 모듈에 의존하게 해야 한다. 라는 의미로, 도메인 > 인프라 의존관계를, 인프라 > 도메인 의존관계로 변경한다는 의미입니다.

그림 3 - 고수준이 저수준을 의존하는 관계

 

그림 4 - 저수준이 고수준을 의존하는 관계

이렇게 함으로써 도메인방향으로 의존하게 만듬으로써, 작고 소중한 도메인은 순수함을 유지할 수 있었습니다.

 

이런 내용의 단점도 있습니다.

여기서 저는 다른 개발자분과 대화하며 또다른 부분에 대해서 이야기해볼 수 있었는데요.

그림 5 - Slack 대화 중...

위 대화속 등장하는 "계층 책임 격리" 에 대해서 이야기해보려 합니다.

지금 부터 이야기하는 부분은 Oreilly 에서 말하는 패턴에 말하는 부분에서 인용했습니다.

계층 아키텍처에 대한 설명.
계층화된 아키텍쳐 패턴 내의 구성 요소는 수평 계층으로 구성되며 각 계층은 애플리키에션 내에서 특정 역할(예: 프리젠테이션, 비즈니스)을 수행합니다. 계층화된 아키텍처 패턴은 패턴에 존재해야 하는 계층의 수와 유형을 지정하지는 않지만 대부분의 계층화된 아키텍처는 프리젠테이션, 비즈니스, 영속성 및 데이터베이스의 4계층으로 구성됩니다. 어떤 경우에는 비즈니스 계층과 지속성 계층이 단일 비즈니스 계층으로 결합되는데, 특히 영속성 계층이 비지니스 계층 구성 요소에 포함되어 있을 때 그렇습니다. 따라서 더 작은 응용프로그램에서는 3계층만 있을 수 있는 반면에 더 크고 복잡한 비즈니스 응용 프로그램에서는 5계층이 될 수도 있습니다.

계층화된 아키텍쳐 패턴의 각 계층에는 애플리케이션 내에서 특정 역할과 책임이 있습니다. ... 아키텍처의 각 계층은 특정 비즈니스 요청을 충족하기 위해 수행해야 하는 작업을 중심으로 추상화를 형성합니다. ... 계층화된 아키텍처 패턴의 가장 강력한 기능 중 하는 구성 요소 간의 '관심사의 분리' 입니다.

여기까지만 읽어보면 저희가 알고 있는 계층형 아키텍처에 대한 내용입니다.

중요한 부분은 여기 입니다.

그림 6 - 닫힌 레이어 및 액세스 요청

 

 Spring MVC 기반의 Model - View - Controller 기반의 코딩을 하다보면, 알아차리지 못했던 부분이 있습니다.

바로 '계층 책임 격리' 라는 것에 대한 이야기입니다.

그림 6 에서 보이는 빨간색 CLOSED 는 닫힌 계층을 의미합니다. 닫힌 계층이란 의미는 요청이 계층으로 이동할 때 바로 아래의 계층을 거쳐야 해당 계층 아래의 다음 계층으로 이동해야 함을 의미합니니다. 만약  표현 계층에서, 영속성 계층으로 가야 한다면 비지니스 계층을 꼭 지나야 한다는 것을 의미합니다.

 이렇게 하지 않는 이유는 무엇일까요? 

 

격리 계층은 아키텍처의 한 계층에서 이루어진 변경이 일반적으로 다른 게층의 구성 요소에 영향을 미치거나 영향을 미치지 않는다는 것을 의미합니다. 변경은 해당 계층 내의 구성 요소와 가능한 다른 관련 계층내에서 이뤄져야 합니다. 표현계층에서 영속성 계층에 대한 직접 엑세스를 허용하면 영속성 계층 내에서 SQL에 대한 변경 사항이 비즈니스 계층과 표현 계층 모두 영향을 미치므로 구성 요소 간에 상호 의존성이 많은 매우 긴밀하게 연결된 응용 프로그램이 생성됩니다. 이러한 유형의 아키텍처는 변경하기가 매우 어렵고 비용이 많이 듭니다.

레이어 격리 개념은 각 레이어가 다른 레이어와 독립적이므로 아키텍처에서 다른 레이어의 내부 작동에 대한 지식이 거의 또는 전혀 없음을 의미합니다.  
...
프레젠테이션 계층과 비즈니스 계층 간에 사용되는 계약(예: 모델)이 동일하게 유지된다고 가정하면 비즈니스 계층은 리팩토링의 영향을 받지 않고 프레젠테이션 계층에서 사용하는 사용자 인터페이스 프레임워크 유형과 완전히 독립적입니다.  

개방 및 폐쇄 계층의 개념을 활용하면 아키텍처 계층과 요청 흐름 간의 관계를 정의하는 데 도움이 되며 설계자와 개발자에게 아키텍처 내의 다양한 계층 액세스 제한을 이해하는 데 필요한 정보를 제공합니다. 
아키텍처의 어떤 계층이 열려 있고 닫혀 있는지(그리고 그 이유) 문서화하거나 적절하게 전달하지 못하면 일반적으로 테스트, 유지 관리 및 배포하기가 매우 어려운 밀접하게 결합되고 취약한 아키텍처가 됩니다.
 - https://www.oreilly.com/content/software-architecture-patterns/

 결론은 인접한 계층을 통과하지 않고 직접 엑세스를 하게 되면 결과적으로 취약한 아키텍처가 된다는 것을 의미합니다.

 

간단한 CRUD 할 때, 아무런 비지니스 로직도 없는데- 꼭 해당 계층을 지나가야 할까? 라는 고민을 많이 했었습니다. 그렇게 하지 않으면 어떻게 되지? 라는 고민을 했었는데- 그 당시 관심사의 분리 에 대해서 명확히 알고 있다라면 좀 더 견고하게 아키텍쳐를 설계하지 않았을까 싶습니다.

 

 

[참고자료]

https://www.oreilly.com/content/software-architecture-patterns/

 

Software architecture patterns

Take a deep dive into several common software architecture patterns

www.oreilly.com

댓글