강의

멘토링

로드맵

Inflearn brand logo image

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

종운님의 프로필 이미지
종운

작성한 질문수

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

ArchUnit을 이용한 아키텍처 테스트

안녕하세요. 토비님! 도메인의 다양한 업데이트 요구사항을 Web API 계층에서 어떻게 다뤄야 할까요?

작성

·

209

0

안녕하세요, 토비님. 강의를 들으며 많은 인사이트를 얻고 있습니다.
강의를 완강한 후에도 내면적으로 정리되지 않은 부분이 있어 조심스럽게 질문을 드리게 되었습니다.

생성과 관련된 설계는 강의에서 잘 이해가 되었지만, 업데이트(update)와 관련된 내용은 직접적으로 다뤄지지 않아 고민이 생겼습니다. 특히, 제 고민은 다음과 같습니다.
"도메인의 비즈니스 규칙이 Web API 설계에 어느 정도까지 직접적으로 드러나야 하는가?"

현재 도메인 로직에서는 사용자의 여러 정보를 변경할 수 있는 비즈니스 규칙이 존재합니다. 예를 들어:

  • 비밀번호 변경

     

  • 기타 세부정보 변경

비즈니스적으로는 각각의 규칙이 잘 정의되어 있고, 각각의 변경 로직도 Member 객체 내에 명확히 메서드로 존재합니다.

여기서, 이러한 비즈니스에 대해서 API에 어떻게 노출시켜야 하는가에 대해서 두 가지 선택지가 고려됩니다.

1. 비즈니스 정의를 역할 별로 구성한다.

POST /api/v1/members/{id}/change-password
POST /api/v1/members/{id}/change-nickname

생각이 나는 장단점은 다음과 같습니다.
장점: 비즈니스에 따라 API를 관리하여 클라이언트가 이해하기 용이합니다.
단점: 수정 가능한 필드가 많아질수록 API의 개수가 증가하며, 유지보수가 어려워질 수 있고, Restful 규칙에 위배됩니다.

2. 하나의 update API로 통합한다.

PATCH /api/v1/members/{id}

{
  "password": "originalPassword123!", // nullable
  "detailRequest": { // nullable
    "email": "user@example.com",
    "nickname": "nickname123",
    "password": "newPassword456!"
  }
}

장점: API가 간결하여 확장이 용이하며, 클라이언트는 필요한 값만 상황에 따라 요청하면 됩니다.
단점: API가 비즈니스 책임에 명확하지 않을 수 있습니다.

결론적인 질문은 다음과 같이 정리 할 수 있을 것 같습니다.

  • 비즈니스 로직이 도메인 레이어에 잘 분리되어 있는 경우, API 계층에서도 분리하여 표현하는 것이 좋은가요?

  • 도메인의 역할만 명확하다면 API는 통합해서 update 형식으로 만들어도 괜찮은가요?


만약, 후자로 처리를 한다면 어디서 처리를 하는게 좋아보이시나요?

  1. 서비스 계층

  2. 도메인 계층

// MemberModifyService
public void update(Long memberId, MemberUpdateRequest request) {
    Member member = memberFinder.find(memberId);
    if (request.password() != null) {
        member.changePassword(request.password());
    }

    if (request.detailRequest() != null) {
        member.updateInfo();
    }
}
--------
// MemberModifyService
public void update(Long memberId, MemberUpdateRequest request) {
    Member member = memberFinder.find(memberId);
    member.update(request);
}

// Member
public void update(MemberUpdateRequest request) {
    if (request.password() != null) {
        changePassword(request.password());
    }

    if (request.detailRequest() != null) {
        updateInfo();
    }
}

뭔가, 이런 고민이 계속 드는 이유가 외부 계층에 종속적이지 않고 도메인에 의존하여 개발을 하더라도 실제로 저희가 처한 상황은 대부분 WebAPI 계층에서의 요청이 많다보니 외부의 행위 또한 도메인에 종속되어야 하는가 하는 고민이 생긴 것 같습니다.

양질의 강의 제공해주셔서 감사드립니다!

답변 2

3

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

좋은 질문 해주셔서 감사합니다.

이건 저도 항상 고민하는 내용입니다.

보통 프론트 엔드 설계에 따라서 API의 요청 단위가 만들어지는 경우가 많습니다. 이걸 따라서 애플리케이션 서비스의 인터페이스를 만들어둘 수 있습니다. 그런데 UI/UX 관점에서 변경이 자주 일어나게 되고, 그때마다 API와 애플리케이션 포트, 도메인 메소드까지 매번 따라서 바뀌는 것은 별로 좋아보이지 않습니다.

제가 선택하는 기준은 도메인이 가지고 있는 지식과 정보의 본질적인 특성을 따라서 변경의 단위를 정하는 것입니다. 사용자라는 액터가 시스템에 대해서 어떤 것을 변경할 수 있고, 특히 함께 변경되어야 하는 것들, 항상 같이 다니는 정보들이 무엇인지를 생각해보고, 이 기준으로 도메인 계층 또는 애플리케이션 계층의 메소드와 인터페이스를 설계합니다.

