O0oO0Oo / netty-reservation-service

트랜잭션, 동시성을 공부하기 위한 토이 프로젝트입니다.
0 stars 0 forks source link

feat: encapsulation of domain logic #29

Closed O0oO0Oo closed 2 months ago

O0oO0Oo commented 2 months ago

PR 설명

DDD 도입과 테스트코드 작성

변경 사항

배경

DDD 를 도입하여 비즈니스 로직과 엔티티 간의 경계를 설정하여, 서비스 코드의 복잡성을 줄이고, 단일 책임 원칙을 지킬 수 있도록 개선하고자 하였습니다.

비즈니스 로직에서 도메인 로직을 엔티티, 값 객체 내부로 캡슐화함으로써 서비스 레이어의 역햘의 명확성, 코드 가독성과 유지보수성을 향상 시켰고, 도메인 이벤트를 사용하여 모듈 간 결합도를 낮추게 되었습니다.

예시 - ReservableItem

https://github.com/O0oO0Oo/netty-reservation-service/blob/develop/reservation-saga/reservable-item/src/main/java/org/server/rsaga/reservableitem/domain/ReservableItem.java

변경 전 - 값에 대한 여러 검증이 이뤄진다.

@Transactional
public ReservableItem modifyBusinessReservableItem(Long itemId, Long businessId, ModifyReservableItemRequest request) {
    ReservableItem item = findBusinessReservableItemOrElseThrow(itemId, businessId);

    // 수정하려고 하는 날짜가 오늘보다 뒤여야함.
    isDateLaterToday(request.reservableTime());

    // isAvailable 판매 종료되어 변경 불가능
    isItemAvailable(item.getIsAvailable());

    item
            .setQuantity(
                    requireNonNullElse(request.quantity(), item.getQuantity())
            );

    item
            .setName(
                    requireNonNullElse(request.name(), item.getName())
            );
    item
            .setMaxQuantityPerUser(
                    requireNonNullElse(request.maxQuantityPerUser(), item.getMaxQuantityPerUser())
            );
    item
            .setPrice(
                    requireNonNullElse(request.price(), item.getPrice())
            );
    item
            .setReservableTime(
                    requireNonNullElse(request.reservableTime(), item.getReservableTime())
            );

    return item;
}

private void isItemAvailable(Boolean isAvailable) {
    if (!isAvailable) {
        throw new CustomException(ErrorCode.ITEM_IS_NOT_AVAILABLE);
    }
}

private void isDateLaterToday(Date date) {
    if (Objects.nonNull(date) && date.before(new Date(System.currentTimeMillis()))) {
        throw new CustomException(ErrorCode.RESERVATION_DATE_LATER_TODAY);
    }
}

변경 후 - ReservableItem 의 연관 엔티티가 추가되었음에도 서비스 코드의 복잡성이 줄어듦

@Transactional
public ReservableItemWithTimeDetailsResponse modifyReservableItem(Long reservableItemId, ModifyReservableItemRequest request) {
    Long businessId = request.businessId();

    ModifyReservableTime reservableTimeDto = request.reservableTime();

    ReservableItem reservableItem =
            reservableItemCustomRepository.findReservableItemByIdAndBusinessIdOrElseThrow(
                    reservableItemId,
                    new ForeignKey(businessId)
            );

    reservableItem
            .changeName(
                    request.name()
            )
            .changeMaxQuantityPerUser(
                    request.maxQuantityPerUser()
            )
            .changePrice(
                    new Money(request.price())
            )
            .changeIsItemAvailable(
                    request.isItemAvailable()
            )
            .updateReservableTime(
                    reservableTimeDto.reservableTimeId(),
                    reservableTimeDto.reservableTime(),
                    new Stock(
                            reservableTimeDto.stockQuantity(),
                            reservableTimeDto.stockUnit()
                    ),
                    reservableTimeDto.isTimeAvailable()
            );

    return ReservableItemWithTimeDetailsResponse.of(reservableItem);
}

관련 이슈

21

추가 정보