Files
llm-wiki/raw/interviews/transaction-port-vs-spring-transactional.md

11 KiB

title, source_type, status, related_branches, related_projects, tags, created, status_label
title source_type status related_branches related_projects tags created status_label
interview-prep / transaction-port-vs-spring-transactional interview-prep raw
feature-application-port-usecase-contract
ca-skeleton
interview-prep
ca-skeleton
transaction
hexagonal
spring
transactional
transaction-port
2026-05-28 collecting

interview-prep: transaction-port-vs-spring-transactional

Layer: raw/interviews/ — 면접 질문 원본 수집·연구 노트. 다듬어진 답변은 /interviewizewiki/interview/ 에 별도 작성.

Parent / 부모

질문 / Question

  • 질문 원문: Spring 프로젝트에서 application 계층이 @Transactional직접 부착하지 않고 TransactionPort 같은 추상화로 감싸는 선택의 trade-off 를 설명해 보세요.
  • 출처: 예상 질문 (실 면접 아님).
  • 받은 날짜·맥락: 아직 없음. Clean Architecture / Hexagonal 패턴 경험 검증용 질문 후보.

질문 의도 추론 / Why this question

  • 핵심 평가 대상:
    • Clean Architecture / Hexagonal 을 했다는 선언이 framework leakage 차단 수준까지 갔는지 변별.
    • @Transactionalself-invocation 함정 인지.
    • 다수파 (@Transactional 직접) vs 소수파 (TransactionPort) 의 둘 다 합리적 임을 인식하는지.
    • 추상이 enforce 되는 형태 (ArchUnit fitness function) 가 함께 있어야 의미가 있다는 점을 알고 있는지.
  • 함정 / 흔히 빠지는 답변 패턴:
    • "Clean Architecture 라서 추상화" 만 답하고 구체 이득 (testability, self-invocation 회피, Spring 의존 surface 축소) 을 설명 못함.
    • "boilerplate 가 늘어서 안 쓰는 게 낫다" 만 답하고 다수파의 proxy leak / self-invocation 위험 인식 못함.
    • "TransactionPort 가 더 좋다" 같이 한쪽을 우월 로 표현 — 실제로는 맥락 의존 결정.
  • 따라올 만한 후속 질문:
    • 다수파 (@Transactional 직접) 입장이 reasonable 한가요?
    • TransactionPortREQUIRES_NEW / noRollbackFor / timeout 까지 표현 가능해야 한다면 API 가 어떻게 커지나요?
    • TransactionTemplate 기반 구현과 @Transactional AOP proxy 기반 구현 중 어느 쪽이 self-invocation 함정 에서 자유롭나요? 왜요?
    • TransactionPort 가 없으면 application 단위 테스트에서 transaction 동작을 어떻게 모킹 / fake 합니까?
    • application-core 의 Gradle 에서 spring-tx제거 했는데, 왜요?

