VO (Value Object) 정리
Published in
4 min readJan 10, 2023
인터넷에 VO를 정리한 많은 글이 있지만 굳이 포스팅을 하는 이유는 최근 받은 리뷰 과정에서 VO를 제대로 몰랐기에 정리하고자 합니다.
그랬다.. VO가 값 그 자체를 나타내는 객체
로 알고 있었지만 정작 그 특징을 제대로 알지 못했다.
VO는 크게 3가지 특징을 갖는다.
- 불변성
Value Object는 불변하다. 즉 수정자(setter)가 없다.
불변하기 때문에 존재하는 장점은 참조를 통해 공유해도 이로 인한 Side Effect가 없다. - 값 동등성
두 Value Object가 동일한 값을 가진다면 둘은 동등하다. 이를 위해 equals와 hashcode 메소드를 재 정의한다. - 자가 유효성 검증
Value Object는 유효한 값으로만 생성된다. 즉 아래와 같은 Age VO가 있을 때 생성된 모든 Age 값 객체는 0 이상을 보장할 수 있다.
@Embeddable
public class Age {
private int value;
protected Age() {}
public Age(int value) {
validate(value);
this.value = value;
}
public void validate(int value) {
if (value < 0) {
// 예외 발생
}
}
}
VO 사용시 장점
3번 자가 유효성 검증
에서 알 수 있듯이 객체에 제약사항을 둘 수 있으며 VO 자체로 좀 더 객체지향적인 프로그래밍을 할 수 있다.
또한 Entity가 유효성 검증 코드로 거대해지는 것을 완화할 수 있다. 아래 Human 이란 엔티티가 있고 name, age 컬럼이 있다고 가정하자. 이 때 name과 age에 유효성을 검증하는 코드가 있다.
@Entity
public class Human {
private String name;
private int age;
public Human(String name, int age) {
if (name.isEmpty()) {
// name 에러
}
if (age < 0) {
// age 에러
}
this.name = name;
this.age = age;
}
}
만약 Human 엔티티에 컬럼이 추가되고 유효성 검증이 필요한 경우 이를 검증하는 코드들이 늘어날 것이다.
하지만 name과 age를 각각 값 객체로 만들고 거기서 유효성 검증을 한다면 Human 엔티티는 각 컬럼에 대한 유효성 검사를 하는 책임을 덜 수 있다.
@Entity
public class Human {
private Name name; // Name Value Object 내부에 빈 값에 대한 유효성 검증
private Age age; // Age Value Object 내부에 0 보다 작은 값에 대한 유효성 검증
public Human(Name name, Age age) {
this.name = name;
this.age = age;
}
}
VO Getter 메소드 명
사람마다 취향 차이는 있겠지만 Value Object의 getter 메소드명이 get 보다는 value 라는 이름이 개인적으로 더 적절하다고 생각한다.
public class Age {
private int value;
protected Age() {}
public Age(int value) {
this.value = value;
}
public int value() {
return value;
}
}
레퍼런스
https://medium.com/@nicolopigna/value-objects-like-a-pro-f1bfc1548c72