@Embedded 타입으로 빼놓은 Address를 그대로 사용했습니다. 이처럼 핵심 도메인에 대해서 데이터의 연관성이 있는 것들을 Embedded 분리해놓으면 여러모로 좋습니다.
DateTime 클래스도 Embedded 타입으로 지정해서 반복적인 생성일, 수정일 칼럼들을 일관성 있고 편리하게 생성할 수 있습니다.
지금부터는 1:N 관계 팁에 관한 이야기를 진행하겠습니다.
Delivery를 통해서 DeliveryLog를 관리함으로 CascadeType.PERSIST 설정을 주었습니다.
1: N 관계를 맺을 경우 List를 주로 사용하는데 객체 생성을 null로 설정하는 것보다 new ArrayList&lt&gt();설정하는 것이 바람직합니다. 이유는 다음과 같습니다.
1 2 3 4 5
privatevoidverifyStatus(DeliveryStatus status, Delivery delivery){ if (!delivery.getLogs().isEmpty()) { ... } }
초기화하지 않았을 경우 null로 초기화되며 ArrayList에서 지원해주는 함수를 사용할 수 없습니다. 1:N 관계에서 N이 없는 경우 null인 상태인 보다 Empty 상태가 훨씬 직관적입니다. null의 경우 값을 못가져 온것인지 값이 없는 것인지 의미가 분명하지 않습니다.
CascadeType.PERSIST 설정을 주면 Delivery에서 DeliveryLog를 저장시킬 수 있습니다. 이 때 ArrayList 형으로 지정돼 있다면 add 함수를 통해서 쉽게 저장할 수 있습니다. 이렇듯 ArrayList의 다양한 함수들을 사용할 수 있습니다.
FetchType.EAGER 통해서 모든 로그 정보를 가져오고 있습니다. 로그 정보가 수십 개 이상일 경우는 Lazy 로딩을 통해서 가져오는 것이 좋지만 3~4개 정도로 가정했을 경우 FetchType.EAGER로 나쁘지 않다고 생각합니다.
privatevoidverifyNotYetDelivering(){ if (isNotYetDelivering()) thrownew DeliveryAlreadyDeliveringException(); }
privatevoidverifyAlreadyCompleted(){ if (isCompleted()) thrownew IllegalArgumentException("It has already been completed and can not be changed."); } }
객체의 상태는 언제나 자기 자신이 관리합니다. 즉 자신이 생성되지 못할 이유도 자기 자신이 관리해야 한다고 생각합니다. 위의 로직은 다음과 같습니다.
cancel() : 배송을 취소하기 위해서는 아직 배달이 시작하기 이전의 상태여야 가능합니다.
verifyAlreadyCompleted() : 마지막 로그가 COMPLETED 경우 더는 로그를 기록할 수 없습니다.
즉 자신이 생성할 수 없는 이유는 자기 자신이 갖고 있어야 합니다. 이렇게 되면 어느 곳에서 생성하든 같은 기준으로 객체가 생성되어 생성 관리 포인트가 한 곳에서 관리됩니다.
배송 로그 저장
1 2 3 4 5 6 7 8 9 10 11 12 13 14
publicclassDeliveryService{
public Delivery create(DeliveryDto.CreationReq dto){ final Delivery delivery = dto.toEntity(); delivery.addLog(DeliveryStatus.PENDING); return deliveryRepository.save(delivery); }
public Delivery updateStatus(long id, DeliveryDto.UpdateReq dto){ final Delivery delivery = findById(id); delivery.addLog(dto.getStatus()); return delivery; } }
create : Delivery 클래스를 생성하고 delivery.addLog를 PENDING 상태로 생성하고 Repository의 save 메소드를 통해서 저장할 수 있습니다. 최종적인 JSON 값은 아래와 같습니다.
updateStatus : 해당 객체를 데이터베이스에서 찾고 해당 배송 객체에 배송 로그를 추가합니다. 배송 로그에 추가적인 로그 저장은 delivery.addLog(..); 메서드를 통해서 진행됩니다. 언제나 관리 포인트를 줄이는 것은 중요하다고 생각됩니다.
마무리
코드 양이 많아지고 있어서 반드시 전체 코드와 테스트 코드를 돌려 보면서 이해하는 것을 추천해 드립니다. 이전 포스팅에서도 언급한 적 있지만 소스코드에서는 setter 메서드를 사용하지 않고 있습니다. 무분별하게 setter 메서드를 남용하는 것은 유지 보수와 가독성을 떨어트린다고 생각합니다. 다음 포스팅에서는 setter를 사용하지 않는 장점에 대해서 조금 더 깊게 설명해 보겠습니다.