값들을 하나의 곳으로 모아서, 목적에 따라 인스턴스 간 주고받을 수 있게 포장되어지는 데이터셋
It is a Data Transfer Object, i.e. the data aggregate object, sole purpose of which is to facilitate conveying data from the point A (the source) to the point B (destination).
목적에 따른 값을 추출해 A→B로 값을 옮기기 위함
to convey data from point A (the source) to the point B (destination).
어떤 반환값을 전달할 지에 따라 달려있다. 값이 어떤 값을 나타내어야 하는지에 따라 다르다.
it depends on what do you use response for; what representation of data do you need; what should be the capacity and purpose of the object in question, and etc..
아래 두 가지 유형이 있겠다!
- 여러 인스턴스로부터 값을 추출하여 다른 곳으로 리소싱할 때
- 인스턴스로부터 값들을 추출한 뒤, 값을 깎아서 반환해야할 때
- When you want to aggregate the data for your object from different [re]sources, i.e. you want to put some object transformation logic between the Persistence Layer and the Business(or Web) Layer

VO란 이렇게 도메인에서 한 개 또는 그 이상의 속성들을 묶어서 특정 값을 나타내는 객체
When programming, I often find it's useful to represent things as a compound.
👉 프로그래밍할 때, 사물을 복합물로 표현하는 것이 유용한 경우가 종종 있다.
A 2D coordinate consists of an x value and y value. An amount of money consists of a number and a currency. A date range consists of start and end dates, which themselves can be compounds of year, month, and day.
👉 예를 들면 x, y로 이루어진 2차원 좌표를 표현하거나, 숫자와 통화로 이루어진 금액, 시작 날짜와 끝 날짜로 이루어진 날짜 기간 등이 있다.
To avoid “Primitive Obsession”
사용자가 값을 입력한다. 그런데, 사용자의 입력값은 언제나 어떤 특정한 목적성을 가진 값이다.
따라서 특정 값들은 동일한 캐릭터로서 , 즉 하나의 type으로서 보장받아야한다.
우리는 이것을 원시값으로 저장하면 안 된다.
우리는 원시값을 이용하여 그 캐릭터를 만들어주어야 한다!!
/** 사용자가 좌표값을 입력 **/
// 좌표인지, 좌표가 아닌 다른 값인지 구분할 수 있는가?
int x = 3; int y =5;
// 확실히 구분가능하다.
Point p = new Point(3,5);
판단에는 ”어떤 공통된 성격의 원시값들을 살아있는 인스턴스로 만들어내는 작업” 이라고 판단된다.
왜냐하면 다음과 같은 특징이 있기 때문이다.
아래와 같이, 엔티티 내에서 일급 컬렉션과 같은 구조로 사용할 때 용이할 것 같다.
public final class EmailAddress {
private static final EmailValidator validator = EmailValidator.getInstance();
private final String value;
public EmailAddress(String value) {
if (!validator.isValid(value)) {
throw new InvalidEmailException();
}
this.value = value;
}
public EmailAddress change(String value) {
return new EmailAddress(value);
}
@Override
public String toString() {
return value;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
EmailAddress that = (EmailAddress) o;
return Objects.equals(value, that.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}
값을 하나의 캐릭터로(타입으로) 사용하고자 할 때 (”Avoid primitive obsession”)
// How can you identify the difference ??
Double price;
Double weight;
Double height;
// VO do validation, and won't ever change its value !!
Price price;
Weight weight;
Height height;
각 속성에 대한 equality를 강력하게 해야할 때
if(EmailAddress.equals(EmailAddress)){
,,,,
}
함수의 파라미터가 공통된 특성일 때
/** Avoid this ❌ **/
void sendEmail(String email, String subject, String body);
// => sendEmail("Some subject", "Some content", "john@doe.com");
// => 이렇게, 잘못된 입력값에 대해 검증을 못 하거나, 따로 해주어야하는 상황 발생
/** Favor this ✅ **/
1. sendEmail(EmailAddress recipient, Subject subject, Content content);
2. sendEmail(Email email);