도메인 주도 설계 철저 입문 2장 학습 내용을 정리 한다.
도메인 주도 설계 철저 입문 - 예스24
이해하기 쉬운 패턴부터 학습하자! 도메인 주도 설계를 쉽게 이해할 수 있는 입문서!초심자라도 이해하기 쉽고 실천하기도 쉬운 패턴부터 시작해 구체적인 예제와 함께 도메인 주도 설계에서
www.yes24.com
값 객체란?
프로그래밍 언어에는 원시 데이터 타입이 있다. 이 원시 데이터 타입만 사용해 시스템을 개발할 수도 있지만, 때로는 시스템 특유의 값을 정의해야 할 때가 있다. 이러한 시스템 특유의 값을 표현하기 위해 정의하는 객체를 값 객체라고 한다.
// 원시 데이터 타입의 값으로 '성명' 나타내기
string fullName1 = "이 민규";
string fullName2 = "LEE MINKYU";
성과 이름을 구분하는 요구 사항이 있을 때 원시 데이터 타입인 string을 사용한다면 위와 같이 사용할 수 있을 것이다. 공백을 보고 성과 이름을 구분이 필요하다는 생각을 유추할 수도 있겠지만 어떤 사람은 오타인가 라고 의문을 가질 수도 있고, 언어가 달라진다면 성과 이름이 어떤 순서로 쓰여졌는지 오해할 수도 있을 것 같다.
DDD에서는 값을 명확히 표현해 주기 위해 값을 표현하는 객체인 값 객체를 사용한다.
// 성과 이름을 나타내기 위한 FullName 클래스
class FullName
{
public FullName(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; }
public string LastName { get; }
}
// firstName과 lastName 구분이 명확해진다.
var fullName = new FullName("민규", "이");
Console.WriteLine(fullName.LastName); // "이"
값의 성질과 값 객체 구현
값의 성질을 아는 것은 값 객체를 이해하기 위해 중요한 사항이다. 대표적인 값의 성질 세 가지는 아래와 같다.
- 변하지 않는다.
- 주고 받을 수 있다.
- 등가성을 비교할 수 있다.
1. 값의 불변성
값은 변화하지 않는 성질을 갖는다. 책 내용이 한번에 이해가 되지 않아 몇번을 읽고 이해하게 됐다. 보통 변수의 값을 수정할 때 아래와 같이 코드를 나타낼 수 있다.
string name = "이 민규";
Console.WriteLine(name) // -> "이 민규"
name = "LEE MINKYU";
Console.WriteLine(name) // -> "LEE MINKYU"
name 변수의 값이 "이 민규"에서 "LEE MINKYU"로 수정되었다. 여기서 변경된 것은 name 변수의 내용이 변경된 것이지 "이 민규"라는 값 자체에서 "LEE MINKYU"로 변경된 것은 아니다. 값 자체에서 변경이 일어나려면 "이 민규" 라는 문자열에서 "LEE MINKYU"로 변경할 수 있는 메서드를 지원하면 가능할 수 있겠지만 있다고 하더라도 보통 변수에 새로운 값을 대입하지 않냐 이런 요지로 파악이 된다.
정리하면, 객체 내에서 필드를 수정하는 메서드는 정의해서는 안된다.
var fullName = new FullName("이", "민규");
// 이렇게 값을 변경하는 메서드를 구현할 수 있다고 생각하지 말 것
fullName.setLastName("LEE");
fullName.setFirstName("MINKYU");
// 아래와 같이 시도하려고 하는거랑 같은 의미다.
"이".setLastName("LEE") + " " + "민규".setFirstName("MINKYU");
// 수정하려할 때 새로운 인스턴스를 대입해야 한다.
fullName = new FullName("LEE", "MINKYU");
2. 교환 가능 ( '=' 대입 )
// 숫자 값 수정
int age = 20;
age = 21;
// 문자열 값 수정
string name = "이민규";
name = "LEE MINKYU";
// 값 객체값 수정
var fullName = new FullName("이", "민규");
fullName = new FullName("LEE", "MINKYU");
3. 등가성 비교 가능
// 1. 숫자 값 비교
Console.WriteLine(0 == 0) // -> true
Console.WriteLine(0 == 1) // -> false
// 2. 문자 값 비교
Console.WriteLine("hello" == "hello") // -> true
Console.WriteLine("hello" == "hi") // -> false
// 3. 값 객체 값 비교 ( O )
var fullName1 = new FullName("이", "민규");
var fullName2 = new FullName("이", "민규");
Console.WriteLine(fullName1.Equal(fullName2)); //등가성을 비교하는 메서드 구현하고 필드값들은 메서든 내에서 비교
// 4. 값 객체 값 비교 ( X ) - 아래처럼 속성 값을 꺼내서 비교 하지 말 것
var fullName1 = new FullName("이", "민규");
var fullName2 = new FullName("이", "민규");
Console.WriteLine(
fullName1.LastName == fullName2.LastName &&
fullName1.FirstName == fullName2.FirstName)
)
값 객체가 되기 위한 기준
위의 FullName 클래스의 속성 (FirstName, LastName) 또한 원시 데이터 타입을 사용하는 것이 아닌 객체로 생성할 수 있을 것이다. 어디까지 객체로 생성할 것인지를 판단하는데 기준을 가지고 있어야 한다. 그 기준은 사람, 상황마다 다르게 생각할 수 있고 명확하게 답이 있는 것은 아니다. 작가의 값 객체로 정의해야 할지에 대한 기준은 '규칙이 존재하는가'와 '낱개로 다루어야 하는가'라는 점을 중요하게 본다고 한다. 기준에 부합하면 값 객체를 생성한다.
FirstName과 LastName이 각각 더 이상 표현할 규칙이나 특성이 없다고 하면 원시데이터 타입을 사용 한다. 공통적인 특성이 존재하면 두개를 나타내는 Name이라는 각 객체를 만들어서 표현할 수도 있을 것이다. 또는 각 각 별도의 규칙이나 특성이 있다면 FirstName객체, LastName 객체를 추가할 수 있을 것이다. 이러한 기준에서 상황에 맞게 값 객체를 생성할지 판단할 수 있을 것 같다.
행동 정의 가능 (메서드)
독자적인 행위를 정의할 수 있다는 점이 중요하다. 돈 객체를 예로 들면,
class Money
{
private readonly decimal amount;
private readonly string currency;
public Money(decimal amount, string currency)
{
if (currency == null) throw new ArgumentNullException(nameof(currency));
this.amount = amount;
this.currency = currency;
}
}
Money 값 객체가 있다. 돈은 덧셈이 가능하다. 덧셈이 가능한 특성을 고려해 Add메서드를 정의해 줄 수 있다.
class Money
{
private readonly decimal amount;
private readonly string currency;
(..생략..)
public Money Add(Money arg)
{
if (arg == null) trow new ArgumentNullException(nameof(arg));
if (currency != arg.currency) throw new ArgumentException("화폐가 다름"); // 이런 조건도 넣어줄 수 있고
return new Money(amount + arg.amount, currency); // 새로운 인스턴스 만들어서 넘겨주는것도 포인트
}
}
값 객체를 도입했을 때의 장점
1. 표현력이 증가한다. ( 필드, 메서드로 값의 특유의 성질을 나타낼 수 있다. 문서를 대체할 수 있다. )
2. 무결성이 유지된다. ( 특성을 고려한 validation 등 )
3. 잘못된 대입을 방지한다. ( Type 오류 )
4. 로직이 코드 이곳저곳에 흩어지는 것을 방지한다. ( 규칙들을 객체 내에서 정의해주면 되니까)
단점
1. 너무 많은 클래스를 생성해야 한다. ( 해보지 않았지만 벌써부터 귀찮다. )
+ 사용할 때도 귀찮음 ( 인스턴스 생성이 많아짐, 맵핑도 귀찮아짐 )
정리
값 객체의 개념은 '시스템 고유의 값을 만드는 것' 이다. 물론 원시 타입의 값만으로도 소프트웨어를 만들 수 있다. 그러나 원시 타입은 지나치게 범용적이기에 표현력이 빈약한다.
도메인에는 다양한 규칙이 포함된다. 값 객체를 정의하면 이러한 규칙을 값 객체 안에 기술해 코드 자체가 문서의 역할을 할 수 있다. 시스템의 명세는 일반적으로 문서에 정리되는데, 이때 코드로 규칙을 나타낼 수 있다면 더 나을 것이다. 값 객체는 도메인 지식을 코드로 녹여내는 도메인 주도 설계의 기본 패턴이다. 도메인의 개념을 객체로 정의할 때는 우선 값 객체에 적합한 개념인지 검토가 필요하다.
'서적 > 도메인 주도 설계 철저 입문' 카테고리의 다른 글
[도메인주도설계철저입문] 7장 소프트웨어의 유연성을 위한 의존 관계 제어 (0) | 2024.03.14 |
---|---|
[도메인주도설계철저입문] 6장 애플리케이션 서비스 (0) | 2024.03.03 |
[도메인주도설계철저입문] 5장 리포지토리 (0) | 2023.10.16 |
[도메인주도설계철저입문] 4장 도메인 서비스 (0) | 2023.10.16 |
[도메인주도설계철저입문] 3장 도메인 엔티티 (0) | 2023.10.15 |