🧐TIL

[DDD] 리포지토리, Repository

date
Mar 4, 2023
thumbnail
slug
ddd-repository
author
status
Public
summary
“도메인 주도 설계 철저 입문”을 읽으며 공부한 내용을 정리합니다.
type
Post
category
🧐TIL
tags
TIL
DDD
Architecture
updatedAt
Jun 18, 2023 05:57 AM

리포지토리

소프트웨어로 도메인 개념을 표현했다고 해서 애플리케이션이 되는 것은 아니다. 프로그램을 실행하며 메모리에 로드된 데이터는 프로그램이 종료되면 사라지는데, 그래서 프로그램이 종료되더라도 객체가 사라지지 않도록 저장하고 복원할 수 있도록 영속화(퍼시스턴시, Persistency)가 필요하다.
애플리케이션의 동작을 위해 객체를 저장하고 복원하는 행위는 도메인 개념과 거리가 멀다고 생각할 수 있으나 생애주기를 갖는 엔티티 같은 객체의 경우 도메인 규칙에 의해 생애주기가 이루어져야 하는데 프로그램이 종료되는 것으로 인해 강제로 엔티티의 생애주기가 결정된다면 도메인 규칙을 올바르게 유지하고 구현할 수 없게 되는 문제가 있다.
그래서 퍼시스턴시의 역할과 책임을 지게 하는 객체가 바로 ‘저장소’라는 의미를 지니고 있는 리포지토리다.

리포지토리의 책임

앞서 도메인 서비스에 대한 단원에서 ‘회원정보 중복검사’ 같이 값 객체나 엔티티의 행동으로 정의하기 부자연스러운 기능은 도메인 서비스로 구현하는 것이 적절하다는 것을 공부했었다.
‘회원정보 중복검사’를 구현하려면 기존 회원 목록에 같은 회원명 또는 식별자를 가진 회원 엔티티가 있는지를 확인할 수 있어야 하고, 그러려면 중복된 회원 엔티티가 존재하는지를 어딘가에서 조회할 수 있어야 한다.
일반적으로는 데이터의 저장, 조회, 업데이트, 삭제(CRUD)를 위해 데이터베이스를 사용하는데, 회원 데이터를 조회하는 코드가 도메인 서비스 내에 구현될 경우 도메인 로직을 파악하기 어려워질뿐더러 회원 정보를 조회하는 로직이 불필요하게 노출되게 된다.
그래서 리포지토리 객체를 만들어 데이터베이스에 대한 접근 책임을 부여하고 CRUD와 같은 행동은 리포지토리에 정의하게 된다.

리포지토리의 형태

데이터를 조회할 수 있는 ‘어딘가’로 관계형 데이터베이스나 NoSQL 데이터베이스 등이 일반적으로 사용되기는 하지만 리포지토리가 반드시 데이터베이스와 연관된 속성과 행동만 정의되어야 하는 것은 아니다. 데이터는 파일로 저장을 할 수도 있고 테스트 환경처럼 데이터의 유지가 중요하지 않다면 인 메모리 저장소를 구현해서 사용할 수도 있다.
중요한 것은 데이터를 저장하고 조회하는 등의 규칙이 정의되는 객체가 리포지토리고 어떤 종류의 DB를 쓰거나 어떤 방식으로 데이터를 다루는지에 대해서는 리포지토리 밖에서 노출되지 않아야 한다는 것이다.

리포지토리에 정의하기 부적절한 메소드

‘회원정보 중복검사’를 구현할 때 회원을 조회하기 위한 메소드 find()나 이미 동일한 회원이 존재하는지 확인하는 exists() 메소드를 구현할 수 있을 것이다. 이 때 exists()는 어디에 정의하는 것이 좋을까?
저장소에 회원정보가 존재하는지 확인하는 것이기 때문에 리포지토리에 정의하는 것이 적절하다고 생각하기 쉽지만 ‘동일한 회원’이라는 조건의 세부 규칙은 도메인 규칙에 해당하기 때문에 이를 리포지토리에 구현하는 것은 리포지토리의 책임을 벗어나게 되므로 도메인 서비스에 구현하는 것이 적절하다.
다만, 어떻게 구현하느냐에 따라 차이는 있을 수 있는데 만약 exists() 메소드에 User 객체 대신 user_id나 user_name 같은 구체적인 인자를 주어 ‘동일한 회원’을 조회하는 것이 아니라 ‘같은 id 조회’ 또는 ‘같은 이름 조회’ 같은 책임을 부여한다면 도메인 규칙을 드러내지 않고 리포지토리에 어울리는 메소드를 정의할 수 있다.

리포지토리 구현시 유의할 점

  • 객체를 저장하거나 업데이트 하는 메소드를 정의할 때는 객체 자체를 인자로 받도록 하는 것이 좋다. 그렇지 않으면 객체의 속성 별로 저장/수정 메소드를 정의해야 하게 될 수 있으며 객체의 수정은 애초에 객체 자신에게 맡기는 것이 옳다.
  • 저장된 모든 객체를 복원하는 메소드는 컴퓨터의 리소스를 소모시키기 쉽다. 성능에서 기안하는 문제를 회피하기 위해 전체 복원보다는 필요에 따라 더 적합한 메소드를 정의하는 것이 좋다.
  • 인 메모리 리포지토리를 구현하는 경우 객체 조회 시 찾은 객체를 그대로 반환하지 말고 깊은 복사를 통해 만든 새로운 객체를 반환하는 것이 중요하다. 자칫하면 복원된 인스턴스를 조작했을 때 리포지토리에 저장된 객체에 영향을 미칠 수 있기 때문이다.