[개발] 도메인 계층을 안전하게!

김종준·2023년 11월 6일
0

Hiit

목록 보기
12/12
post-thumbnail

도메인 계층을 안전하게!


관련 코드 바로가기


들어가며

최근에 오브젝트라는 책을 읽고 우아한테크코스 프리코스(숫자야구, 자동차경주)를 하며 객체지향적에 관해서 더 깊은 생각을 하고 있습니다.

그러다 보니 프로젝트 아키텍처 및 유즈케이스 계층 설계에 관한 글을 작성하지도 벌써 3달이 다 되어 가는 시점이지만 도메인 계층을 다시 살펴보니 아직 수정할 수 있는 부분을 확인할 수 있었고 이를 수정하며 생각한 고민을 나누어 보려 합니다.


이전 설계에서 보완할 점

이전 설계에 있어 도메인 계층이 레포지토리 계층에 의존을 가지고 있는 경우는 크게 2 경우가 있습니다.


우선 컨버터에서 엔티티를 도메인 객체로 변경할 때 레포지토리 계층에 의존하게 됩니다.

관련 코드

스크린샷 2023-11-06 오후 3 55 31

그리고 유즈케이스에서도 레포지토리 계층에 의존합니다.

관련 코드

스크린샷 2023-11-06 오후 3 57 33

처음 설계할 때는 컨트롤러 > 도메인 > 레포지토리 사이의 의존성은 인정하자는 마음이었습니다.

사실 그러한 선택을 하게 된 이유는 의존을 끊을방법에 대한 확신이 없었기 때문입니다.


기본적으로 Spring Data Jpa를 사용한다면 아래와 같이 레포지토리를 선언하여 사용합니다.

public interface FooJpaRepository extends JpaRepository<FooEntity, Long> {}

해당 인터페이스의 위치는 아마 레포지토리 계층일 것이고요.


인터페이스와 대체 클래스로 도메인은 다른 것을 몰라도 되도록 만들자!

그럼 어떻게 레포지토리 계층과의 의존을 끊을 수 있을까요?

도메인 계층에서 레포지토리의 계층에 접근할 수 있는 데이터 접근 객체(Dao) 그리고 엔티티를 대체하여 도메인에서 사용될 데이터(Data)가 필요합니다.

관련 코드


도메인에서 Dao를 만든다는 것은 Data Jpa가 우리에게 제공해 주는 자동으로 만들어 주는 메서드에 대한 편리함을 버린다는 것입니다.

이러한 불편함은 우리가 편리함을 포기하면서까지 도메인을 안전하게 만드는 것을 망설이게 합니다.

그렇다면 어떻게 해야 할까요? 포기해야 할까요?

저는 "한 번만 고생하자!"하는 마음으로 JpaDao , AbstractJpaDao와 같은 클래스를 만들어 Data Jpa가 제공하는 편리함에는 못 미칠 수 있지만 추후 데이터가 추가되더라도 재사용하기 편리한 환경을 만들려 노력해 보았습니다.


우선 JpaDao 인터페이스를 만들어 주었습니다.

해당 인터페이스에는 JpaRepository가 제공하는 메서드를 모두 동일하게 선언해 주었습니다.

하지만 다른 점은 JpaRepository는 엔티티를 반환한다면 JpaDao는 데이터를 반환한다는 것입니다.


그리고 엔티티 클래스를 데이터 클래스로 만들어 줄 데이터 컨버터를 만들어 줍니다.

데이터 컨버터의 경우도 다른 데이터가 추가되는 경우에 쉽게 재사용 사용할 수 있도록 인터페이스로 만들어 줍니다.


마지막으로 JpaRepository와 데이터 컨버터만 주입하면 완성되는 JpaDao를 구현한 추상 클래스 AbstractJpaDao를 만들었습니다.

해당 클래스에서는 데이터 클래스를 엔티티 클래스로 변환하여 JpaRepository를 다루는 작업과

그렇게 JpaRepository를 활용하여 얻은 통해 엔티티를 데이터로 변환하는 작업을 미리 구성해 두었습니다.


이러한 작업을 통해 Data Jpa가 기본적으로 제공하는 메서드를 쉽게 활용할 수 있는 환경을 만들 수 있었습니다.

코드로 살펴보면 아래의 코드만으로 Foo에 대한 기본적인 메서드를 만들어 낼 수 있는 것입니다.

public interface FooDao extends JpaDao<FooData, Long> {}
@Repository
@Transactional
public class FooDaoImpl extends AbstractJpaDao<FooEntity, Long, FooData> implements FooDao {

	public FooDaoImpl(
			JpaRepository<FooEntity, Long> jpaRepository,
			AbstractDataConverter<FooEntity, FooData> converter,
			FooCustomRepository customRepository) {
		super(jpaRepository, converter);	}
}

그럼 다시 기존에 레포지토리 계층과 의존된 부분을 확인해 봅시다.

관련코드

스크린샷 2023-11-06 오후 7 33 27

관련코드

스크린샷 2023-11-06 오후 7 34 58

레포지토리 계층이 아닌 dao에 대한 의존성을 확인할 수 있습니다.

성공입니다!!!ㅎㅎㅎ


뭐가 좋아진 걸까?

그래서 도메인인 레포지토리 계층과 의존성을 끊고 도메인은 도메인이 가지고 있는 것만 알게 함으로써 뭐가 좋아진 걸까요?

만약 Data Jpa를 사용하지 않는다고 해봅시다.

도메인이 레포지토리 계층을 알고 있다면 도메인까지 영향을 받습니다.

하지만 인터페이스로 인해 도메인은 레포지토리 계층의 변화를 알지 않아도 되게 됩니다.


그리고 지금은 단일 모듈이지만 추후 멀티 모듈로 분리한다고 생각해 봅시다.

그러한 경우에도 레포지토리 패키지 속에 있는 클래스들만 다른 모듈로 이동시키면 됩니다.


이렇게 좋은 점이 생긴 만큼 따라서 나빠지는 점도 생기게 되는데 지금 제가 느낄 수 있는 나쁜 점은 코드의 복잡성이 증가한다는 것뿐인 것 같습니다.


마치며

"결국 소프트웨어 설계의 모든 근본은 유지보수성을 위해 결합과 응집의 저울을 다스리는 것"이라는 말을 최근에 들었습니다.

이러한 판단을 내려야 하는 자리에서 결정을 내리기 위해서는 그러한 자리에 오르기 전에 미리 많이 고민해 보고 생각해 보아야 한다고 생각합니다.

저의 지금의 판단이 맞을 수도 있고 틀릴 수도 있겠지만 많은 것을 고민하고 생각할 수 있게 해주는 판단이길 바라며 이번 글을 마무리하려 합니다.

감사합니다.

0개의 댓글