Post

[리팩토링] UserEntity SRP/DIP 위반과 해결

[리팩토링] UserEntity SRP/DIP 위반과 해결

User 엔티티의 문제점

엔티티는 도메인 모델이자 비즈니스 로직과 상태만 가져야 한다. 하지만 기존 코드는 SRP와 DIP를 위반하고 있었다.

1
2
3
4
5
6
7
8
9
10
11
12
public static User register(UserDto dto, UserValidator validator, PasswordEncoder passwordEncoder) {
    validator.RegisterValidate(dto.getUsername(), dto.getEmail());

    Username username = new Username(dto.getUsername());
    Email email = new Email(dto.getEmail());
    String encodedPassword = passwordEncoder.encode(dto.getPassword()); // 암호화
    Password password = new Password(encodedPassword); // 암호화된 값으로 생성
    Nickname nickname = new Nickname(dto.getNickname());
    Phone phone = new Phone(dto.getPhone());

    return new User(username, email, password, nickname, phone);
}

DIP(의존성 역전 원칙) 위반
도메인 계층의 User가 인프라 계층의 PasswordEncoder(기술 서비스) 에 의존한다. 즉 도메인 모델이 애플리케이션 계층을 참조해 DIP를 위반했다.

SRP 위반
User는 도메인 객체인데 암호화(PasswordEncoder) 책임 까지 가지고있다..
User는 ‘사용자 도메인 규칙 관리’ 책임만 가져야 한다.

1
2
// 도메인 계층의 User가 외부 계층에 의존
User → PasswordEncoder (인프라 계층)

정적 팩토리 메서드 사용은 맞는데, 팩토리 메서드를 두는 위치가 문제. “도메인 엔티티 안에 둘지? 애플리케이션 계층으로 올릴지?”

엔티티는 비즈니스 상태와 행위만 가져야 한다

리팩터링

엔티티는 비지니스 로직에 집중 해야한다.

  • 간단한 정적 팩토리 메서드(단순 생성 + 검증) -> 엔티티 내부
  • 외부 의존성이 필요한 생성 로직 -> Service/Factory 클래스로 분리
1
2
3
public static User of(Username username, Email email, Password password, Nickname nickname, Phone phone) {
    return new User(username, email, password, nickname, phone);
}
  • 순수한 정적 팩토리 메서드 (외부 의존성 제거)

편의를 위해 UserFactory 클래스를 만들었다.

1
2
3
4
5
6
7
8
9
10
11
12
public class UserFactory {
  public static User createFromPrimitives(String username, String email, String encodedPassword, String nickname, String phone) {
      return User.of(
              new Username(username),
              new Email(email),
              new Password(encodedPassword),
              new Nickname(nickname),
              new Phone(phone)
      );
  }
}

  • User를 생성하는 편의 메서드를 별도의 클래스로 분리.

애플리케이션 서비스에서 책임 조율

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public void registerUser(UserDto userDto) {
    //암호화
    String encryptedPassword = passwordEncoder.encode(userDto.getPassword());

		//순수 팩토리 사용
    User user = UserFactory.createFromPrimitives(
            userDto.getUsername(),
            userDto.getEmail(),
            encryptedPassword,
            userDto.getNickname(),
            userDto.getPhone()
    );
    
    //저장
    userRepository.save(user);
}
  • 리팩토링함으로써 User 엔티티는 도메인 본연의 역할만 담당하게 되었고, 암호화는 각 계층에서 책임을 나눠 SRP와 DIP 원칙을 지킬 수 있게 되었다.
  • SRP와 DIP를 의식한 설계 덕분에 계층 간 결합도가 줄어들고, 책임이 명확하게 분리된 아키텍처를 얻을 수 있었다.
This post is licensed under CC BY 4.0 by the author.