본문 바로가기
Back-End/도메인 주도 개발 시작하기

[DDD] 도메인 주도 개발 시작하기_Ch8

by ChaSso 2023. 4. 2.

Chapter 8 애그리거트 트랜잭션 관리

8.1 애그리거트와 트랜잭션
∘ 운영자는 상품의 배송 상태를 바꾸고, 동시에 고객은 배송지 정보를 바꿈. 서로 다른 애그리거트 객체를 이용하지만 트랜잭션 커밋할 때 두 정보가 다 바뀜 → 애그리거트의 일관성이 깨짐
∘ 애그리거트 트랜잭션 처리 방식에는 선점 잠금과 비선점 잠금이 있음

8.2 선점 잠금
선점 잠금 : 먼저 애그리거트를 구한 스레드가 애그리거트 사용이 끝날 때까지 다른 스레드가 해당 애그리거트를 수정하지 못하게 블로킹
먼저 애그리거트를 사용하던 스레드가 수행하고 트랜잭션을 커밋하면 잠금이 해제됨
데이터 충돌 문제 해결 가능
선점 잠금은 보통 DBMS가 제공하는 행단위 잠금을 사용해서 구현함
JPA 프로바이더와 DBMS에 따라 잠금 모드 구현이 다름

8.2.1 선점 잠금과 교착 상태
교착 상태 : 두 스레드가 각각 상대 스레드가 먼저 선점한 잠금을 구할 수 없음
교착 상태의 발생을 막으려면 잠금을 구할 때의 최대 대기 시간을 지정해야 됨

8.3 비선점 잠금
운영자가 배송 상태를 변경하는 사이에 고객이 배송지를 변경해버리는 상황은 선점 잠금으로 해결하지 못하는 상황
비선점 잠금 : 동시에 접근하는 것을 막는 대신 변경한 데이터를 실제 DBMS에 반영하는 시점에 변경 가능 여부를 확인함
새로 커밋하려는 버전이 이미 커밋된 버전과 충돌하면 데이터 수정에 실패
JPA는 버전을 이용한 비선점 잠금 기능을 지원함
응용 서비스가 버전은 확인할 필요 없이 애그리거트 데이터가 변경되면 트랜잭션이 종료될 때 JPA가 비선점 잠금을 위한 쿼리 실행
시스템은 애그리거트를 조회할 때 버전 값도 불러옴
비선점 잠금 방식을 여러 트랜잭션으로 확장하려면 애그리거트 정보와 함께 버전 정보도 화면에 보여줘야 함
응용 서비스에 보내는 요청 데이터에 사용자가 전송한 버전 값이 포함됨 → 응용 서비스가 그 버전 값과 애그리거트 버전이 일치하는지 확인 후에 일치하면 기능 수행
표현 계층은 익셉션이 발생하면 사용자에게 버전 충돌이 발생했음을 알림

8.3.1 강제 버전 증가
애그리거트 내에 있는 구성요소의 상태가 바뀌면 애그리거트의 버전 값이 증가해야 비선점 잠금이 올바르게 동작할 수 있음
JPA는 엔티티를 구할 때 강제로 버전 값을 증가시키는 잠금 모드 지원. EntityManager#find()

8.4 오프라인 선점 잠금
컨플루언스는 사전에 충돌 여부는 알려주지만 동시에 수정하는 것은 허용함
누군가가 수정 화면을 보고 있을 때 수정 화면을 실행하지 못하도록 하려면 오프라인 선점 잠금 방식을 이용해야 됨
선점 잠금 방식과 비선점 잠금 방식은 한 트랜잭션 범위에서만 적용되지만 오프라인 잠금 선점 잠금 방식은 여러 트랜잭션에 적용됨
영원히 잠금을 구할 수 없는 상황을 막기 위해 잠금 유효 시간 필요

8.4.1 오프라인 선점 잠금을 위한 LockManager 인터페이스와 관련 클래스
잠금 선점 시도, 잠금 확인, 잠금 해제, 잠금 유효시간 연장 기능이 필요
LockManager 인터페이스
tryLock() : 잠금을 구할 때. 잠금 식별을 위한 LockId 리턴
LockManager#tryLock() : 오프라인 선점 잠금이 필요한 코드에서 잠금 시도할 때
컨트롤러가 오프라인 선점 잠금 기능으로 데이터 수정 폼에 동시에 접근하는 것을 제어
잠금 해제할 대 LockId 이용

8.4.2 DB를 이용한 LockManager 구현
checkAlreadyLocked() : 이미 잠금이 선점되었는지 확인
locking() : 잠금 선점
isExpired() : 유효 시간이 지났는지