2. 개발 가이드라인 | 시스템 아키텍처 구조 - DDD-Community/DDD-12-MOYORAK-API GitHub Wiki

1. 기본 아키텍처 구성 (Layered Architecture)

우리 프로젝트는 전형적인 레이어드 아키텍처를 기반으로 다음과 같은 계층 구조를 따릅니다.

Controller → Service → Repository → Database

각 계층은 다음과 같은 역할을 담당합니다.

계층 역할 설명
Controller 외부 요청을 받아, 어느 서비스를 호출할 것인지를 판단합니다.
Service 핵심 비즈니스 로직을 담당합니다.
Repository DB 접근과 영속성 관련 작업을 담당합니다.
Database 실제 데이터를 저장하는 저장소입니다.

이러한 구조는 관심사의 분리(SoC, Separation of Concerns)를 기반으로 하며, 각 계층은 자신의 역할에만 집중할 수 있도록 설계되어 있습니다.

🔁 계층 간 의존성 규칙

우리는 레이어드 아키텍처의 단방향 의존성을 원칙으로 삼습니다. 즉, 상위 계층은 하위 계층을 참조할 수 있지만, 하위 계층이 상위 계층을 참조하는 역방향 참조는 금지합니다.

  • ❌ 허용되지 않는 예
    • Controller → 다른 Controller 참조 ❌
    • Service → Controller 참조 ❌
    • Repository → Service 또는 Controller 참조 ❌

🚫 Controller 간 참조 금지

Controller는 단순히 외부 요청을 받아 처리 흐름을 시작하는 역할만 하므로,
다른 Controller를 직접 참조하거나 호출하는 것은 금지합니다.

따라서 컨트롤러 클래스는 public으로 선언하지 않고, default(package-private)로 선언하여
외부 참조를 명시적으로 차단합니다.

@RestController
class AuthController {
    // public ❌, class-level default 접근제한자 사용
}

2. 서비스 간 의존성과 파사드 계층

기본적으로 Service 클래스는 서로를 직접 참조하지 않도록 설계합니다. 서비스 간 의존이 많아질 경우 다음과 같은 문제가 발생할 수 있습니다.

  • 서비스 간 강한 결합도 (tight coupling)
  • 순환 참조 (circular dependency)
  • 단일 책임 원칙 위반
  • 테스트 및 유지보수의 어려움

그래서 도입하는 파사드(Facade) 계층을 도입하고자 합니다.

여러 서비스가 협력해야 하는 복잡한 비즈니스 흐름이 있을 경우, 컨트롤러와 서비스 사이에 파사드 계층을 두어 서비스들을 조율하는 역할을 맡깁니다.

Controller
   ↓
Facade (서비스 간 조율)
   ↓
Service1   Service2   ...
   ↓         ↓
Repository  Repository

🧩 파사드 계층의 특징

|항목|설명| |역할|여러 서비스를 조합하여 하나의 흐름을 형성| |위치|컨트롤러와 서비스 사이. @Service로 명시| |장점|각 서비스 마다 독립성 유지 + 복잡한 흐름 처리 가능| |트랜잭션 관리|파사드 계층을 통해 전체 트랜잭션 범위 제어| |예외 처리|하나의 기능 단위로 예외 처리 용이|

@Service말고 명확한 구분이 필요하다면 커스텀 어노테이션 @Facade 생성을 해도 좋을 것 같습니다.

예시

@Service
@RequiredArgsConstructor
public class AuthFacade {

    private final UserService userService;
    private final CouponService couponService;

    @Transactional
    public void signUp(final SignUpRequest request) {
        userService.register(request));
        couponService.issue(CouponType.ISSUE, request);
    }
}

만약 서비스간의 참조가 가능했더라면, UserService.register 내부에서 회원가입 기념 쿠폰 발급을 위해, CouponService.signupIssue를 참조해야 할 것입니다.
파사드 계층을 이용하게 되면 컨트롤러는 AuthFacade.signUp()만 호출하면 되며, 내부적으로 필요한 서비스들이 조합되어 처리됩니다.
각 서비스는 다른 서비스의 의존 없이 단일적으로 처리되게 됩니다.

⚠️ **GitHub.com Fallback** ⚠️