Skip to the content.

CommandAdapter 가이드

목적: Domain Aggregate를 JPA Entity로 변환하여 저장하는 CQRS Command Adapter 구현 가이드


1️⃣ CommandAdapter란?

역할

Domain Aggregate → JPA Entity → DB 저장

CQRS의 Command(쓰기) 담당으로, Domain Model을 영속화하는 단순 저장소 역할만 수행합니다.

책임

핵심 원칙

Application Layer (UseCase)
  ├─ 1. Domain 조회 (Load Port)
  ├─ 2. 비즈니스 로직 실행 (Domain 메서드 호출)
  │     └─ order.confirm()
  │     └─ order.softDelete()
  │     └─ order.updateStatus()
  └─ 3. 변경 반영 (Persist Port)
        └─ CommandAdapter.persist(order)  ← 단순 저장만!
              └─ save() → JPA 더티체킹 자동 UPDATE

2️⃣ 핵심 원칙

원칙 1: 비즈니스 로직은 Domain 에서

원칙 2: 더티체킹 활용 (신규/수정 통합)

// ✅ persist() 하나로 신규/수정 모두 처리
public OrderId persist(Order order) {
    OrderJpaEntity entity = mapper.toEntity(order);
    OrderJpaEntity saved = repository.save(entity);
    return OrderId.of(saved.getId());
}

// JPA가 알아서 판단:
// - ID 없음 → INSERT
// - ID 있음 → 더티체킹으로 UPDATE

원칙 3: CQRS 엄격 분리

원칙 3: Transaction 어노테이션 절대 금지


3️⃣ 템플릿 코드

기본 템플릿

@Component
public class {Bc}CommandAdapter implements {Bc}PersistencePort {

    private final {Bc}JpaRepository {bc}JpaRepository;
    private final {Bc}JpaEntityMapper {bc}JpaEntityMapper;

    public {Bc}CommandAdapter(
        {Bc}JpaRepository {bc}JpaRepository,
        {Bc}JpaEntityMapper {bc}JpaEntityMapper
    ) {
        this.{bc}JpaRepository = {bc}JpaRepository;
        this.{bc}JpaEntityMapper = {bc}JpaEntityMapper;
    }

    /**
     * Bc 저장 (신규 생성 또는 수정)
     *
     * <p>신규 생성 (ID 없음) → JPA가 ID 자동 할당 (INSERT)</p>
     * <p>기존 수정 (ID 있음) → 더티체킹으로 자동 UPDATE</p>
     *
     * @param bc 저장할 Bc (Domain)
     * @return 저장된 Bc의 ID
     */
    @Override
    public {Bc}Id persist(Bc bc) {
        // 1. Domain → Entity 변환
        {Bc}JpaEntity entity = {bc}JpaEntityMapper.toEntity(bc);

        // 2. JPA 저장 (신규/수정 JPA가 자동 판단)
        {Bc}JpaEntity savedEntity = {bc}JpaRepository.save(entity);

        // 3. ID 반환
        return {Bc}Id.of(savedEntity.getId());
    }
}

8️⃣ 체크리스트

CommandAdapter 구현 시:

작성자: Development Team 최종 수정일: 2025-11-12 버전: 1.0.0