TRANSACTION BOUNDARY
서비스 메서드가 한 유스케이스의 커밋 경계를 잡는다
컨트롤러는 요청을 해석하고, 서비스는 함께 성공해야 하는 DB 변경을 하나로 묶고, 외부 부작용은 커밋 밖으로 밀어냅니다.
판단 기준
같이 성공해야 하면 안쪽, 다시 시도해도 되면 바깥쪽
트랜잭션 안에는 DB 일관성을 만드는 작업만 두고, 알림과 결제
연동은 outbox나 after commit으로 분리합니다.
Controller
요청 경계
DTO 검증, 인증 사용자 확인, command 객체 생성
DB 커넥션을 오래 붙잡지 않도록 빠르게 서비스로 넘김
Service.createOrder() 트랜잭션 내부
@Transactional
begin
재고 확인과 차감
읽은 재고와 차감이 같은 커넥션 문맥에서 이어짐
write
주문 저장
orders 행이 생성되고 이후 상세가 같은 주문을 참조
write
주문 항목 저장
order_items가 주문과 상품을 연결하며 금액 스냅샷을 보존
commit
성공 또는 rollback
중간 예외가 전파되면 모든 변경을 취소
After commit
외부 부작용
메일, 알림, 이벤트 발행, 검색 인덱싱
Outbox
실패 재시도
커밋된 이벤트를 별도 작업자가 재전송
원자성
재고 차감과 주문 생성이 분리 커밋되면 안 됩니다.
예외 전파
실패를 삼키면 롤백 규칙이 실행되지 않습니다.
잠금 시간
외부 API를 안쪽에 두면 DB 잠금이 불필요하게 길어집니다.
관찰 가능성
트랜잭션 id와 outbox id를 로그로 연결합니다.
핵심 감각
서비스 레이어 트랜잭션은 “계층이 예뻐 보이게 만드는 장식”이 아니라,
한 유스케이스의 불변식을 어디서 한 번에 보장할지 정하는
경계입니다.