강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

김지훈님의 프로필 이미지
김지훈

작성한 질문수

토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1

어플리케이션 , 도메인 계층질문

해결된 질문

작성

·

61

0

안녕하세요 토비님

어플리케이션 계층의 port는 도메인과 외부의 연결을 위해 필요 하다고 알고 있는데

jpa나 마이바티스 같은 기술 또한 도메인 외부의 내용이기에 인프라 계층으로 들어간다고 알고 있습니다

그런데 의존성 역전을 위한 레파지토리 인터페이스를 어플리케이션 계층에 만들면 어플리케이션 서비스에서 도메인에 관련된 내용을 너무 깊게 관여하는 것 같아서 질문 드립니다

어플리케이션과 도메인의 경계를 어디까지로 보는게 좋을까요

답변 3

0

토비님의 프로필 이미지
토비
지식공유자

의존성 역전이 아니라면 애플리케이션 코드는 기술에 의존적인 외부 인터페이스에 의존합니다. 기술과 관련된 코드가 어디에 존재하는지를 따지는 이유는 해당 코드가 기술에 의존하고 있는가, 즉 기술의 변경이 코드에 영향을 미치는가로 판단해야 합니다. 의존성 역전으로 스프링 데이터 JPA의 관례를 따르는 코드가 애플리케이션의 Port가 되었지만, 이 인터페이스는 특정 기술에 의존적이지 않습니다. 오히려 도메인 지식과 용어가 도메인 로직에 충실하게 드러나는 인터페이스일 뿐입니다. 물론 JPA 기술에서 제공하는 애토네여션이 메타 정보로 부여되지만 이 때문에 해당 인터페이스가 JPA에 의존되지는 않습니다. 도메인과 무관한 JPA API가 사용된 적도 없으며 해당 기술이 JPA가 아니라 다른 스토리지 기술로 변경된다고 하더라도, 만들어진 인터페이스의 코드는 변경되지 않습니다.

기술이 침투한다는 것이 어떤 의미인지, 어떤 효과가 있는 것인지를 잘 따져보고 초기 개발 맥락에서 최선이라고 한 선택을 한 것입니다. 관련된 내용은 강의에서 그리고 Q&A의 여러 질문을 통해서 말씀드렸습니다.

도메인 모델의 내용을 최대한 표현해낸 코드가 도메인 계층이 되고, 애플리케이션(헥사곤) 외부와의 연동과 책임을 담당하는 부분을 애플리케이션 서비스로 개발합니다. 이 둘 다 도메인 지식과 로직에 충실하게 작성되었고, 일부 외부 기술의 애노테이션이 노출되는 것이 이런 개발 원칙을 깨뜨리지 않는 선에서 적절하게 선택했습니다.

기술의 침투, 기술에 의존이라는 건 기술의 변화, 그 기술을 적용하는 환경이 바뀔 때 그 "코드"도 바뀔 가능성이 높은가로 판단하면 됩니다.

김지훈님의 프로필 이미지
김지훈
질문자

토비님 답변 감사합니다

제 질문의 주된 내용은 헥사곤 안과 밖의 의존이 아닌 헥사곤 내부에서 애플리케이션과 도메인의 경계에 관한 내용인데 질문이 미흡했던 것 같습니다

도메인은 비지니스의 핵심 부분, 어플리케이션은 외부와 내부의 조율로 생각하고 있는데

제가 개발을 하다 보니 조율을 해야 할 어플리케이션에서 애그리거트의 데이터를 조회하고 연관된 다른 애그리거트를 가져오는 등의 작업을 하고 내부 로직을 실행 하다 보니 어플리케이션에서 도메인의 영역을 깊게 관여한다고 느껴졌습니다

도메인과 어플리케이션의 구분이 되어 있는데 그 경계에 대해 질문 드립니다

토비님의 프로필 이미지
토비
지식공유자

말씀하신 애그리거트를 조회하고 엔티티 루트를 통해서 도메인 기능을 호출하는 것은 애플리케이션 서비스의 몫입니다. 도메인 계층의 기능을 사용하는 관문(facade) 역할을 하기 때문에 애플리케이션 서비스를 도메인 퍼사드라고 부릅니다.