답변 재료 / Raw answer material

  • 사실 1 (근거: feature-application-port-usecase-contract.md D3 + raw/company-tech-blogs/transaction-port-clean-ddd-spring-medium UNIL-TX-C1 + raw/company-tech-blogs/transaction-port-vassilis-soum-github-readme VSOUM-TX-C1): application 계층은 outbound port (TransactionPort) 만 호출하고, Spring @Transactional 의 직접 import 는 forbidden. Spring 의존은 infrastructure 어댑터 (SpringTransactionPort) 에 격리.
  • 사실 2 (근거: raw/official-docs/at-transactional-spring-official AT-TX-C5): Spring @Transactional AOP proxy 의 self-invocation 함정 — 같은 클래스의 메서드가 this.otherMethod() 형태로 호출되면 proxy 를 우회해서 transaction 이 적용되지 않음. TransactionTemplate 기반 추상화는 proxy 가 아니므로 영향 없음.
  • 사실 3 (근거: raw/company-tech-blogs/hexagonal-reflectoring-transactional-placement HEX-REFL-C1, HEX-REFL-C5): Hexagonal 표준 다수파@Transactional 을 application service 에 직접 부착. 이유는 boilerplate 최소화 + Spring 공식 권고 (AT-TX-C1) 정합성. 다만 HEX-REFL-C5 는 negative claim — 저자가 자기 결정에 대한 명시적 정당화 없이 단순 채택.
  • 사실 4 (근거: raw/official-docs/spring-tx-management-reference SPRING-TX-MGR-C6): readOnly = true 는 REQUIRED / REQUIRES_NEW propagation 에 한정 해서 적용. ca-tmpl 의 TransactionPort.inRead 도 이 제약을 따라 REQUIRED + readOnly 로 구현.
  • 사실 5 (근거: feature-application-port-usecase-contract.md Decisions 2026-05-28): application-core 의 Gradle 에서 spring-tx제거 해서 @Transactional 어노테이션이 컴파일 classpath 자체에서 reach 불가능. ArchUnit rule 과 belt+suspenders.
  • 사실 6 (근거: 동일 branch-note Decisions 2026-05-28 + 구현 결과): SpringTransactionPort 는 mode 별로 미리 빌드된 TransactionTemplate 인스턴스 3개 (writeTemplate, readTemplate, requiresNewTemplate) 를 보관해서 호출 시점의 mutation 없이 동시성 안전 보장.
  • 사실 7 (근거: 동일 branch-note Claims to Verify "application package의 ArchUnit rule" 행 → actually-implemented): noClasses().that().resideInAPackage("..application..").should().dependOnClassesThat().haveFullyQualifiedName("org.springframework.transaction.annotation.Transactional") 로 application 의 @Transactional import 자동 차단.
  • 내가 직접 한 경험:
    • ca-tmpl 의 application-coreTransactionPort 인터페이스 + 3 메서드 + TransactionMode / Isolation enum 정의.
    • adapter-persistenceSpringTransactionPortTransactionTemplate 기반으로 구현 + 4 unit test (SpringTransactionPortTest) — propagation / isolation / readOnly / rollback-on-exception.
    • sample-ticket 의 aggregate Service (UserService / PostService) 의 모든 @Transactionaltx.inRead / tx.inWrite 로 일괄 치환. 동작 동등 + Spring 의존 surface 감소.
    • ArchUnit rule 추가 후 app-bootstrap test classpath 가 sample-ticket 을 못 보아 vacuously 통과한 사례를 testImplementation project(':sample-ticket') 로 해결 — raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28.
  • 트레이드오프:
    • TransactionPort 추상의 진짜 이득 은 testability 가 아니라 (@Transactional 메서드도 @SpringBootTest 로 테스트 가능) Spring 의존을 단일 진입점 (SpringTransactionPort) 으로 좁히는 것. Spring 업그레이드 / multi-tenant / multi-DB 시나리오에서 transaction 정책 변경 진입점이 한 클래스.
    • boilerplate 증가는 사실 — 메서드마다 tx.inWrite(() -> { ... }) 한 단 추가. 작은 팀 / 단일 DB / 단일 transactionManager 환경에서는 다수파 (@Transactional 직접) 가 reasonable.
    • 소수파 결정의 정당화abstraction 자체의 testability 이득 이 아니라 enforce 되는 추상 이 함께 있을 때만 성립. ArchUnit fitness function 없이 TransactionPort 만 두면 컨벤션이고, fitness function 이 @Transactional import 를 실패시키면 drift 방지 메커니즘.
    • TransactionTemplate vs @Transactional AOP proxy: 후자는 AT-TX-C5 의 self-invocation 함정. 전자는 proxy 가 없어서 self-invocation 영향 없음. ca-tmpl 은 후자의 함정을 피하려고 전자 선택.
  • 한계 / "이건 안 해봤다":
    • REQUIRES_NEW 의 실 outbox / audit row 동작 통합 검증 미수행feature-domain-event-outbox-contract 로 위임.
    • TransactionPortnoRollbackFor / timeout / transactionManager (multi-DB) 표현 불가능 — 의도적 한정. 필요 시 API 확장 결정.
    • Hibernate session statistics 로 readOnly flush-mode 측정 PoC 미수행 — Testcontainers 환경 후.
    • prod 운영 검증 없음 — ca-tmpl 은 template repository.

Sources / 근거

미해결 / Unknown

  • 모르는 것: TransactionPortnoRollbackFor / timeout 까지 표현 해야 하는 시점은 언제인가. 현재는 의도적 한정이지만, 실 사업 도메인 들어오면 필요할 수 있음.
  • 모르는 것: TransactionTemplate 기반 구현이 완전히 self-invocation 함정에서 자유로운지의 PoC (port 메서드가 다른 port 메서드 호출 시).
  • 모르는 것: multi-DB / multi-tenant 시 TransactionPorttransactionManager 선택을 어떻게 표현할지.
  • 확인 방법: feature-domain-event-outbox-contract (outbox / REQUIRES_NEW), self-invocation PoC, multi-DB 시나리오 추가 branch.

답변 경계 / Answer boundary

  • 자신 있게 말할 수 있는 범위:
    • ca-tmpl application-coreTransactionPort 인터페이스 정의 + adapter-persistenceSpringTransactionPort 구현 + 4 unit test + sample-ticket 마이그레이션의 직접 수행 범위.
    • 다수파 vs 소수파 trade-off 의 양쪽 근거 — 다수파의 boilerplate 이득, 소수파의 Spring 의존 surface 축소.
    • @Transactional AOP proxy 의 self-invocation 함정 vs TransactionTemplate 의 직접 호출 차이.
    • ArchUnit fitness function + Gradle spring-tx 제거의 belt+suspenders 패턴.
  • "이 부분은 공식 문서를 다시 보고 답변드리겠습니다" 라고 해야 하는 부분:
    • Spring TransactionInterceptor / TransactionAttributeSource내부 동작 (안 깊이 봄).
    • multi-DB 시 PlatformTransactionManager 선택의 운영 best practice (안 해봄).
    • Hibernate session 의 readOnly flush-mode 변경 구체 동작 (needs-confirmation, Testcontainers PoC 후).
  • 절대 과장하지 말 것:
    • TransactionPort 가 다수파보다 우월 하다는 식 금지 — 이 맥락 (template repository, 격리 우선) 까지만.
    • prod 운영 검증 없음 — locally-verified 등급.
    • REQUIRES_NEW실 outbox 시나리오에서 동작 검증됐다고 표현 금지 — 단위 테스트의 PROPAGATION_REQUIRES_NEW 설정만 확인.