아래 간단한 엔티티에 대해서 피드백 받고 생각해본점을 정리했다
// Customer.java
@Entity
@Table(name = "customers")
@Getter
public class Customer {
@Id
private Long id;
private String firstName;
private String lastName;
public void setId(Long id) {
validateId(id);
this.id = id;
}
public void setFirstName(String firstName) {
validateName(firstName);
this.firstName = firstName;
}
public void setLastName(String lastName) {
validateName(lastName);
this.lastName = lastName;
}
private void validateId(Long id) {
if (id < 0)
throw new IllegalArgumentException("ID cannot be a negative number");
}
private void validateName(String name) {
final int MAX_NAME_LENGTH = 255;
if (name.contains(" "))
throw new IllegalArgumentException("Name cannot contain space");
if (name.length() > MAX_NAME_LENGTH)
throw new IllegalArgumentException(MessageFormat.format("Name length cannot exceed {0} characters", MAX_NAME_LENGTH));
}
}
엔티티에 들어갈 데이터를 검증하는 방법에는 여러가지가 있다. 타입화할 수도 있고, 세터에서 검증할 수도 있고, 별도 검증 클래스가 있을 수도 있고, DTO에서 할 수도 있다. 어디서 하는게 좋을까?
이름을 추가한다 할때
Name
이라는 타입을 만드는게 나을까?
class Name {
String name;
public Name(String name) {
validate(name);
this.name = name;
}
}
Name firstName;
Name lastName;
class Name {
String firstName;
String lastName;
public Name(String firstName, String lastName) {
validate(firstName, lastName);
this.firstName = firstName;
this.lastName = lastName;
}
}
Name name;
validateName()
같은 검증 메소드를 엔티티에 작성하는게 나을까?
맨위에 내가 작성엔 코드에서 했듯이 엔티티의 세터 메소드들에서 검증을 할 수도 있다
하지만 세터 메소드에 검증 로직이 포함되는것이 맞을까?
위 타입화와 마찬가지로 레이어 문제가 있다
별도
Validator
객체를 작성하는게 나을까?
class Validator {
public static void validateName(String name) {
if (!패턴)
throw new ...
}
}
레이어와 분리되어있기 때문에, 원하는 곳에서 사용할 수 있다. 하지만 이러한 검증기가 많아지면 정적 메소드들이 늘어난다는 점과 commons
같은 별도의 패키지를 관리해줘야한다
필드가 몇개 없는 객체의 검증이라면 에너테이션을 작성해 볼 수도 있다
엔티티에 도달하기 전의 DTO에서 검증하는게 나을까?
class CustomerDto {
private final Long id;
private final String firstName;
private final String lastName;
public CustomerDto (Long id, String firstName, String lastName) {
validate(id)
this.id = id;
validate(firstName)
this.firstName = firstName;
validate(lastName)
this.lastName = lastName;
}
...
}
레이어의 이동 전에 검증하기 때문에 오류 검출에 더 유리할 수 있다
검증은 레이어 간 데이터 이동을 위한 DTO의 목적에 부합하기도 하다
검증 방식과 위치에 따라 장단점이 있을 수 있다
왜 해당 위체에서 검증을 하는지 꼭 생각해보자
검증 방식을 반드시 하나만 골라야하는 것은 아니다. 검증을 여러 레이어에서 해야 더욱 데이터의 상태가 안전해진다
다른 팀원들 중 Customer 엔티티에 빌더 패턴을 적용한 분들도 있었다. 해당 엔티티에 생성은 어떻게 해볼 수 있을까?
빌더 패턴을 통해 생성하는게 나을까?
Customer customer = Customer.builder()
.id(1L)
.firstName("Sangmin")
.lastName("Lee")
.build;
빌더 패턴이 필요한지 생각해보자
보통 필드가 4개가 넘어가면 빌더 패턴을 고려한다고 한다
위 경우에는 3개의 필드를 가지는데 그냥 생성자를 사용해도 괜찮지 않을까?
생성자를 사용하는게 나을까?
// 훨씬 짧고 간결하다
Customer customer = new Customer(1L, "Sangmin", "Lee");
인자 수가 적다면, 동일한 객체를 만들때 그냥 생성자를 사용하는게 더 간결할 수 있다
정말 팩토리 패턴이 필요한지 생각해보자
생성자 대신 정적 팩토리 메소드 사용을 고려해 볼 수도 있다
정적 팩토리 메소드는 생성자에 비해 다음과 같은 장점을 가진다
String.valueOf
가 대표적인 표준 자바의 정적 팩토리 메소드이다
// 당연히 아무도 이렇게 안쓰지만 이렇게 쓰는게 가능하긴 하다
String name = new String("Sangmin");
// 정적 팩토리를 통해 다양한 로직을 명확하게 실행할 수 있다
String one = String.valueOf(1);
String bool = String.valueOf(true);