본문 바로가기

Book

작업 단위 패턴

저장소 패턴이 영속적 저장소 개념에 대한 추상화라면 작업 단위 패턴은 원자적 연산이라는 개념에 대한 추상화.
서비스 계층에서 직접 세션과, 저장소를 불러서 작업하지 않게 해 줌으로써, 서비스 계층과 각 계층을 분리시켜줌. 
책에서는 컨텐스트 매니저를 통해 구현했다. 

근데 이게 뒤에서도 나오는데 기존에 세션이 하는 걸 한번 wrapping 한 느낌이다.  왜 그런지 밑에서 설명하고 그 차이와 트레이드오프를 말해준다. 

밑에 예제만 봐도 알겠지만 서비스 로직를 트랜잭션 처리했다. 그 과정에서 uow는 필요한 모든 걸 갖고 있고 외부에서 호출하는 caller는 그냥 얘만 인자로 넘기면서 서비스를  호출하면 된다.

책 예제

class SqlAlchemyUnitOfWork(AbstractUnitOfWork):
    def __init__(self, session_factory=DEFAULT_SESSION_FACTORY):
        self.session_factory = session_factory

    def __enter__(self):
        self.session = self.session_factory()  # type: Session
        self.batches = repository.SqlAlchemyRepository(self.session)
        return super().__enter__()

    def __exit__(self, *args):
        super().__exit__(*args)
        self.session.close()

    def commit(self):
        self.session.commit()

    def rollback(self):
        self.session.rollback()
        
 ...
 
 def reallocate(line: Orderline, uow:AbstractUnitOfWork) -> str:
 	with uow:
    	batch = uow.batches.get(sku= line.sku)
        if batch is None:
        	raise InvalidSku(f'Invalid sku {line.sku}')
            batch.deallocate(line)
            allocate(line)
            uow.commit()

해당 패턴에서 느낀 개인 프로젝트에 대한 생각

이 파트 중반에 보면 명시적 커밋과 암시적 커밋에 대한 내용이 잠깐 나오는데, 보면서 갑자기 생각이 든 건 내가 혼자 했던 프로젝트가 생각났다.

Meet-up-Spot에서는 애초에 도매인 모델 형식으로 설계할 생각도 못해서 이미 너무 다른 구조의 프로젝트가 되었지만, 내 프로젝트의 CRUD계층과 저장소 패턴부터 지금까지의 작업 단위 패턴을 보니까 CRUD계층이 너무 애매한 거 같다. 무엇보다 crud에 커밋을 묶어서 처리하는 게 여러 crud연산을 하나의 트랜잭션으로 묶는 것이 힘들다. 고쳐야 하는 게 맞는데 딱히 복잡한 로직이 없어서 그냥 두고 있긴 하다.

이 작업단위 패턴을 한다면 암시적으로 커밋을 해도 내부에 Uow에 필요한 repo들을 사용해 여러 저장소를 사용하는 트랜잭션이 가능할 것이다.

이걸 보면서 내가 만약 바꾼다면 그냥 crud에서 커밋을 뺄지 아니면 이상태에서 한 번 더 작업단위계층은 아니더라도 계층을 쌓아서 처리할지 고민을 해봤다. 기본 코드를 고치는 게 맞는 거 같은데, 왜냐면 계속 계층만 추가하면 더 이상해질 거 같다.  아무튼 첨부터 구조를 잘 짜는 게 정말 중요한 거 같다.

 

자신이 만든 것이 아니면 모킹하지 말라

한마디로 자신이 만든게 아니라 세션 같은 복잡한 인터페이스랑 결합하게 되면 테스트할 때도 분명히 걸리는 부분이 생김 그니까 한 번 더 추상화해서 사용하자는 거임. 나도 그래서 테스트할 때 불편한 것도 있었음. 위 문장은 와닿았음
여기서도 UoW 계층이 생기면서 기존에 test_orm 같은 모듈은 제거해도 된다. 어차피 test_repository에서 테스트가 되기때문에 장기적으로 필요 없기 때문이다. 
나도 이번 내용을 보면서 느꼈지만, 실제로 SQLAlchemy는 세션 객체를 UoW패턴을 사용해서 구현했다고 한다.  그렇다면 굳이 한번더 추상화를 할 필요가 있을까 하는 생각이 든다. 이것 역시 트레이드오프를 잘 따져야겠다.

장점 단점
원자적 연산을 표현하는 좋은 추상화를 만듬, 컨텍스트 관리자를 사용하면 원자적으로 한 그룹으로 묶어야하는 코드 블록을 시각적으로 쉽게 알아 볼수 있음.
안전하게 트랜잭션 처리를 할 수 있음
또한 원자성은 뒤에서 이벤트나 메시지 버스를 사용할 때도 도움이 된다함
사용 중인 ORM이 이미 원자성을 중심으로 추상화를 제공하고 있음. 프로젝트에 사용중인 SQLAlchemy도 컨텐스트 관리자도 제공함. 
롤백, 멀티쓰레딩 내포된 트랜잭션 등을 처리하는 코드를 만들때는 매우 신중하게 해야 된다고함.

정리하면
위에서도 잠깐 언급했지만 세션 API는 풍부한 기능과 도메인에서 불필요한 연산을 제공하기 때문에, UoW는 세션을 단순화 시켜서 필요한 부분만 사용하게 해 준다.

 

참고 및 출처: <파이썬으로 살펴보는 아키텍처 패턴: TDD, DDD, EDM 적용하기)> (해리 퍼시벌, 밥 그레고리 지음, 오현석 옮김,  한빛미디어 , 2021)