10 KiB
title, source_type, url, archive_url, status, confidence, tags, related_branches, related_projects, created, last_reviewed
| title | source_type | url | archive_url | status | confidence | tags | related_branches | related_projects | created | last_reviewed | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Clean DDD Lessons: Transactions with Spring (UNIL engineering) | company-tech-blog | https://medium.com/unil-ci-software-engineering/clean-ddd-lessons-transactions-with-spring-e78324bfec9a | raw | medium |
|
|
|
2026-05-22 | 2026-05-27 |
Clean DDD Lessons: Transactions with Spring
Layer:
raw/company-tech-blogs/— UNIL CI Software Engineering (Medium) 의 원문 발췌·출처 기록. 대학 엔지니어링 팀이 Spring 의존을 application layer 밖으로 밀어내며 TransactionPort 패턴으로 전환한 사례. 검증된 요약은/ingest후wiki/concepts/에 별도 작성. 원본은 raw에 영구 보관.
Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| raw/branch-notes/feature-application-port-usecase-contract | output port 에 runInTransaction(Runnable) 형 메서드를 두는 ca-tmpl 결정의 reference 사례 |
| raw/branch-notes/feature-transaction-concurrency-contract | Topic 2 — Transaction Boundary 대안 비교에서 ca-tmpl 채택안 (TransactionPort) 의 동종 사례 |
| raw/project-notes/ca-skeleton-operational-contract | §14. Transaction / Concurrency Contract — TransactionPort 결정의 외부 동종 사례 근거 + §5. Exception Ownership Contract — presentation 분리 |
컨텍스트 / 왜 저장했는지
ca-tmpl 의 TransactionPort 결정에 대한 대안 1: TransactionPort abstraction (output port + TransactionTemplate) 의 실제 적용 사례. 대학 엔지니어링 팀이 Spring 의존을 application layer 밖으로 밀어내는 동일 결정을 한 사례.
출처 / Source
- 원본 URL: https://medium.com/unil-ci-software-engineering/clean-ddd-lessons-transactions-with-spring-e78324bfec9a
- 아카이브 URL: (미수집)
- 저자 / 조직: UNIL CI Software Engineering (스위스 로잔대학교 엔지니어링 팀 기술블로그)
- 발행일: 2024 (최종 업데이트 2024-05-24)
- 마지막 확인일: 2026-05-27
핵심 인용 / Key quotes (verbatim)
[§Annotation 권장] "Prefer to use
javax.transaction.Transactionalannotation when demarcating the methods of use cases for transactional processing with Spring — this isolates "Use Cases" layer from dependency on a framework (design-time), which is prohibited by CA."
[§Output port 도입 (2024-05-24 업데이트)] "We declare a method in the output port for our persistence adapter" that executes "provided {@linkplain Runnable} in a transaction configured with default propagation strategy and isolation level."
[§TransactionTemplate 구현] "transactionTemplate.executeWithoutResult(status -> runnable.run());"
[§Rollback 제어] "Control the conditions under which the transaction of a use case will be committed or rollback using
try-catchblocks andorg.springframework.transaction.interceptor.TransactionInterceptor."
[§Presentation 분리] "if a use case completes successfully its main logic (modifying the state of one or several domain entities), the overall state of the system must be consistent — even if presentation of the results (to the user) fails for some reason afterwards."
[§Presentation 분리] "Present result of successful execution of the use case outside transactional boundary."
[§Presentation 분리] "Do not let any errors in presentation logic affect the execution of a transaction."
Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| UNIL-TX-C1 | use case 메서드 트랜잭션 경계는 Spring @Transactional 이 아닌 javax.transaction.Transactional (framework-neutral) 을 우선 사용 — Use Cases 레이어를 framework 의존성에서 격리 |
[§Annotation 권장] "Prefer to use javax.transaction.Transactional annotation when demarcating the methods of use cases for transactional processing with Spring — this isolates "Use Cases" layer from dependency on a framework (design-time), which is prohibited by CA." |
company-case-study |
Clean Architecture + Spring 환경 use case 클래스 | jakarta.transaction.Transactional 이 모든 Spring 버전에서 @Transactional 과 동일하게 작동한다는 뜻은 아님 — Spring 의 인터셉터 처리 여부는 별도 |
| UNIL-TX-C2 | 후속 업데이트(2024-05-24) 에서는 persistence adapter 의 output port 에 Runnable 을 받는 트랜잭션 실행 메서드를 선언하고 adapter 가 TransactionTemplate 으로 구현하는 방식으로 전환 |
[§Output port 도입] "We declare a method in the output port for our persistence adapter" + "executes provided {@linkplain Runnable} in a transaction" + [§TransactionTemplate 구현] "transactionTemplate.executeWithoutResult(status -> runnable.run());" | company-case-study |
application layer 가 framework annotation 도 import 하지 않으려는 hexagonal 케이스 | nested transaction / propagation / isolation 의 전체 표현력을 Runnable 시그니처로 충분히 표현 가능한지는 본 인용 범위 밖 |
| UNIL-TX-C3 | use case 트랜잭션의 commit/rollback 조건은 try-catch 블록 + org.springframework.transaction.interceptor.TransactionInterceptor 조합으로 제어 가능 |
[§Rollback 제어] "Control the conditions under which the transaction of a use case will be committed or rollback using try-catch blocks and org.springframework.transaction.interceptor.TransactionInterceptor." |
company-case-study |
Spring TX 인프라 + use case 레벨 rollback 제어 | Try.Failure / Either.Left 같은 functional 타입과의 통합 방법은 본 인용 범위 밖 |
| UNIL-TX-C4 | use case 의 핵심 로직이 성공하면 시스템 상태는 일관되어야 하며, 결과 presentation 의 실패가 트랜잭션을 롤백시켜서는 안 된다 — 따라서 presentation 은 트랜잭션 경계 밖에 위치 | [§Presentation 분리] "if a use case completes successfully its main logic ... the overall state of the system must be consistent — even if presentation of the results (to the user) fails" + "Present result of successful execution of the use case outside transactional boundary." + "Do not let any errors in presentation logic affect the execution of a transaction." | company-case-study |
application service + 결과 직렬화/응답 생성 분리 설계 | "presentation" 의 정확한 경계 (HTTP 응답만? 로깅도? 이벤트 발행도?) 는 본 인용에서 모호 |
Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
UNIL-TX-C1: framework-neutral annotation 선호 권고 (Use Cases isolation 목적)UNIL-TX-C2: output port +Runnable+TransactionTemplate패턴의 실제 코드 사례UNIL-TX-C3:try-catch+TransactionInterceptor로 rollback 조건 제어 가능성UNIL-TX-C4: presentation 을 트랜잭션 밖으로 분리하는 명시적 권고
- 이 자료가 증명하지 않는 것:
- 이 패턴이 산업계 표준이라는 주장 (
engineering-blog수준 — 대학 팀 사례) - prod 환경에서 트랜잭션 안정성 측정값 (글에 측정 데이터 없음)
- 모든 propagation/isolation 시나리오 (
Runnable시그니처로 표현 가능 여부)
- 이 패턴이 산업계 표준이라는 주장 (
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- ca-tmpl 의
TransactionalUseCaseRunner가 본 사례의runInTransaction(Runnable)보다 한 단계 더 abstraction 을 가짐 — 추가 abstraction 의 비용/이득 분석 - nested transaction 이 필요한 use case 가 ca-tmpl 에 존재하는지 (있다면
Runnable시그니처 불충분) - presentation 의 정확한 경계 정의 (ca-tmpl 의 controller/serializer 분리 정책과 일치 검증)
- ca-tmpl 의
메모 / Notes (내 프로젝트 해석)
본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 적용 시나리오: hexagonal/clean architecture 에서 application(use case) layer 가 Spring
@Transactional직접 import 없이 트랜잭션 경계를 제어해야 할 때. - 장점:
- application layer 가
org.springframework.transaction.*의존성 0개. dependency rule 보존. - presentation 코드가 트랜잭션 안에 묶여 commit 이 지연되거나, 응답 직렬화 실패가 rollback 을 유발하는 문제를 차단.
- mock 으로 port 갈아끼우면 단위 테스트에서 Spring context 부팅 없이 commit/rollback 시나리오 검증 가능.
- application layer 가
- 단점:
runInTransaction(Runnable)형태가 nested transaction / propagation / isolation 표현력에서@Transactional속성 대비 빈약함. 옵션을 늘리면 port 가 다시 Spring 모양에 가까워짐.- 모든 use case 에 wrap 코드가 들어가서 시그니처 잡음 증가.
- ca-tmpl(TransactionPort) 와의 차이: 거의 동일한 채택. ca-tmpl 의
TransactionalUseCaseRunner는 use case 를 외부에서 감싸 자동으로 경계를 그리는 점에서 한 단계 더 abstraction layer 가 두꺼움. - testability 영향: ★ 상승 (Spring context-free 테스트 가능).
- code 복잡도 영향: 중간 — port 인터페이스 추가, adapter 에서
TransactionTemplate위임, use case 에서port.runInTransaction { ... }명시.
Related / 관련
- 같은 주제 다른 raw:
- raw/company-tech-blogs/hexagonal-reflectoring-transactional-placement (baseline
@Transactionaldirect) - raw/company-tech-blogs/transaction-port-vassilis-soum-github-readme (functional 통합 변형)
- raw/company-tech-blogs/woowahan-hexagonal-multimodule (multi-module 보완)
- raw/company-tech-blogs/hexagonal-reflectoring-transactional-placement (baseline
- 인용하는 branch:
- 인용하는 project:
- 인용한 wiki 요약: (미작성)
- 대안 그룹: Topic 2 — Transaction Boundary (대안 5종: TransactionPort / @Transactional direct / TransactionTemplate / Functional monad / Custom AOP)
- 본 source 의 위치: ca-tmpl 채택안 baseline (TransactionPort abstraction)