문제는 한 애그리거트의 루트를 통한 도메인 로직의 수행 결과를 바탕으로 다른 애그리거트의 기능을 또 호출하는 등의 작업이 애플리케이션 서비스에 있어야 하는 것인가인데요.

이 내용을 파트2와 두 번째 주제인 도메인 이벤트를 다룰 때 집중적으로 설명할 것입니다.

간단히 미리 말씀드리면, 여러 애그리거트에 걸쳐서 애플리케이션 로직이 수행되는 경우 여러가지 선택지가 있습니다.

  • 엔티티가 연관된 엔티티(애그리거트 루트)에 대한 레퍼런스를 가지고 있고, 연결되는 도메인 로직을 직접 다른 엔티티 호출로 수행하는 방법이 있습니다. 어쩌면 가장 객체지향적인 설계와 응용이라고 볼 수 있지만, 이 방식을 잘 사용하기가 은근히 까다롭습니다. 특히 연관관계 엔티티를 레퍼런스로 가져오지 못하고 리포지토리를 타고 가져와야 하는 경우 엔티티에 리포지토리를 주입해야 하는가라는 고민도 발생하죠. 어쨌든 가능한 방법이긴 합니다.

  • 두번째 방법은 서비스 계층에서 도메인 로직의 흐름에 따라서 필요한 다른 애그리거트를 조회하거나 로직을 가진 엔티티의 메소드를 호출하는 방식입니다. 애플리케이션 서비스가 일종의 도메인 로직에 대한 라우팅 역할을 한다고 보고, 캡슐화된 도메인 로직의 범위를 넘어서는 애플리케이션 로직을 담당한다고 보면 이 방식도 합리적입니다. 다만, 이게 연결되는 범위가 커지면 서비스가 많은 리포지토리를 의존하고, 여러 애그리거트의 로직을 호출하는 식으로 동작하게 되는데, 시스템이 복잡해지면 이러한 애플리케이션 로직 자체도 중복이 발생하거나, 변경이 발생하는 위치를 추적하기가 어려워집니다.

  • 그래서 선택할 수 있는 다른 대안으로는 애그리거트를 조회하거나 기능을 사용하는 범위를 분리하는 것입니다. 이게 이번 강의 말미에 이야기했던, 애그리거트 단위로 헥사곤을 분리해서 만드는 전략입니다. 하나의 헥사곤은 컴포넌트처럼 사용됩니다. 꼭 클라이언트의 요청만 포트로 받는게 아니라, 다른 헥사곤 컴포넌트를 사용하는 것도 가능하죠. 이게 제가 직접적인 애플리케이션 로직의 흐름을 담아서 개발하는 방식으로 파트 2에서 설명할 방식입니다.

  • 마지막으로 각 애그리거트 로직의 연결 강도에 따라서 혹은 의존 수준에 따라서 해당 헥사곤에 대한 직접 호출 대신 도메인 이벤트를 이용한 간접적인 연동을 선택할 수도 있습니다. 이에 대해서는 클린 스프링 두 번째 주제에서 방법을 살펴보고, 그에 맞게 구조를 변경하는 리팩터링을 진행할 것입니다. 이 접근 방법은 애그리거트 컴포넌트를 넘어서 기능 모듈 구조로 시스템을 구분하고, 이를 분리하는 MSA 등으로 확장할 때도 유리한 점이 있습니다.

 

일단 고민이 되는 코드를 살펴보시고 애플리케이션 서비스에 노출된 코드 중에서 도메인 계층으로 옮길 수 있는 게 있는지 먼저 확인해보시면 좋겠습니다. 혹은 애그리거트의 범위를 좀 더 크게 잡는 방법도 있습니다. 그러면 도메인 로직이 그 안에서 한번에 처리되기도 하니까요. 물론 애그리거트를 무작정 키우는 것은 부작용이 있기 때문에 신중해야 합니다. 충분히 도메인에 충실한 로직을 도메인 계층에 넣으셨다면 이를 오케스트레이션하는, 즉 지휘해서 애플리케이션 레벨에서 포트를 통해서 받은 요청을 도메인 계층을 이용해서 풀어내는 로직은 애플리케이션 서비스에 두어도 괜찮습니다.

김지훈님의 프로필 이미지
김지훈
질문자

답변 감사합니다

다음 강의 파트 2도 기대하겠습니다

