도메인과 엔티티, 꼭 분리해야 하나요?

Jayson·2025년 4월 8일
0
post-thumbnail

이번 글에서는 프로젝트를 진행하다 보면 자주 마주하게 되는 질문, “도메인과 엔티티를 꼭 분리해야 할까?”에 대해 이야기해보며 정리해보려고 해요. 특히 협업과 유지보수 측면에서 이 분리가 왜 중요한지, 어떤 장점이 있는지를 함께 살펴보겠습니다! ✨

1. 도메인과 엔티티란?

  • 도메인(Domain): 비즈니스 로직을 처리하는 핵심 영역으로, 순수 자바 객체를 활용해 구현돼요.
  • 엔티티(Entity): 데이터베이스 접근 및 관리를 담당하며, JPA의 @Entity 어노테이션과 함께 사용되는 객체예요.

2. 도메인과 엔티티를 분리하는 이유

엔티티와 도메인을 분리하면 데이터베이스의 변화가 도메인 로직에 영향을 주지 않아, 시스템이 더 안정적으로 관리될 수 있어요. 이는 UI 변화로부터 엔티티를 보호하기 위해 DTO를 사용하는 것과 유사한 맥락이에요.

객체지향 관점

  • 단일 책임 원칙(SRP): 도메인은 비즈니스 로직에만, 엔티티는 데이터 관리에만 집중해 역할이 명확해져요.
  • 응집도 증가: 각 객체가 자신의 책임에만 집중하면 의존성도 줄어들고 코드가 더 명확해져요.

협업 관점

  • 명확한 업무 분담: 도메인 로직과 데이터 접근 로직을 따로 관리할 수 있어 개발자 간 협업이 쉬워져요.
  • 동시 작업 가능: 백엔드, DB 담당자가 독립적으로 작업하기 좋아 업무 속도가 빨라져요.

유지보수 관점

  • 변경 유연성: 비즈니스 로직과 데이터베이스 로직이 독립적이므로 요구사항 변경 시 수정이 쉬워져요.
  • 테스트 용이성: 순수 자바 코드로 도메인 로직을 테스트할 수 있어 테스트 품질이 높아지고 유지보수가 간편해져요.

3. 분리 기준과 구현 예시

도메인과 엔티티를 분리할 때 명확한 기준은 바로 PK(기본키)의 유무예요. 데이터베이스와 관련된 PK를 가진 객체는 엔티티로, PK와 관계없이 비즈니스 로직을 수행하는 객체는 도메인으로 분리할 수 있어요.

ProductName (VO)

@Embeddable
public class ProductName {

    private final String value;

    protected ProductName() {
        this.value = null;
    }

    private ProductName(String value) {
        validate(value);
        this.value = value;
    }

    public static ProductName of(String value) {
        return new ProductName(value);
    }

    public String value() {
        return value;
    }

    private void validate(String value) {
        if (value == null || value.isBlank()) {
            throw new IllegalArgumentException("상품명은 비어있을 수 없습니다.");
        }
    }
}

도메인 객체 예시

public class Product {

    private final ProductName name;
    private final Price price;

    public Product(ProductName name, Price price) {
        this.name = name;
        this.price = price;
    }

    public long calculatePrice(int quantity) {
        return price.value() * quantity;
    }
}

엔티티 객체 (JPA 예시)

@Entity
public class ProductEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private ProductName name;

    private long price;

    protected ProductEntity() {}

    public ProductEntity(ProductName name, long price) {
        this.name = name;
        this.price = price;
    }

    public Product toDomain() {
        return new Product(name, new Price(price));
    }
}

4. 도메인과 엔티티의 활용 예시

회원이 상품을 주문하는 간단한 시나리오를 예로 들어볼게요. 이 상황에서 도메인과 엔티티가 어떻게 역할을 나누는지 살펴보겠습니다.

요구사항

  • 회원이 상품을 주문한다.
  • 할인 적용 여부에 따라 상품 가격을 계산한다.
  • 회원의 잔액이 충분한지 확인한다.
  • 상품 재고가 충분한지 확인한다.
  • 주문이 가능하면 회원의 돈과 상품 재고를 차감한다.

도메인이 담당하는 책임

  • 상품 가격에 할인 적용 여부를 판단하고 계산한다.
  • 회원의 잔액이 주문 금액보다 충분한지를 검증한다.
  • 상품의 재고가 주문 수량보다 많은지 확인한다.

👉 도메인은 비즈니스 규칙과 판단 기준을 내리는 역할에 집중하며, 데이터가 어디서 왔는지는 신경 쓰지 않아요.

엔티티가 담당하는 책임

  • 회원, 상품 정보를 데이터베이스에서 조회한다.
  • 주문 처리 후 DB에 저장하기 위한 상태를 변경한다.

👉 엔티티는 데이터의 저장과 변경, 즉 영속성과 관련된 책임만 가지고 있어요.

실제 서비스 코드 예시

@Transactional
public void completeOrder(Long memberId, Long productId, int quantity) {
    MemberEntity member = memberRepository.findById(memberId);
    ProductEntity product = productRepository.findById(productId);

    Product domainProduct = product.toDomain();
    long totalPrice = domainProduct.calculatePrice(quantity, LocalDate.now());

    member.deductBalance(totalPrice);
    product.reduceStock(quantity);
}

코드의 핵심 포인트

  • Repository는 DB 조회만 수행합니다.
  • 도메인은 엔티티에서 변환한 순수 자바 객체로 비즈니스 로직을 수행합니다.
  • 엔티티는 주문 후 상태를 변경하고 이를 DB에 저장합니다.

5. 장단점

  • 장점
    • ORM 의존성을 줄여 객체지향 설계가 가능
    • 테스트 코드 작성이 쉬워짐
    • 변경 사항 적용이 쉽고 안정적
  • 단점
    • 작성할 코드가 많아져 초기 개발 시간은 증가할 수 있음

단점이 있지만, 장기적으로 보면 유지보수성이 뛰어나 프로젝트 규모가 커질수록 더 많은 이점이 있어요.

마치며

도메인과 엔티티의 분리는 처음엔 낯설고, 구현해야 할 코드도 많아 보여 부담스럽게 느껴질 수 있어요.

하지만 프로젝트의 규모가 커지고 복잡한 요구사항이 늘어날수록, 각 객체가 자신의 책임에 집중하도록 설계하는 것의 중요성은 점점 더 커집니다.

이번 글에서는 그런 구조를 만들어가기 위한 하나의 방향으로 도메인과 엔티티를 분리하는 이유와 방법을 살펴보았고, 실제 예시를 통해 어떻게 적용할 수 있을지도 함께 고민해봤습니다.

이러한 설계는 단순히 “기술적으로 멋있어 보이기 위해서”가 아니라,

협업이 쉬운 구조,

테스트가 가능한 로직 분리,

유지보수가 용이한 아키텍처를 만들기 위한 노력의 일환이에요.

작은 프로젝트에서도 한 번쯤 적용해보면

“아, 이래서 분리를 하는구나” 하고 체감하게 되는 순간이 분명히 올 거예요.

읽어주셔서 감사합니다! 🙏

업로드중..

profile
Small Big Cycle

0개의 댓글