레이어드 아키텍처의 문제점과 해결책

Gyeongjae Ham·2023년 6월 22일
0

TEST

목록 보기
5/7
post-thumbnail

이 시리즈는 TDD를 숙달하기 전에 TEST 자체에 대한 이해를 높이기 위한 학습 시리즈입니다

진단

  • h2가 들어간 테스트는 앞선 분류에서 중형 테스트라고 분류된 테스트입니다
  • 때문에 속도도 빠르지 않고, 이런 테스트가 양이 많아질수록 전체 테스트를 돌리는 시간에 대한 부담이 커질 것입니다

문제가 발생하는 이유

  1. 설계가 잘못되었을 확률
    • 현재까지의 예제로 보면 현재 예제의 시스템은 RDB에 강결합된 상태이고, h2Mockito가 없이는 테스트 작성이 어렵습니다
    • 이 예제에서 사용하는 h2는 테스트용 임베디드 모드를 지원했기에 테스트라도 진행할 수 있었지만, 만약 ElasticSearch같이 지원이 안되는 DB를 사용중일 경우는 어떻게 해야 할까요?
  2. 지금 작성한 테스트가 실제로 테스트가 필요한 본질이 아닐 확률

레이어드 아키텍처

  • 위 문제들의 근본적인 원인은 예제 시스템의 아키텍처가 레이어드 아키텍처로 이뤄져 있기 때문입니다
  • 레이어드 아키텍처는 가시적으로 개발할 수 있고, 구조가 비슷한 기능끼리 묶여있어서 파악하기 쉽다는 장점을 가지고 있지만 단점들도 존재합니다

레이어드 아키텍처의 단점

DB 주도 설계

  • 레이어드 아키텍처는 데이터베이스 주도 설계를 유도합니다
  • 어떤 서비스를 만드려고 할 때, 이런 아키텍처에 익숙해진 상태라면 먼저 Entity를 떠올리게 마련입니다
  • 하지만, 어떤 서비스를 계획할 때 가장 먼저 생각해야 하는 건 Use case이고, 여기 안의 도메인들과 도메인들의 관계를 먼저 떠올려야 합니다

동시 작업 문제

  • 어떤 기능을 개발해야 한다고 가정했을 때, EntityRepository가 나와야 Service 개발이 가능하고, Service가 나와야 Controller가 개발 가능합니다
    • 따라서 특정 기능 개발을 한 명만 수행이 가능하게 됩니다

도메인이 죽는다

  • 객체를 중심으로 설계하지 않고, 함수를 위주로 설계하게 되면서 절차 지향적인 코드가 나오게 됩니다
  • 그만큼 점점 도메인에 대한 관심이 멀어지게 됩니다
  • 사실상 Service가 모든 일을 처리하는 역할을 수행하게 됩니다. 이런 서비스를 fat service라고 부릅니다
  • 이뿐만이 아니라 레이어드 아키텍처는 의존성에 대한 고민을 유도하지 않고, 규모가 커질수록 확장성이 떨어진다라는 단점 또한 가지고 있습니다

개선된 아키텍처

도메인

  • 비즈니스 문제를 해결하는 객체
  • OOP스러운 도메인들이 협력하는 곳
  • lombok을 제외한 어노테이션이 없는 오브젝트로 도메인 엔티티영속성 객체를 구분합니다
  • 계층간 연결된 의존성이 없습니다

Repository의 DB 강결합 구조 해결

  • Service도메인을 의존하고 있지만 도메인은 순수 자바코드로 인스턴스화 하는 게 어렵지 않으므로, 테스트에 큰 방해요소가 아닙니다
  • 비즈니스 레이어에 만든 Repository 인터페이스를 위치시킵니다
    • 영속성 레이어에서는 위 인터페이스를 구현한 구현체를 두고, 이 구현체가 JpaRepository를 사용하도록 합니다
  • 위와 같은 간단한 구조 변경으로 테스트할 때 우리는 fakeRepository를 구현체로 등록해 쉽게 테스트할 수 있는 환경이 됐습니다

Controller의 의존성 풀어주기

  • 앞선 ServiceRepository에 대한 의존성을 풀어준 방법으로 ControllerService, Repository, Domain에 가지는 의존성을 해결해 줍니다
  • 이렇게 구조 개선을 하면 마찬가지로 테스트할 때, Service를 대체하는 FakeServiceMockService를 구현체로 둠으로써 쉽게 테스트할 수 있습니다

외부 연동

  • 위 구조 개선은 이 예제 시스템에서 보여지는 부분뿐만 아니라 외부 시스템을 연동해야 할 경우에도 모두 적용할 수 있는 아키텍처이고, 이를 적용하면 보다시피 외부 시스템일지라도 쉽게 테스트할 수 있는 환경을 가지게 됩니다
  • 예제 시스템에서 봤던 JavaMailSender 역시 인터페이스와 구현체로 분리해서 작성할 수 있겠습니다

개선 전 추가될 사항

  1. 의존성 역전 원리를 이용하여 외부를 다루도록 개선할 겁니다
  2. 약해진 의존성의 이점인 테스트에 용이해진 설계에서 필요한 경우 Mock 객체로 치환해서 테스트할 겁니다(Mockito, h2 없이 테스트를 짭니다)
  3. 패키지 구조 개선

-> 도메인 중점으로 패키지를 구성합니다
-> 이렇게 구성할 경우 추후 MSA 아키텍처를 적용하는데 유리합니다

  • 패키지 이름 respository 대신 infrastructure로 변경하겠습니다
    • 외부 기능들도 포함하는 패키지이기 때문에 더 적절한 패키지명으로 변경합니다
  1. 순환 참조가 생기는지 의식하면서 개발해야 합니다
    • 패키지기리도 일어날 수 있습니다
  2. JPA 엔티티도메인 모델을 분리
  3. Service에 있는 setter를 모두 제거하고 domain/VO로 이동시킵니다
  4. CQRS
    • 명령과 질의의 책임을 분리
    • 메소드를 명령과 질의로 나누자
      • 명령: 상태를 바꾸는 메소드
        • 명령 메소드는 void 타입이어야 합니다
        • 편의상 명령 메소드가 종종 return this하는 경우도 있는데, 이렇게 해서는 안됩니다
      • 질의: 상태를 물어보는 메소드
        • 질의 메소드는 상태를 변경해서는 안됩니다
    • 하나의 메소드는 명령이나 쿼리여야 하며, 두 가지 기능을 모두 가져서는 안됩니다. 명령은 객체의 상태를 변경할 수 있지만, 값을 반환하지는 않는다. 쿼리는 값을 반환하지만 객체를 변경하지 않는다

profile
Always be happy 😀

0개의 댓글