
테이블 설계관점에서 조회와 쓰기 사이의 트레이드 오프
간단한 SNS 서비스를 구현하면서 정규화와 비정규화를 해보자.
깃참고
일단 멤버의 가입, 닉네임 변경에 대한 로직은 간단히 작성한 후 회원의 닉네임 변경이력을 조회 할 수 있어야한다. 부분을 진행하도록 하겠다.
요구사항에 따라 데이터의 최신성을 보장해야하는 데이터인지 과거의 내역을 보장해야하는 데이터인지를 고려해야한다.
그러므로 현재의 경우 MemberNicknameHistory의 nickname과 Member의 nickname은 둘 다 존재해야하는 경우이다.
처음에 가입 시 MemberNicknameHistory에 첫 닉네임과 함께 같이 저장하고 변경 시에 새로운 닉네임을 받아 같이 저장해주는 로직으로 작성했다.
MemberNicknameHistory entity 정의하기
package com.example.twittermysql.domain.member.entity;
import java.time.LocalDateTime;
import java.util.Objects;
import lombok.Builder;
import lombok.Getter;
@Getter
public class MemberNicknameHistory {
final private Long id;
final private Long memberId;
final private String nickname;
final private LocalDateTime createdAt;
@Builder
public MemberNicknameHistory(Long id, Long memberId, String nickname, LocalDateTime createdAt) {
this.id = id;
this.memberId = Objects.requireNonNull(memberId);
this.nickname = Objects.requireNonNull(nickname);
this.createdAt = createdAt == null ? LocalDateTime.now() : createdAt;
}
}
follow는 데이터의 최신성을 보장해야하는 경우다.
follow에서 member를 조인하게 되면 follow 서비스에 멤버가 침투하게 될것이다...
그러면 두 도메인 간에 엄청난 결합이 이뤄진다.
초반부터 큰 결합을 가지는 경우는 유연성이 좋지 않은 서비스가 될 가능성이 높다.
리팩토링도 힘들어진다.
초반에 결합을 낮추면서 개발을 하는 것이 좋다.
follow는 중복을 제거하는 것이 비즈니스 요구사항에 더 적합하다.
Follow entity 정의하기
package com.example.twittermysql.domain.follow.entity;
import java.time.LocalDateTime;
import java.util.Objects;
import lombok.Builder;
import lombok.Getter;
@Getter
public class Follow {
final private Long id;
final private Long fromMemberId;
final private Long toMemberId;
final private LocalDateTime createdAt;
@Builder
public Follow(Long id, Long fromMemberId, Long toMemberId, LocalDateTime createdAt) {
this.id = id;
this.fromMemberId = Objects.requireNonNull(fromMemberId);
this.toMemberId = Objects.requireNonNull(toMemberId);
this.createdAt = createdAt == null ? LocalDateTime.now() : createdAt;
}
}
