From 230803348df42841db0fcfaa53ba9bcfb01c56f3 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Tue, 14 Jul 2026 11:24:30 +0000 Subject: [PATCH] =?UTF-8?q?TA=20=EC=97=AD=ED=95=A0=20Spring=20=EA=B2=BD?= =?UTF-8?q?=EA=B3=84=20smoke=20(runtime-role-matrix-live-20260714112301-v1?= =?UTF-8?q?1-ta-001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ADR-001-spring-boundary-architecture.md | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 docs/adr/ADR-001-spring-boundary-architecture.md diff --git a/docs/adr/ADR-001-spring-boundary-architecture.md b/docs/adr/ADR-001-spring-boundary-architecture.md new file mode 100644 index 0000000..d500810 --- /dev/null +++ b/docs/adr/ADR-001-spring-boundary-architecture.md @@ -0,0 +1,200 @@ +# ADR-001: Spring 경계 아키텍처 정의 + +## Context + +runtime-role-matrix-live 프로젝트는 역할(Role) 기반 접근 제어 시스템을 구현한다. 현재 계층화 아키텍처의 명확한 경계가 정의되어 있지 않아 다음과 같은 문제가 발생한다. + +- **응집도 부족**: Controller에서 비즈니스 로직 직접 수행 +- **결합도 증가**: Service 간 직접 의존으로 단위 테스트 어려움 +- **트랜잭션 범위 모호**: Repository 호출 시 트랜잭션 전파 정책 불명확 +- **오류 처리 불일치**: 각 계층별 예외 처리 방식 상이 + +### 현재 시스템 범위 + +| 계층 | 책임 | +|------|------| +| Controller | HTTP 요청/응답 변환, 입력 검증, 라우팅 | +| Service | 비즈니스 로직, 트랜잭션 경계, 도메인 조율 | +| Repository | 데이터 접근 추상화, 쿼리 실행 | + +--- + +## Decision + +### 1. Controller-Service-Repository 경계 정의 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Controller Layer │ +│ - HTTP 요청 파라미터 바인딩 및 검증 (@Valid) │ +│ - HTTP 응답 변환 (DTO → ResponseEntity) │ +│ - 예외 → HTTP 상태码 매핑 │ +│ - 트랜잭션 경계에 참여하지 않음 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Service Layer │ +│ - @Transactional 메서드 단위 트랜잭션 경계 │ +│ - 비즈니스 규칙 및 도메인 로직 실행 │ +│ - 다중 Repository 조율 │ +│ - 도메인 객체 생성 및 상태 관리 │ +│ -Checked Exception → Unchecked Exception 변환 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Repository Layer │ +│ - JPA Repository (JpaRepository) 상속 │ +│ - @Query 기반 커스텀 쿼리 │ +│ - 도메인 엔티티 직접 반환 │ +│ - 트랜잭션 읽기 전용 (readOnly=true) 활용 │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 2. 오류 계약 (Error Contract) + +#### 예외 계층 구조 + +``` +RuntimeException (java.lang) + │ + ├── RoleNotFoundException → HTTP 404 + ├── RoleAlreadyExistsException → HTTP 409 + ├── InvalidRoleStateException → HTTP 400 + └── PermissionDeniedException → HTTP 403 +``` + +#### 오류 응답 형식 + +```json +{ + "timestamp": "2026-07-14T11:23:01Z", + "status": 404, + "error": "Not Found", + "code": "ROLE_NOT_FOUND", + "message": "Role with id '123' does not exist", + "path": "/api/v1/roles/123" +} +``` + +#### 전역 예외 처리 규칙 + +| 예외 유형 | HTTP 상태 | 응답 코드 | +|-----------|-----------|-----------| +| RoleNotFoundException | 404 | ROLE_NOT_FOUND | +| RoleAlreadyExistsException | 409 | ROLE_ALREADY_EXISTS | +| InvalidRoleStateException | 400 | INVALID_ROLE_STATE | +| PermissionDeniedException | 403 | PERMISSION_DENIED | +| MethodArgumentNotValidException | 400 | VALIDATION_ERROR | +| 기타 RuntimeException | 500 | INTERNAL_ERROR | + +### 3. 트랜잭션 경계 정책 + +#### 기본 원칙 + +| 작업 유형 | 전파 정책 | readOnly | +|-----------|-----------|----------| +| 조회 (Read) | REQUIRED | true | +| 생성 (Create) | REQUIRED | false | +| 수정 (Update) | REQUIRED | false | +| 삭제 (Delete) | REQUIRED | false | + +#### Service 클래스 설계 + +```java +@Service +@Transactional(readOnly = true) +public class RoleService { + + @Transactional(readOnly = false) + public Role createRole(CreateRoleRequest request) { + // 비즈니스 로직 + } + + @Transactional(readOnly = false) + public Role updateRole(Long id, UpdateRoleRequest request) { + // 비즈니스 로직 + } + + public Role findById(Long id) { + // readOnly=true 상속 + } +} +``` + +#### 격리 수준 + +- **기본값**: READ_COMMITTED +- **필요 시**: @Transactional(isolation = Isolation.SERIALIZABLE) + +--- + +## Alternatives + +### 대안 1: Transactional死在 Controller + +```java +@RestController +@Transactional +public class RoleController { ... } +``` + +| 항목 | 결함 | +|------|------| +| 문제점 | HTTP 요청/응답 스레드와 트랜잭션 결합 | +| 결과 | 롤백 시 응답 불가 상태 발생 가능 | + +### 대안 2: Service 계층 생략 (Transaction Script) + +```java +@RestController +public class RoleController { + @Autowired RoleRepository repository; + public Role create(...) { ... } +} +``` + +| 항목 | 결함 | +|------|------| +| 문제점 | 복잡한 도메인 로직 축적 시 재사용 어려움 | +| 결과 | Controller 비대화, 테스트 어려움 | + +### 대안 3: Checked Exception 직접 전파 + +| 항목 | 결함 | +|------|------| +| 문제점 | 호출자에게 예외 처리 강제, 결합도 증가 | +| 결과 | Service 교체 시 Caller 코드 수정 필요 | + +--- + +## Consequences + +### 긍정적 결과 + +- **단위 테스트 용이성**: Service를 순수 Java로 테스트 가능 +- **일관된 오류 처리**: 전역 @ControllerAdvice로 중앙화 +- **트랜잭션 명확성**: 메서드 단위 경계로 디버깅 용이 +- **유지보수성**: 계층별 책임 분리 + +### 부정적 결과 + +- **추가 코드 작성**: DTO, Exception, Mapper 클래스 증가 +- **학습 곡선**: 개발자별 아키텍처 이해 필요 +- **성능 오버헤드**: Proxy 기반 AOP 약간의 지연 (미미) + +### 모니터링 필요 항목 + +- 트랜잭션 롤백 빈도 +- 예외 발생 패턴 (ROLE_NOT_FOUND 등) +- Service 메서드 응답 시간 + +--- + +## 참고 + +- Java: 17+ +- Spring Boot: 3.2.x +- JPA: Hibernate 6.x +- 빌드 도구: Maven