0

토비님의 프로필 이미지
토비
지식공유자

의존성 역전이 아니라면 애플리케이션 코드는 기술에 의존적인 외부 인터페이스에 의존합니다. 기술과 관련된 코드가 어디에 존재하는지를 따지는 이유는 해당 코드가 기술에 의존하고 있는가, 즉 기술의 변경이 코드에 영향을 미치는가로 판단해야 합니다. 의존성 역전으로 스프링 데이터 JPA의 관례를 따르는 코드가 애플리케이션의 Port가 되었지만, 이 인터페이스는 특정 기술에 의존적이지 않습니다. 오히려 도메인 지식과 용어가 도메인 로직에 충실하게 드러나는 인터페이스일 뿐입니다. 물론 JPA 기술에서 제공하는 애토네여션이 메타 정보로 부여되지만 이 때문에 해당 인터페이스가 JPA에 의존되지는 않습니다. 도메인과 무관한 JPA API가 사용된 적도 없으며 해당 기술이 JPA가 아니라 다른 스토리지 기술로 변경된다고 하더라도, 만들어진 인터페이스의 코드는 변경되지 않습니다.

기술이 침투한다는 것이 어떤 의미인지, 어떤 효과가 있는 것인지를 잘 따져보고 초기 개발 맥락에서 최선이라고 한 선택을 한 것입니다. 관련된 내용은 강의에서 그리고 Q&A의 여러 질문을 통해서 말씀드렸습니다.

도메인 모델의 내용을 최대한 표현해낸 코드가 도메인 계층이 되고, 애플리케이션(헥사곤) 외부와의 연동과 책임을 담당하는 부분을 애플리케이션 서비스로 개발합니다. 이 둘 다 도메인 지식과 로직에 충실하게 작성되었고, 일부 외부 기술의 애노테이션이 노출되는 것이 이런 개발 원칙을 깨뜨리지 않는 선에서 적절하게 선택했습니다.

기술의 침투, 기술에 의존이라는 건 기술의 변화, 그 기술을 적용하는 환경이 바뀔 때 그 "코드"도 바뀔 가능성이 높은가로 판단하면 됩니다.

0

안녕하세요, 인프런 AI 인턴입니다.

김지훈님의 질문은 어플리케이션 계층과 도메인 계층 사이의 경계 설정에 대한 깊은 고민을 반영하고 있습니다.

어플리케이션 계층의 포트는 외부 인터페이스(입력/출력)와의 연결을 담당하며, 이로 인해 도메인 폴리시에 대한 변화에 강건함을 제공하는 것이 주된 역할입니다. 반면, JPA나 MyBatis 같은 기술은 인프라 계층으로 분류되며, 이들은 영속성 관련 로직으로 도메인 로직에 영향을 미치지 않도록 격리하는 것이 중요합니다.

의존성 역전을 위한 레포지토리 인터페이스의 위치에 대해 고민하신 부분은 매우 중요합니다. 레포지토리 인터페이스를 어플리케이션 계층에 두는 이유는, 이러한 설계가 도메인 로직의 변화에 따른 영향도를 최소화하면서도, 외부 기술이나 구체적인 DB 구현에 종속되지 않는 구조를 가능하게 하기 때문입니다. 이는 헥사고날 아키텍처의 주된 목적 중 하나이기도 합니다.

관련하여, 토비님께서 이전에 다른 질문에서 어플리케이션 계층의 포트와 도메인 사이의 관계 및 의존성 설정에 대해 설명하셨던 사례가 있습니다. 예를 들어, 의존성 방향과 관련된 질문이 있습니다. 게시물에서, 외부 세상과 내부 애플리케이션을 분리하고, 헥사고날 아키텍처에서 포트와 어댑터를 어떻게 구성할 수 있는지에 대해 토비님께서 상세하게 설명하신 바 있습니다.

이를 통해, 어플리케이션 계층과 도메인의 경계는 도메인이 비즈니스 로직에만 집중할 수 있도록 보장하며, 어플리케이션 계층은 이러한 도메인이 외부와의 상호작용을 관리하도록 설계하는 방향으로 설정하는 것이 바람직합니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.

현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

김지훈님의 프로필 이미지
김지훈

작성한 질문수

질문하기