# Architecture ## Context & Scope 이 문서는 `Project-Auth-Server`의 레이어 구조와 조립 방식을 설명합니다. 핵심 질문은 "왜 이 구조를 택했고, 실제로 의존 방향을 어떻게 통제하는가"입니다. 문서 작성 기준은 [docs/documentation-guide.md](../documentation-guide.md)를 따릅니다. ### Scope - 포함: 레이어 책임, 의존 방향, 웹 앱과 migration 앱의 조립 방식 - 제외: 개별 인증 플로우, 운영 환경별 GitOps 선언 상세 ## Why 이 프로젝트는 인증/인가 서버이기 때문에 비즈니스 규칙, HTTP 계약, 외부 기술 구현이 빠르게 섞이기 쉽습니다. 특히 보안, DB migration, 외부 시스템 연동이 함께 존재하면 "편해서 한 군데에 몰아넣은 코드"가 빠르게 늘어납니다. 이 구조가 해결하려는 문제는 다음과 같습니다. - 도메인 규칙이 Spring MVC, JPA, 보안 프레임워크에 오염되는 문제 - 응답 계약과 비즈니스 규칙이 뒤섞이는 문제 - migration 같은 운영성 실행을 웹 애플리케이션과 강하게 결합하는 문제 - 기술 교체 영향 범위를 좁히기 어려운 문제 ## What ### 한눈에 보는 의존 흐름 ```mermaid flowchart LR Bootstrap[bootstrap] Presentation[presentation] Infrastructure[infrastructure] Application[application] Domain[domain] Bootstrap --> Presentation Bootstrap --> Infrastructure Bootstrap --> Application Bootstrap -. compileOnly .-> Domain Presentation --> Application Infrastructure --> Application Infrastructure --> Domain Application --> Domain ``` 핵심은 **의존 방향이 항상 안쪽으로만 흐른다**는 점입니다. - `domain`은 가장 안쪽에 있고 아무 것도 모릅니다. - `application`은 유스케이스와 포트를 통해 도메인 규칙을 조합합니다. - `presentation`, `infrastructure`는 바깥쪽 어댑터로서 `application`에 의존합니다. - `bootstrap`은 실제 Spring Boot 실행 모듈로, 바깥 레이어들을 조립합니다. ### 레이어별 책임 ### domain 도메인 모델과 순수 규칙만 둡니다. - 예: `User`, `UserEmail`, `UserName`, `UserPasswordPolicy` - 금지: Spring annotation, JPA annotation, HTTP/Servlet 타입 ### application “무슨 일을 한다”를 담당합니다. - 유스케이스 - 커맨드/결과 DTO - 포트(`port/in`, `port/out`) - 비즈니스 에러 코드/예외 여기서는 더 이상 HTTP status를 다루지 않습니다. 에러는 `code`, `message`만 가지고 있고, HTTP status 매핑은 바깥쪽 `presentation`에서 담당합니다. ### presentation HTTP 입출력과 API 응답 계약만 담당합니다. - Controller - Request/Response DTO - `ApiResult` - `ApiSuccessCode` - `ValidationExceptionHandler` - `RequestExceptionHandler` - `ApplicationExceptionHandler` - `ApiErrorHttpStatusMapper` 즉 API 응답 모양과 HTTP status는 이 레이어의 책임입니다. ### infrastructure 기술 구현체만 담당합니다. - JPA repository adapter - persistence mapper/entity - password encoder adapter - JWT/Vault integration 즉 `application`이 정의한 포트를 실제 기술로 연결합니다. ### bootstrap 전체를 조립합니다. - Spring Boot entrypoint - configuration - security wiring - `presentation`이 직접 의존할 수 없는 기술 예외를 HTTP 경계에서 번역하는 bootstrap 전용 web adapter - migration 전용 app entrypoint 이 프로젝트에서는 일반 웹 애플리케이션 진입점과 별도로 `MigrationApplication`을 둬서 DB migration을 전용 실행 단위로 분리했습니다. `InfrastructureExceptionHandler`가 bootstrap에 있는 이유도 여기 있습니다. 이 핸들러는 HTTP 응답을 만들지만, 이를 presentation으로 옮기면 `presentation -> infrastructure` 의존이 생겨 현재 ArchUnit 규칙을 깨게 됩니다. 그래서 security filter chain 예외나 `InfrastructureException`처럼 presentation이 직접 알 수 없는 타입을 HTTP로 번역하는 adapter는 bootstrap이 맡습니다. ## How ### 웹 앱과 migration 앱의 조립 ```mermaid flowchart TD Web[AuthApplication] Migration[MigrationApplication] App[application] Infra[infrastructure] Pres[presentation] Domain[domain] DB[(PostgreSQL)] Web --> Pres Web --> App Web --> Infra Pres --> App Infra --> App Infra --> Domain App --> Domain Infra --> DB Migration --> App Migration --> Infra Migration --> DB ``` 이 구조의 의도는 이렇습니다. - 웹 앱은 `presentation + application + infrastructure`를 조립 - migration 앱은 웹 어댑터 없이 DB migration만 수행 즉 migration을 위해 auth-server 전체 웹 컨텍스트를 억지로 띄우지 않도록 분리했습니다. ### 레이어 규칙을 어떻게 유지하는가 구조는 설명만으로 유지되지 않기 때문에, ArchUnit 테스트로 핵심 규칙을 검증합니다. - 위치: `bootstrap/src/test/java/com/project/auth/architecture/LayerDependencyArchitectureTest.java` 현재 강제하는 규칙: - `domain`은 Spring/JPA/Servlet에 의존하지 않는다 - `application`은 `presentation`/`infrastructure`에 의존하지 않는다 - `presentation`은 `domain`/`infrastructure`에 직접 의존하지 않는다 - `bootstrap`만 `config` 패키지를 조립 지점으로 사용한다 즉 이 문서는 "설계 설명"이고, ArchUnit은 "설계 위반 방지 장치"입니다. ## Alternatives Considered ### 대안 1: 패키지 수준 구분만 두고 단일 모듈에 집중 - 장점: 초기 개발 속도가 빠르고 진입 장벽이 낮습니다. - 단점: 시간이 지나면 HTTP, DB, 도메인 규칙이 같은 계층에서 섞이기 쉽습니다. - 결론: 지금 프로젝트처럼 인증, 보안, 외부 시스템 연동이 많은 경우 경계가 빨리 무너질 가능성이 높아 채택하지 않았습니다. ### 대안 2: 웹 앱과 migration을 같은 진입점에서 처리 - 장점: 실행 경로가 단순합니다. - 단점: migration만 수행해도 웹 관련 빈과 설정이 함께 로딩될 수 있습니다. - 결론: 운영 절차를 분리하고 실패 반경을 줄이기 위해 별도 `MigrationApplication`을 유지합니다. ## Result / Trade-offs 이 구조로 얻는 이점은 다음과 같습니다. - 도메인/유스케이스가 웹 프레임워크에 오염되지 않습니다. - 기술 교체 영향 범위를 주로 `infrastructure`로 제한할 수 있습니다. - 응답 계약과 비즈니스 규칙의 경계를 분명히 할 수 있습니다. - migration 같은 운영성 실행을 별도 진입점으로 분리할 수 있습니다. 동시에 감수하는 비용도 있습니다. - 모듈과 패키지 경계를 지키기 위한 학습 비용이 있습니다. - 단기적으로는 파일 수와 설정 포인트가 늘어납니다. - 구조를 설명하는 문서와 ArchUnit 테스트를 함께 관리해야 합니다.