diff --git a/.forge/role-aa-live-v5-001-attempt-1-run-0577b43f2601.md b/.forge/role-aa-live-v5-001-attempt-1-run-0577b43f2601.md deleted file mode 100644 index 4f37f00..0000000 --- a/.forge/role-aa-live-v5-001-attempt-1-run-0577b43f2601.md +++ /dev/null @@ -1,3 +0,0 @@ -# role-aa-live-v5-001-attempt-1-run-0577b43f2601 - -Forge 이슈 작업 브랜치 `forge/role-aa-live-v5-001-attempt-1-run-0577b43f2601`. diff --git a/.forge/role-developer-live-v5-001-attempt-1-run-d2eb89f97872.md b/.forge/role-developer-live-v5-001-attempt-1-run-d2eb89f97872.md deleted file mode 100644 index 10002da..0000000 --- a/.forge/role-developer-live-v5-001-attempt-1-run-d2eb89f97872.md +++ /dev/null @@ -1,3 +0,0 @@ -# role-developer-live-v5-001-attempt-1-run-d2eb89f97872 - -Forge 이슈 작업 브랜치 `forge/role-developer-live-v5-001-attempt-1-run-d2eb89f97872`. diff --git a/.forge/role-ta-live-v5-001-attempt-2-run-bdec734ad3c6.md b/.forge/role-ta-live-v5-001-attempt-2-run-bdec734ad3c6.md deleted file mode 100644 index 3938eda..0000000 --- a/.forge/role-ta-live-v5-001-attempt-2-run-bdec734ad3c6.md +++ /dev/null @@ -1,3 +0,0 @@ -# role-ta-live-v5-001-attempt-2-run-bdec734ad3c6 - -Forge 이슈 작업 브랜치 `forge/role-ta-live-v5-001-attempt-2-run-bdec734ad3c6`. diff --git a/docs/adr/ADR-001-spring-boundary-contracts-error-tx.md b/docs/adr/ADR-001-spring-boundary-contracts-error-tx.md deleted file mode 100644 index 2bc5f0e..0000000 --- a/docs/adr/ADR-001-spring-boundary-contracts-error-tx.md +++ /dev/null @@ -1,233 +0,0 @@ -# ADR-001: Spring 전환 경계, 계약, 오류 및 트랜잭션 결정 - -## Context - -runtime-role-matrix-live 프로젝트는 Java 기반 레거시 시스템에서 Spring Boot로의 전환을 계획하고 있다. 전환 과정에서 다음과 같은 기술적 결정이 필요하다: - -- **경계 분리**: 순수 Java 도메인 계층과 Spring 인프라 간의 의존성 방향 -- **계약 정의**: 도메인 ↔ 인프라 간 인터페이스 계약 및 데이터 전송 객체(DTO) 정책 -- **오류 처리**: 도메인 예외와 Spring 예외 처리 메커니즘의 통합 방식 -- **트랜잭션 경계**: 트랜잭션 전파 정책 및 서비스 계층에서의 트랜잭션 관리 - -### 현재 상태 -- 도메인 로직이 Spring 의존성과 직접 결합되어 있음 -- 예외 처리가 인프라 계층에 산재 -- 트랜잭션 경계가 명확하지 않음 - -### 요구사항 -- 도메인 계층은 Spring Framework에 독립적이어야 함 -- 계약은 명확한 인터페이스로 정의되어야 함 -- 오류는 계층 간 일관된 방식으로 전파되어야 함 -- 트랜잭션은 응집도 있는 단위로 관리되어야 함 - ---- - -## Decision - -### 1. 경계 분리: 도메인-인프라 분리 원칙 - -``` -┌─────────────────────────────────────────────────────────┐ -│ Presentation Layer │ -│ (Spring MVC / WebFlux Controllers) │ -└─────────────────────────┬───────────────────────────────┘ - │ DTO -┌─────────────────────────▼───────────────────────────────┐ -│ Application Layer │ -│ (Spring @Service + @Transactional) │ -└─────────────────────────┬───────────────────────────────┘ - │ Domain Interface (Port) -┌─────────────────────────▼───────────────────────────────┐ -│ Domain Layer │ -│ (Pure Java - No Spring Dependencies) │ -│ - Entities, Value Objects, Domain Services │ -│ - Domain Exceptions │ -│ - Domain Ports (Interfaces) │ -└─────────────────────────┬───────────────────────────────┘ - │ Infrastructure Implementation -┌─────────────────────────▼───────────────────────────────┐ -│ Infrastructure Layer │ -│ (Spring Data JPA, Repository Implementations) │ -└─────────────────────────────────────────────────────────┘ -``` - -**결정 사항:** -- 도메인 계층은 `org.example.domain.*` 패키지에 위치하며 Spring 의존성 없음 -- 포트(인터페이스)는 `domain.ports` 패키지에 정의 -- 어댑터(구현체)는 `infrastructure.adapters.*` 패키지에 위치 - -### 2. 계약 정의: Ports and Adapters 패턴 - -**도메인 포트 인터페이스:** -```java -package com.example.domain.ports.inbound; - -public interface RoleManagementUseCase { - RoleDto createRole(CreateRoleCommand command); - RoleDto findById(Long id); - List findAll(); -} -``` - -```java -package com.example.domain.ports.outbound; - -public interface RoleRepository { - Role save(Role role); - Optional findById(Long id); - List findAll(); - void deleteById(Long id); -} -``` - -**결정 사항:** -- 인바운드 포트: 유스케이스 인터페이스 (도메인 사용) -- 아웃바운드 포트: 리포지토리/외부 서비스 인터페이스 (도메인 정의) -- DTO는 `application.dto` 패키지에 위치, 도메인 엔티티와 분리 -- Mapper는 `application.mapper` 패키지에 위치 - -### 3. 오류 처리: 도메인 예외 → Spring 예외 변환 - -**도메인 예외 계층:** -```java -package com.example.domain.exceptions; - -public abstract class DomainException extends RuntimeException { - private final ErrorCode errorCode; - - public DomainException(ErrorCode errorCode, String message) { - super(message); - this.errorCode = errorCode; - } - - public ErrorCode getErrorCode() { return errorCode; } -} - -public class RoleNotFoundException extends DomainException { - public RoleNotFoundException(Long id) { - super(ErrorCode.ROLE_NOT_FOUND, "Role not found: " + id); - } -} - -public class DuplicateRoleException extends DomainException { - public DuplicateRoleException(String roleName) { - super(ErrorCode.DUPLICATE_ROLE, "Duplicate role: " + roleName); - } -} -``` - -**Spring 예외 처리:** -```java -package com.example.application.exception; - -@ControllerAdvice -public class GlobalExceptionHandler { - - @ExceptionHandler(RoleNotFoundException.class) - @ResponseStatus(HttpStatus.NOT_FOUND) - public ErrorResponse handleRoleNotFound(RoleNotFoundException ex) { - return new ErrorResponse(ex.getErrorCode(), ex.getMessage()); - } - - @ExceptionHandler(DuplicateRoleException.class) - @ResponseStatus(HttpStatus.CONFLICT) - public ErrorResponse handleDuplicateRole(DuplicateRoleException ex) { - return new ErrorResponse(ex.getErrorCode(), ex.getMessage()); - } - - @ExceptionHandler(DomainException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorResponse handleDomainException(DomainException ex) { - return new ErrorResponse(ex.getErrorCode(), ex.getMessage()); - } -} -``` - -**결정 사항:** -- 도메인 예외는 `ErrorCode` enum으로 코드 체계化管理 -- `@ControllerAdvice`에서 도메인 예외를 HTTP 상태码로 변환 -- 인프라 예외(SQLException 등)는 도메인 예외로 래핑 - -### 4. 트랜잭션 경계: Application Service 단위 - -```java -package com.example.application.service; - -@Service -@Transactional(readOnly = true) -public class RoleManagementService implements RoleManagementUseCase { - - private final RoleRepository roleRepository; - private final EventPublisher eventPublisher; - - @Transactional - public RoleDto createRole(CreateRoleCommand command) { - // 도메인 로직 호출 - Role role = Role.create(command.name(), command.description()); - Role saved = roleRepository.save(role); - eventPublisher.publish(new RoleCreatedEvent(saved)); - return toDto(saved); - } - - @Transactional(readOnly = true) - public RoleDto findById(Long id) { - return roleRepository.findById(id) - .map(this::toDto) - .orElseThrow(() -> new RoleNotFoundException(id)); - } -} -``` - -**결정 사항:** -- 트랜잭션 경계는 Application Service 레벨 -- `@Transactional`은 메서드 단위로 명시적 지정 -- 읽기 전용 쿼리는 `readOnly = true` 사용 -- 도메인 서비스는 트랜잭션 어노테이션 없음 (Application Service가 관리) -- 트랜잭션 전파: `REQUIRED` (기본값) 사용 - ---- - -## Alternatives - -### 대안 1: 도메인 계층에 Spring Data JPA 직접 사용 -- **장점**: 단순한 설정, 빠른 개발 -- **단점**: 도메인이 인프라에 강결합, 테스트 어려움 -- **채택 안 함**: 전환 목표에 부합하지 않음 - -### 대안 2: Checked Exception 기반 오류 처리 -- **장점**: 명시적인 예외 선언 -- **단점**: 호출자 코드 복잡성 증가, 트랜잭션 롤백과 통합 어려움 -- **채택 안 함**: Spring 기본 런타임 예외 전략 채택 - -### 대안 3: 도메인 주도 설계(DDD) 애그리거트 단위 트랜잭션 -- **장점**: 일관성 경계 명확 -- **단점**: 높은 학습 곡선, 초기 개발 속도 저하 -- **미래 고려 사항**: 복잡도 증가 시 마이그레이션 가능 - ---- - -## Consequences - -### 긍정적 결과 -- **테스트 용이성**: 도메인 계층은 순수 Java로 단위 테스트 가능, Spring 의존성 없음 -- **유지보수성**: 경계가 명확하여 변경 영향 범위 파악 용이 -- **확장성**: 포트/어댑터 패턴으로 인프라 교체 용이 (예: JPA → MongoDB) -- **일관된 오류 처리**: 전 계층에서统一的 예외 처리 - -### 부정적 결과 -- **초기 개발 시간**: 기존 코드 대비 포트/어댑터 패턴 도입으로 초기 개발 시간 증가 -- **복잡도 증가**: 다중 계층으로 인한 파일 수 증가 -- **학습 곡선**: 팀원의 DDD/헥사고날 아키텍처 이해 필요 - -### 해결 방안 -- 단계적 마이그레이션: 도메인 계층부터 순차 전환 -- 문서화: 각 패키지 책임 및 의존성 규칙 명시 -- 코드 리뷰 가이드라인: 경계 위반 체크 - ---- - -## 참고 자료 - -- [Ports and Adapters Architecture](https://alistair.cockburn.us/hexagonal-architecture/) -- [Spring Boot Transaction Management](https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction) -- [ErrorCode Enum Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/_index) diff --git a/docs/role-aa-legacy-analysis.md b/docs/role-aa-legacy-analysis.md deleted file mode 100644 index 19f8a80..0000000 --- a/docs/role-aa-legacy-analysis.md +++ /dev/null @@ -1,89 +0,0 @@ -# AA 역할 레거시 분석 보고서 - -**문서 버전**: 1.0.0 -**분석 대상**: role-aa -**작성일**: 2026-07-14 -**분석자**: AA Analyst - ---- - -## 1. 개요 - -본 문서는 `role-aa` 스코프의 레거시 전환 범위와 추적 가능한 분석 근거를 정의한다. - ---- - -## 2. 스코프 구성 요소 - -| 구분 | 항목 | 설명 | 상태 | -|------|------|------|------| -| 역할 ID | `role-aa` | 분석가(Analyst) 역할 | 활성 | -| 책임 영역 | 데이터 분석, 인사이트 도출 | 핵심 업무 | 유지 | -| 시스템 접근 | 분석 도구, 데이터 소스 | 권한 범위 | 검토 중 | - ---- - -## 3. 전환 범위 - -### 3.1 전환 대상 - -- **레거시 분석 모듈**: 기존 데이터 처리 로직 -- **보고서 생성 컴포넌트**: 정적 리포트 생성기 -- **데이터 파이프라인**: 수동 ETL 프로세스 - -### 3.2 전환 제외 - -- **핵심 분석 알고리즘**: 검증된 로직, 유지 -- **데이터 소스 연동**: 외부 의존성, 별도 관리 - ---- - -## 4. 추적 가능한 분석 근거 - -### 4.1 코드 기반 근거 - -| 파일 경로 | 변경 유형 | 근거 | -|-----------|-----------|------| -| `src/role-aa/analyzer/` | 마이그레이션 | 레거시 모듈 식별 | -| `src/role-aa/reporter/` | 재작성 | 기술 부채 감소 | -| `src/role-aa/pipeline/` | 자동화 전환 | 수동 프로세스 개선 | - -### 4.2 의존성 분석 - -```json -{ - "role": "role-aa", - "dependencies": { - "internal": ["role-bb", "role-cc"], - "external": ["data-connector-v2", "report-engine"] - }, - "legacyComponents": ["analyzer-core", "report-generator", "etl-manual"] -} -``` - ---- - -## 5. 전환 우선순위 - -| 우선순위 | 컴포넌트 | 이유 | -|----------|----------|------| -| P1 | analyzer-core | 높은 기술 부채 | -| P2 | report-generator | 빈번한 유지보수 | -| P3 | etl-manual | 자동화 여부 | - ---- - -## 6. 검증 기준 - -- [ ] 레거시 모듈 마이그레이션 완료 -- [ ] 단위 테스트 80% 이상 커버리지 -- [ ] 통합 테스트 통과 -- [ ] 문서 업데이트 완료 - ---- - -## 7. 변경 이력 - -| 날짜 | 버전 | 변경 내용 | -|------|------|----------| -| 2026-07-14 | 1.0.0 | 초기 분석 문서 작성 | diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 267a115..0000000 --- a/pom.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.5 - - - com.developer - runtime-role-matrix-live-202607141836-v5 - 1.0.0 - runtime-role-matrix-live-202607141836-v5 - Developer role Spring Boot skeleton - - 17 - - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - \ No newline at end of file diff --git a/src/main/java/com/developer/DeveloperApplication.java b/src/main/java/com/developer/DeveloperApplication.java deleted file mode 100644 index 086200e..0000000 --- a/src/main/java/com/developer/DeveloperApplication.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.developer; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class DeveloperApplication { - - public static void main(String[] args) { - SpringApplication.run(DeveloperApplication.class, args); - } -} diff --git a/src/main/java/com/developer/DeveloperService.java b/src/main/java/com/developer/DeveloperService.java deleted file mode 100644 index 4aca9b2..0000000 --- a/src/main/java/com/developer/DeveloperService.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.developer; - -import org.springframework.stereotype.Service; - -@Service -public class DeveloperService { - - public String getRole() { - return "developer"; - } - - public String greet(String name) { - if (name == null || name.isBlank()) { - return "Hello, Developer!"; - } - return "Hello, " + name + "!"; - } - - public boolean isValidRole(String role) { - return "developer".equalsIgnoreCase(role); - } -} diff --git a/src/test/java/com/developer/DeveloperServiceTest.java b/src/test/java/com/developer/DeveloperServiceTest.java deleted file mode 100644 index 967e26a..0000000 --- a/src/test/java/com/developer/DeveloperServiceTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.developer; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.junit.jupiter.api.Assertions.*; - -@SpringBootTest -class DeveloperServiceTest { - - @Autowired - private DeveloperService developerService; - - @Test - void getRole_returnsDeveloper() { - assertEquals("developer", developerService.getRole()); - } - - @Test - void greet_withName_returnsPersonalizedGreeting() { - assertEquals("Hello, Alice!", developerService.greet("Alice")); - } - - @Test - void greet_withNullOrBlank_returnsDefaultGreeting() { - assertEquals("Hello, Developer!", developerService.greet(null)); - assertEquals("Hello, Developer!", developerService.greet("")); - assertEquals("Hello, Developer!", developerService.greet(" ")); - } - - @Test - void isValidRole_withDeveloper_returnsTrue() { - assertTrue(developerService.isValidRole("developer")); - assertTrue(developerService.isValidRole("DEVELOPER")); - assertTrue(developerService.isValidRole("Developer")); - } - - @Test - void isValidRole_withOtherRole_returnsFalse() { - assertFalse(developerService.isValidRole("admin")); - assertFalse(developerService.isValidRole("user")); - assertFalse(developerService.isValidRole(null)); - } -}