4-Layered Architecture & 왜 서비스 간 의존을 피해야 하는가

찬디·2025년 7월 17일

우테코

목록 보기
10/18

🧭 개요

이번에 우테코 팀 프로젝트를 진행하며 서비스 의존을 하자는 의견을 냈고, 팀원이 서비스간 의존을 피하는게 좋다고 하여 이것이 가능한지, 그리고 무엇이 효율적인지 공부하고 정리해보려고 한다
.

🏗️ 4-Layered Architecture란?

우리가 흔히 말하는 4계층 아키텍처는 다음과 같은 책임 분리를 따른다:

계층예시책임
Controller (Web Layer)@RestController외부 요청 처리 및 DTO 변환
Application LayerXXXService유즈케이스 조합, 트랜잭션 관리, 외부 시스템 호출
Domain LayerAggregate, DomainService도메인 규칙, 상태 변화, 비즈니스 의사결정
Infrastructure LayerRepository, 외부 API Client영속성, 외부 통신 세부 구현

핵심은 도메인은 그 자체로 존재하며 외부 의존성, Application을 참조하지 않고, Application만이 Domain을 관리 한다는 점이다.

🤔 2. "서비스 간 의존"이 왜 문제일까?

예를 들어 다음과 같은 구조를 보자:

public class OrganizerService {
    private final EventService eventService;
}

겉보기엔 자연스럽지만, 이는 Application Layer끼리 서로 참조하고 있는 구조다. 이렇게 되면 다음 문제가 발생한다:

🚨 문제점

1. 순환 참조와 의존 방향이 무너짐

Application → Domain → Infrastructure 순으로 단방향 의존이 있어야 하는데, Service 간 참조가 늘어나면 그 방향이 뒤틀린다.

2. 유즈케이스 간 결합도 증가

하나의 서비스 변경이 다른 서비스에 영향을 준다. 이는 테스트, 유지보수, 리팩터링을 어렵게 만든다.
다들 Serivce의 버그를 디버깅할때 이 서비스 저 서비스 이동한 경험이 있을 것이다.

3. 서비스의 책임이 모호해짐

"OrganizerService에서 Event를 생성하는 게 맞나?", "이건 EventService에서 처리해야 하지 않나?" 와 같은 논의가 계속된다.
최악의 경우 심지어는 Event 생성을 두곳에서 하는 경우도 생기게된다

✅ 3. 바람직한 해결 전략

🎯 전략 1: 필요한 정보는 리포지토리 또는 쿼리로 직접 가져온다

public List<Event> findByParticipant(Long participantId) {
    return eventRepository.findByParticipantId(participantId);
}

ApplicationService끼리 참조하지 않고도, 필요한 도메인 데이터를 직접 리포지토리로 가져올 수 있다.

🎯 전략 2: 도메인 규칙으로 통합한다

이벤트 도메인 자체가 Participant 개념을 알고 있어야 한다면, 도메인 규칙으로 통합한다.

→ 즉, 애초에 그 책임이 Domain에 있는지부터 다시 검토해야 한다.

⚖️ 4. "캡슐화"란 무엇인가? Getter는 어디까지 허용인가?

List<Event> events = participantService.findAllEvents(participantId);
return events.stream()
.map(event -> event.getName())
.toList(); 

이건 캡슐화 파괴인가?

  • ❌ 단순 조회를 위한 getter 호출은 캡슐화 위반이 아니다.
  • ✅ 도메인 규칙 판단을 서비스 단에서 직접 로직으로 판단하고 있다면 문제다.

캡슐화는 "데이터를 숨기는 것"이 아니라, "의사결정(=도메인 로직)의 주체를 명확히 하는 것"이다.
결국 캡슐화는 사이드이펙트를 없애기 위한 원칙이다.

📬 5. 이번 프로젝트에서 시도하고 있는 원칙

원칙설명
서비스 간 참조는 최대한 피한다서비스는 독립적인 유즈케이스 단위로 설계되어야 한다
도메인 로직은 도메인 안에서 판단한다getter로 데이터 빼서 판단하지 말고, event.isBelongsTo(participant) 같은 메서드를 도메인에 둔다
Application Service는 orchestration만 한다상태 변화, 판단은 Domain에서 해야 한다
기능 명은 책임과 부작용을 명확히 드러내야 한다createEventAndSendMail()처럼, 숨겨진 행동이 없게 한다

✍️ 마무리

도메인 주도 설계와 4계층 아키텍처의 핵심은 의존 방향과 책임의 명확화다.
둘 모두에 매몰될 필요는 없다.
하지만 두 가지 모두 객체를 좀 더 명확하게 관리하려고 하는 점에서 나온 것들이다.
객체를 명확하게 관리한다는면에서는 한번 열심히 지켜보는것도 학습면에서 좋을지도?

DDD 개념들을 열심히 적용할 생각은 없지만 한번 학습은 해보려고한다.

profile
깃허브에서 velog로 블로그를 이전했습니다.

0개의 댓글