그리고 나서 API 어댑터를 만들 때 이 정보 중에서 일부를 선택하거나, 또는 조합해서 클라이언트와 주고 받을 형식을 최종적으로 정할 수 있습니다. 매번 매우 디테일하게 현재 시점에만 딱 필요한 정보를 주고 받는 것을 선호하는 프론트엔드 개발자들이 있습니다. 그래도 지금 이 화면에서는 변경이 일어나지 않는 정보라 하더라도, 같은 정보 그룹에 속한 것들이라면 우선 API 형식으로 구성을 해두려고 합니다.

그렇다고 엔티티나 애그리거트에서 수정될 수 있는 모든 정보를 한번에 다 넣는 것은 아닙니다. 사용 시나리오에 따라서 각 단계마다 함께 변경되는 것이 적당한 단위를 찾아봅니다.

그런데 그렇게 만들다보면 가끔 옵셔널하게 변경하는 값들을 사용할 것인가의 문제가 있긴합니다. 변경 요청에 어떤 필드의 값이 있으면 변경하고, null이라면 무시하는 스타일이죠. 이 방식으로 만들면 유연하게 API 변경에 대응해서 사용하기 편리합니다. 하지만, 실수로 null이 들어간 것인지, 아니면 정말 변경이 필요없어서 null을 넣었는지 알 수가 없죠. 그래서 버그가 생길 수도 있습니다.

이상적인 것은 변경 단위를 정했으면 바뀌지 않는 값도 모두 전달 받는 것이긴합니다.

매번 디테일을 보면서 다시 고민하곤 하는데, 매번 정답이 딱 보이지는 않네요. 파트2에서 이 이야기를 하게 될텐데 미리 질문을 해주셨네요. 다시 고민이 시작되었습니다.

종운님의 프로필 이미지
종운
질문자

안녕하세요 토비님! 주말에도 답변 감사합니다~!

비즈니스적인 구조로 API를 만들고 BFF를 구성해서 호출하는 형태를 취해야하나 고민을 해보기도 했는데 여러가지 방법들은 있겠지만, 저도 아직까지는 같은 정보 그룹에 대한 API를 만드는게 가장 적합한가. 라는 생각을 가지면서도 코드를 짜면서 계속 고민을 하게 되는 부분 인 것 같습니다.

바뀌지 않는 값도 모두 전달받는다.
라고 말씀하신 부분은 변경 단위에 대해서 클라이언트에서 해당 도메인의 필드를 포함하고 수정할 것들만 보내는 형태를 말씀하시는거겠죠~? 값이 없다거나 하는 것은 Validation으로 검증하면 될테니까요~

0

안녕하세요, 인프런 AI 인턴입니다. 종운님께서 올려주신 질문에 대해 답변드리겠습니다.

도메인의 비즈니스 규칙과 Web API 계층의 설계

도메인의 비즈니스 규칙이 Web API 설계에 어느 정도까지 드러나야 하는가에 대한 질문은, API를 설계하고 비즈니스 로직을 구현할 때 자주 직면하는 문제입니다. 두 가지 접근 방식 모두 각각의 장단점이 있으니, 프로젝트의 특성과 요구사항에 맞게 선택하는 것이 중요합니다.

1. 비즈니스 정의를 역할별로 구성 (RESTful 방식을 따라 API 분리)

  • 장점: 각 API가 특정한 비즈니스 역할을 수행하므로, 클라이언트 측에서도 기대할 수 있는 결과가 명확해집니다. 비즈니스 로직이 분리되어 있어 관리가 용이합니다.
  • 단점: 많은 API 엔드포인트를 관리해야 하며, 변경할 가능성이 있는 모든 비즈니스 규칙을 API로 만들어줘야 할 수 있습니다.

2. 하나의 update API로 통합 (유연성 높은 단일 API)

  • 장점: 클라이언트가 원하는 변경 사항을 선택적으로 보낼 수 있어 유연성이 높습니다. API가 적어져서 관리가 용이합니다.
  • 단점: 비즈니스 규칙이 명확히 드러나지 않을 수 있으며, 서버에서는 유효성 검증과 로직 처리가 복잡해질 수 있습니다.

API 설계 시 고려할 점

  • 비즈니스 변경의 빈도: 비즈니스 로직이 자주 변경된다면 유연성 있는 설계가 유리할 수 있습니다.
  • 복잡성: 복잡한 비즈니스 로직이라면 명확히 분리된 API가 유리할 수 있습니다.
  • 미래의 확장성: 제작한 API가 장기적으로 확장될 여지가 있다면, 통합된 API가 더 효율적일 수 있습니다.

서비스 계층 vs 도메인 계층

  • 서비스 계층: 복합적인 비즈니스 로직을 관리하는 데 적합합니다. 특히 여러 도메인 객체에 걸쳐 있는 비즈니스 로직을 구현할 때 유리합니다.
  • 도메인 계층: 각 도메인 객체가 자신에 대한 책임을 가지고 있으므로, 해당 객체 자체의 내부 로직을 처리하는 데 적합합니다.

만약 여러 속성을 동시에 변경하는 요구사항이 자주 발생하고, 이 변경 요청이 복잡하다면 서비스 계층에서 처리하는 것이 적절합니다. 반면, 도메인 객체 내부의 상태 변경이 명확하고 독립적이라면 도메인 계층에서 관리하는 것이 더 자연스럽습니다.

관련된 토비님의 강의 내용을 참고하시면 더욱 풍부한 인사이트를 얻을 수 있습니다.

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

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

종운님의 프로필 이미지
종운

작성한 질문수

질문하기