Post

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

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

User 엔티티의 문제점

엔티티는 도메인 모델이자 순수한 비즈니스 상태와 행위만 가져야 한다.

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.java의 register 메서드를 보면 UserValidator(응용 서비스 계층 역할), PasswordEncoder(기술 서비스) 에 의존한다. 즉 도메인 모델이 애플리케이션 계층을 참조해 DIP를 위반했다.

SRP 위반
User는 도메인 객체인데 검증(UserValidator) + 암호화(PasswordEncoder) 책임 까지 가지고있다..

1
2
3
// 도메인 계층의 User가 외부 계층에 의존
User  UserValidator (애플리케이션 계층)
User  PasswordEncoder (인프라 계층)

정적 팩토리 메서드 사용은 맞는데, 팩토를 메서드를 두는 위치가 문제 “도메인 엔티티 안에 둘지? 애플리케이션 계층으로 올릴지? 엔티티는 순수한 비즈니스 상태여야 하니 후자가 맞는거 같다.

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

리펙토링

  1. 엔티티는 순수 해야한다.
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);
    }
  • 순수한 정적 팩토리 메서드 (외부 의존성 제거)
  1. 편의를 위해 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. 애플리케이션 서비스에서 책임 조율
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void registerUser(UserDto userDto) {
		//검증
    userValidator.validateForRegistration(userDto.getUsername(), userDto.getEmail());
    //암호화
    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.