728x90
반응형
1.값객체란?
- v시스템 특유의 값을 표현 하기 위해 정의하는 객체를 값 객체
- 프로그래밍 언어에는 원시 데이터 타입이 있음
- 원시데이터 타입만 사용해 시스템을 개발할 수 있으나 때로는 특유의 값을 정의해야 할 때가 있음
1.1 원시데이터 값으로 성명 표현
var fullName = "naruse mananobu";
Console.WriteLine(fullName);// naruse mananobu 값이 출력됨
- 성만 출력하고 싶거나, 성과 이름 순으로 출력하고 싶은 다양한 요구사항이 있는 경우에 아래와 같이 우선 성씨만 출력하는 경우이다.
1.2 이름 중 성씨만 출력
var fullName = "naruse mananobu";
var tokens = fullName.Split(' ');// ["naruse", "mananobu" ] 배열이됨
var lastName = tokens[0];
console.WriteLine(lastName);// naruse 가 출력됨
- 위가 제대로 동작한다고 생각이 들 수 있지만 이름을 쓰는 관습에 따라서
- 실제 성은 smith인데 성씨를 뒤에 쓰는 관습에 따라서 john smith라고 쓴다면
- 위의 코드에 적용이 된다면 이름이 john이 출력이 되기 때문에 제대로 동작하지 않을 수 있음
2.이를 해결하기 위한 방법
- 대개 객체지향 프로그래밍에서는 클래스를 사용함
2.1 클래스로 만들기
Class FullName
{
public FullName (string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
2.1.1 FullName 클래스의 LastName 프로퍼티 사용
var fullName = new FullName("masanobu", "naruse");
Console.WriteLine(fullName.LastName); // naruse가 출력
- 시스템에서 어떤 처리를 해야 하는지에 따라 값을 나타내는 표현이 정해짐
- FullName 클래스는 이 시스템의 필요에 맞는 성명을 나타내는 표현
- 객체이기도 하고 동시에 값이기도함
- 이를, 값 객체라고 함
- 객체이기도 하고 동시에 값이기도함
3.값의 성질과 값 객체의 구현
- 값에도 일정한 성질이 있음
- 이 값의 성질이 값 객체를 이해하는 열쇠
3.1 값의 성질 대표적 세가지
- 변하지 않음
- 주고받을 수 있음
- 등가성을 비교할 수 있음
3.2 값의 불변성
var greet = "안녕하세요";
Console.WriteLine(greet); // 안녕하세요 출력됨
greet = "Hello";
Console.WriteLine(greet); // Hello가 출력됨
- 위에 처럼 값을 수정할 수 있다고 보편적으로 생각함
- 하지만 우리가 값을 수정할 때는 새로운 값을 대입
- 대입은 값을 수정하는 과정이 아님
- 대입을 통해 수정되는 것은 변수의 내용이고, 값 자체가 수정되는것 아님
3.2.1 값을 수정하는 의사 코드
var greet = "안녕하세요";
greet.ChangeTo("Hello"); // 실제로는 이런 메소드 없음
Console.WriteLine(greet); // Hello가 출력됨
- 이해가 안된다고 하더라도 위와 같은것이 허용이 되면 아래와 같은 경우도 허용한다는 소리
"안녕하세요".ChangeTo("Hello");
Console.WriteLine("안녕하세요"); // Hello가 출력됨
- 이런것이 허용이 되면 개발자들은 혼란스러울 것
- 즉, 위와 같이 값을 수정한다면 안심하고 값을 사용 할 수 없음
- 결론, 1이라는 숫자가 갑자기 0이 되면 안되고, 1이라는 숫자는 항상 1이어야 하는 것처럼 값은 변하지 않는다는 것임
3.2.2 일반적으로 볼 수 있는 값 수정
var fullName = new FullName("masanobu", "naruse");
fullName.ChangeLastName("sato");
- 대부분 개발자 입장에서는 자연스러움
- 그러나, FullName 클래스를 값으로 간주할 경우 부자연스러움
- FullName은 시스템 특유의 값을 표현하는 값 객체
- FullName도 값이기도함
- 그래서 변하지 않아야함
- 즉, FullName 클래스에 값을 수정하는 기능을 제공하는 ChangeLastName같은 메서드가 정의되면 안됨
3.3 교환가능하다
- 값은 불변일지라도 값을 수정할 필요는 있음
3.3.1 평소 값을 수정하는 방식
// 숫자값 수정
var num = 0;
num = 1;
// 문자값 수정
var c = '0';
c = 'b';
// 문자열값 수정
var greet = "안녕하세요";
greet = "hello";
- 모두 변수에 값을 대입하는 코드
- 즉, 대입문 자체가 값의 수정을 나타내는 방법
- 값이든 값객체든 그 값자체를 수정하면 안되지만 대입문을 통해 교환의 형식으로 표현됨
3.3.2 값 객체를 수정하는 방법
var fullName = new FullName("masanobu", "naruse");
fullName = new FullName("masanobu", "sato");
3.4 등가성 비교 가능
- 같은 종류의 값끼리는 비교할 수 있음
Console.WriteLine(0 == 0);
Console.WriteLine('a' == 'b');
Console.WriteLine( "hello" == "hello");
- 표현식 0 == 0에서 좌변의 0과 우변의 0은 인스턴스로서는 별개의 존재이지만
- 같은 값으로 취급됨
- 값은 값 자신이 아니라 값을 구성하는 속성을 통해 비교된다는 점
- 값 객체도 값 객체를 구성한느 속성(인스턴스 변수)을 통해 비교됨
3.4.1 값 객체 간의 비교
var nameA = new FullName("masanobu", "naruse");
var nameB = new FullName("masanobu", "naruse");
// 두 인스턴스를 비교
Console.WriteLine(nameA.equals(nameB)); // 인스턴스를 구성하는 속성이 같아서 true
3.4.2 속성값을 꺼내 직접 비교하기
var nameA = new FullName("masanobu", "naruse");
var nameB = new FullName("john", "smith");
var compareResult = nameA.FirstName == nameB.FirstName &&
nameA.LastName == nameB.LastName;
Console.WriteLine(compareResult);
- 언뜻보면 자연스러운 코드 같지만 FullName 객체가 값이라는 사실을 생각하면 부자연스러운 코드임
속성을 꺼내 직접 비교하는 방식을 숫자에 적용한 코드
Console.WriteLine(1.Value == 0.Value); //false?
- 위와 같은 코드는 본적이 없을 것 값의 값을 꺼낸다는 것은 자연스러운것이 아님
- 값 객체는시스템 고유의 값임, 결국 값
- 따라서, 값의 속성을 꺼내 비교하는 것이 아니라, 값과 마찬가지로 직접 값끼리 비교하는 방식이 자연스러움
3.4.3 값끼리 직접 비교하기
var nameA = new FullName("masanobu", "naruse");
var nameB = new FullName("jhon", "smith");
var compareResult = nameA.equals(nameB);
Console.WriteLine(compareResult);
// 연산자 오버라이드를 활용할 수도 있음
var compareResult2 = nameA == nameB;
Console.Write(compareResult2);
- 위 처럼 자연스러운 코드를 사용하려면 값 객체를 비교하는 매서드를 제공해야 함
비교 메서드를 제공하는 FullName 클래스
class FullName : IEquatable<FullName>
{
public FullName(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName {get;}
public string LastName {get;}
public bool Equals(FullName other)
{
if(ReferenceEquals(null, other)) return false;
if(ReferenceEquals(this, other)) return true;
return string.Equals(FirstName, other.FirstName)
&& string.Equals(LastName, other.LastName);
}
public override bool Equals(object obj)
{
if(ReferenceEquals(null, obj)) return false;
if(ReferenceEquals(this, obj)) return true;
if(obj.GetType() != this.GetType()) return false;
return Equals((FullName) obj);
}
// C#에서 Equals를 오버라이드하려면 GetHashCode를 함께 오버라이드해야 한다.
public override int GetHashCode()
{
unchecked
{
return ((FirstName != null ? FirstName.GetHashCode() : 0) * 397)
^ (LastName != null ? LastName.GetHashCode() : 0);
}
}
}
- 위의 코드가 C#에서 비교를 구현하는 전형적인 코드
- 두 값 객체를 비교하려면 값 객체의 속성을 꺼내 비교하는 대신 Equals 메서드를 쓰면 됨
- 위의 방법으로 값 객체도 값과 같은 방법으로 비교할 수 있음
객체를 비교하는 코드를 자연스럽게 만든다는 목적만으로 이만큼의 코드를 작성하는 게 찜찜할 수 있지만 위의 경우는 명확한 장점이 있음
- 값 객체에 인스턴스 변수를 추가할 때 이 장점을 알 수 있음
4.속성을 추가해도 수정이 필요 없음
- 성과 이름 사이에 미들네임이 있는 경우
- 이 미들네임을 표현하기 위해 FullName 클래스에 속성을 추가해야 하는 상황이라면?
- 값 객체에 비교를 위한 메서드가 없어서 코드에서 속성을 직접 꺼내 비교해야 할 경우, 새로 추가된 속성이 생겼을때 비교하는 코드를 모두 수정해야함
4.1 속성을 직접 비교할 경우 새로운 속성 추가하기
var compareResult = nameA.FirstName == nameB.FirstName && nameA.LastName == nameB.LastName && nameA==MiddleName == nameB.MiddleName;
// 위에 처럼 조건식이 추가됨
- 한 곳의 경우 그냥 고칠 수 있지만 여러 곳인 경우는 수정이 어렵다
- 그래서 값 객체에서 직접 비교 수단을 제공하면 단순하고 지루한 작업을 피할 수 있음
- Before
class FullName : IEquatable<FullName>
{
...
public bool Equals(FullName other)
{
if(ReferenceEquals(null, other)) return false;
if(ReferenceEquals(this, other)) return true;
return string.Equals(FirstName, other.FirstName)
&& string.Equals(LastName, other.LastName);
}
...
}
- After
class FullName : IEquatable<FullName>
{
...
public bool Equals(FullName other)
{
if(ReferenceEquals(null, other)) return false;
if(ReferenceEquals(this, other)) return true;
return string.Equals(FirstName, other.FirstName)
&& string.Equals(LastName, other.LastName)
&& string.Equals(MiddleName, other.MiddleName);// 이곳의 조건식만 추가하면 됨
}
...
}
- 즉, 다른 새로운 속성이 추가되더라도
- 수정할 곳은 Equals메서드 내부로 제한됨
- 비교뿐만 아니라 값의 속성을 다루는 처리 역시 값객체에서 제공하게 하면 수정할 곳을 줄일 수 있음
5. 값 객체가 되기 위한 기준
- FullName 클래스를 구성하는 firstName이나 lastName등의 속성은 값 객체가 아니라 원시타입인 문자열로 정의돼 있음
- 가능한 모든 속성을 값 객체로 만든 FullName클래스
class FullName : IEquatable<FullName>
{
private readonly FirstName firstName;
private readonly LastName lastName;
public FullName(FirstName firstName, LastName lastName){
this.firstName = firstName;
this.lastName = lastName;
}
//(생략)
}
- 생성자 메서드에 전달되는 인자는 값 객체
- 이름을 나타내는 값 객체
-
class FullName : IEquatable<FullName> { ... public bool Equals(FullName other) { if(ReferenceEquals(null, other)) return false; if(ReferenceEquals(this, other)) return true; return string.Equals(FirstName, other.FirstName) && string.Equals(LastName, other.LastName); } ... }
- 성을 나타내는 값 객체
-
class LastName { private readonly string value; public LastName(string value) { if(stirng.IsNullOrEmpty(value)) throw new ArgumentException("최소 1글자 이상 이어야함", nameof(value)); this.value = value; } }
- 이책에서 개인적인 기준
- 도메인 모델로 선정되지 못한 개념을 값 객체로 정의해야 할 지에 대한 기준으로
- 규칙이 존재하는가?
- 낱개로 다루어야 하는가?
- 두개를 중점으로 생각
- 성명을 예로 들면
- 규칙
- 성과 이름으로 구성됨
- 낱개
- 앞서 본문에서 봤듯이 낱개로 다뤄지는 정보
- 즉, 위의 상황으로 봐서 성명은 값 객체로 정의 해야 할 개념이 됨
- 규칙
- 성 혹은 이름
- 현재라면 값 객체로 만들지 않을것
- 단, 전제를 바꿔 성과 이름에서 사용가능한 문자에 제약이 있으면?
- 결론적으로 값 객체로 정의하지 않고도 규칙을 제 할 수 있음
class FullName : IEquatable<FullName> { private readonly FirstName firstName; private readonly LastName lastName; public FullName(string firstName, string lastName) { if(firstName == null) throw new ArgumentNullException(nameof(firstName)); if(lastName == null) throw new ArgumentNullException(nameof(lastName)); if(!ValidateName(firstName)) throw new ArgumentException("허가되지 않은 문자가 사용됨", nameof(firstName)); if(!ValidateName(lastName)) throw new ArgumentException("허가되지 않은 문자가 사용됨", nameof(lastName)); this.firstName = firstName; this.lastName = lastName; } private bool ValidateName(string value) { //사용가능한 문자를 알파벳으로 제한 return Regex.IsMatch(value, @"^[a-ZA-Z]+$"); } //(생략) }
- 위에 처럼FullName 클래스속성이 원시타입이어도 인자를 전달받은 시점에 검사를 하면 규칙을 강제할 수 있음
- 값객체로 정의해도 문제는 없음
- 만약 값객체로 만들기로 했다면, 성과 이름을 별도의 타입으로 나눌지 말지 선택
- 따로 다룰 필요가 없는 경우 하나의 타입으로 다룰 수 있음
- 이름을 나타내는 클래스
-
class Name { private readonly string value; public Name(string value) { if(value == null) throw new ArgumentNullException(nameof(value)); if(!Regex.IsMatch(value, @"^[a-zA-Z]+$")) throw new ArgumentException("허가 되지 않은 문자가 사용됨", nameof(value)); this.value = value; } }
- Name클래스를 이용해 구현한 FullName 클래스
-
class FullName { private readonly Name firstName; private readonly Name lastName; public FullName(Name firstName, Name lastName) { if(firstName == null) throw new ArgumentNullException(nameof(firstName)); if(lastName == null) throw new ArgumentNullException(nameof(lastName)); this.firstName = firstName; this.lastName = lastName; //(생략) } }
- 도메인 모델로 선정되지 못한 개념을 값 객체로 정의해야 할 지에 대한 기준으로
6. 행동이 정의된 값 객체
- 값 객체에서 중요한 점 중 하나는 독자적인 행위를 정의할 수 있다는 점
- 돈에는 액수와 화폐 단위 2가지 속성
- 값 객체는 데이터만을 저장하는 컨테이너가 아니라 행동을 가질 수도 있는 객체
-
class Money { private readonly decimal amount; private readonly string currency; this.amount = amount; this.currency = currency; }
6.1 금액을 더하는 처리를 구현하기
class Money
{
private readonly decimal amount;
private readonly string currency;
//(생략)
public Money Add(Money arg){
if(arg == null) throw new ArgumentNullException(nameof(arg));
if(current != arg.currency) throw new ArgumentException($"화폐 단위가 다름(this:{currency}, arg:{arg.currency})");
return new Money(amount + arg.amount, currency);
}
}
- 돈을 더하려면 화폐 단위가 일치해야함
- 그러므로 화폐 단위가 같은지 확인 해야함
- 또 값 객체는 불편이므로 계산된 결과는 새로운 인스턴스로 반환
6.2 계산 결과 받기
var myMoney = new Money(1000, "KRW");
var allowance = new Money(3000, "KRW");
var result = myMoney.Add(allowance);
6.2.1 원시 타입끼리의 덧셈
var myMoney = 1000m;
var allowance = 3000m;
var result = myMoney + allowance;
6.2.2 화폐 단위가 일치하지 않으면 예외를 발생
var krw = new Money(1000, "KRW");
var usd = new Money(10, "USD":);
var result = jpy.Add(usd);// 예외 발생
- 버그는 착각에서 발생
- 값 객체는 결코 데이터를 담는 것만이 목적인 구조체가 아님
- 값 객체는 데이터와 더불어 그 데이터에 대한 행동을 한곳에 모아둠으로써 자신만의 규칙을 갖는 도메인 객체가 됨
6.2.3 정의되지 않았기 때문에 알 수 있는 것
- 즉, 객체에 정의된 행위를 통해 이 객체가 어떤 일을 할 수 있는지 알 수 있음
- 반대로, 객체는 자신에게 정의 되지 않은 행위는 할 수 없다는 말도 됨
- 돈을 나타내는 값 객체끼리 덧셈을 할 수 있지만, 곱셈은 불가능
- 돈에 곱셈이 필요한 경우라면 금리를 계산할 때 정도?
- 금리를 계산하기 위한 시그니처라면 아래와 같은 코드를 생각해 볼 수 있음
class Money
{
//(생각)
public Money Multiply(Rate rate);
// public Money Multiply(Money money)은 정의하지 않음
}
- 돈 객체끼리의 곱셈은 정의되지 않았으므로 묵시적으로 곱셈이 가능하지 않음을 알 수 있음
- 의미랑 존재가 명확하다는 뜻
7.값 객체를 도입했을 때의 장점
- 당연하지만 시스템 고유의 값을 객체로 나타내면 그만큼 정의하는 클래스의 수도 늘어남
- 값 객체를 도입하려면 여기에 따르는 심리적 장애물을 넘어야함
7.1 값 객체의 장점
- 표현력이 증가
- 무결성이 유지
- 잘못된 대입을 방지
- 로직이 코드 이곳저곳에 흩어지는 것을 방지
- 위 네가지는 모두 간단하지만 시스템을 보호하는 데느 크게 도움이 됨
7.2 표현력의 증가
- 식별을 위한 다양한 번호인 로트번호나 일련번호, 제품번호 등 숫자만으로 구성되기도 하고, 알파벳이 섞인 문자열 형태도 있음
- 제품 번호를 원시 타입으로 나타낸 프로그램은 어떨까?
var modelNumber = "a20421-100-1";
- 위의 modelNumber는 원시타입인 문자열 타입의 변수임
- 근데 그냥 보게 되면 제품번호의 내용이 어떤 것인지 예측이 어려움
void Method(string modelNumber)
{
...
}
- 위에 처럼 타입이 문자열이라는 것만 알 수 있음
- 제품번호의 내용을 알려면 modelNumber변수가 어디서 만들어져 어떻게 전달됐는지 따라가야함
7.2.1 제품번호를 나타내는 값 객체
class ModelNumber
{
private readonly string productCode;
private readonly string branch;
private readonly string lot;
public ModelNumber(string productCode, string branch, string lot)
{
if(productCode == null) throw new ArgumentNullException(nameof(productCode));
if(branch == null) throw new ArgumentNullException(nameof(branch));
if(lot == null) throw new ArgumentNullException(nameof(lot));
this.productCode = productCode;
this.branch = branch;
this.lot = lot;
}
public override string ToString()
{
return productCode + "-" + branch + "-" + lot;
}
}
- 위와 같이 클래스를 정의하면 제품 번호가 제품코드, 지점번호, 로트번호로 구성됨 알 수 있음
- 그냥 문자열만 제공한것 보다는 큰 진보임
- 값 객체는 자기 정의를 통해 자신이 무엇인지에 대한 정보를 제공하는 자기 문서화를 도움
7.3 무결성의 유지
- 시스템에는 각 값이 준수해야 할 규칙이 있음
- 사용자명을 예로 들어보자.
- 간단히 말하면 문자열임
- 그러나, 시스템에 따라서 N글자 이상 M글자 이하와 같은 제한이나 알파벳 문자만 포함할 것 같은 규칙이 있을 수 있음
7.3.1 유효하지 않은 값
- 효과가 늦게 나타나는 독과 같이 다루기 까다로운 상태
- 유효하지 않은 값을 사용할 때 항상 값이 유효한지 확인을 거쳐야함
if(userName.Length >=3)
{
// 유효한 값이므로 처리를 계속
}
else
{
throw new Exception("유효하지 않은 값");
}
- 값의 유효성을 매번 확인하면 급한 불은 끌 수 있지만 유효성 검사 코드가 반복됨
- 값 객체를 잘 이용하며 유효하지 않은 값을 처음부터 방지 할 수 있음
7.3.2 사용자명을 나타내는 값 객체
class UserName
{
private readonly string value;
public UserName(string userName)
{
if (value == null) throw new ArgumentNullException(nameof(value));
if (value.Length < 3) throw new ArgumentNullException("사용자명은 3글자 이상이어야함");
this.value = value;
}
}
- 위와 같은 방법으로 시스템상 유효하지 않은 값은 이런 방식의 확인을 거쳐 허용하지 않는 것
- 결과적으로 규칙을 위반하는 유효하지 않은 값을 걱정할 필요가 없음
8. 잘못된 대입 방지하기
User CreateUser(string name)
{
var user = new User();
user.Id = name;
return user;
}
- 위의 경우 실행에는 문제가 없겠지만, 올바른 코드라고 할 수 없음
- 사용자명이 그대로 ID인 경우는 올바른 코드라고 볼 수 있음
- 하지만 , 이메일 주소처럼 별도의 값을 ID로 삼는 경우는 바르지 못한 코드가됨
- 코드가 올바른지 아닌지를 판단하기 위해 관계자의 기억이나 문서를 뒤지는 것보다는
- 자기 문서화의 힘을 빌리는 것이 바람직
- 값객체는 이를 실현할 수 있음
8.1 값 객체로 코드 판별
- 사용자 ID를 나타내는 값 객체
class UserId
{
private readonly string value;
public UserId(string value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
this.value = value;
}
}
- 사용자명을 나타내는 값 객체
class UserName
{
private readonly string value;
public UserName(string value)
{
if(value == null) throw new ArgumentNullException(nameof(value));
this.value = value;
}
}
- 현재 위의 두 클래스는 각각의 원시 타입인 문자열을 래핑한 단순한 객체
8.1.1 값객체로 수정
class User
{
public UserId Id {get; set;}
public UserName Name {get; set;}
}
- 위와 같이 값객체를 사용하도록 User클래스 수정하고
- 함수의 인자로 문자열이 아닌 UserName객체를 전달받게하자
User CreateUser(UserName name)
{
var user = new User();
user.Id = name; // 컴파일 에러
return user;
}
- User 클래스의 Id 속성은 UserId타입의 객체
- 여기에 대입을 시도한 name은 UserName타입의 변수
- 컴파일러가 타입 불일치를 발견하고 에러 발생함
- 즉, 위와 같이 값 객체를 정의해 타입 시스템에 의존하면 예측하기 어려운 에러가 숨은 곳을 줄일 수 있음
- 정적 타이핑 프로그래밍 언어에 사용하면 좋음
- 타입 헌팅 기능을 통해 IDE가 에러를 잡을 수 있음
9. 로직을 한곳에 모아두기
- DRY원칙처럼 코드 중복을 방지하는 일이 매우 중요
- 중복 코드 많아지면 코드를 수정하는 것이 어려움
- 단순하다면 상관 없지만 이외 사용자 정보를 수정하는 처리가 있다면?
- 사용자며의 최소 길이가 변경이 되는 상황
- 우선 사용자를 신규 생성하는 리스트 부분이 있는 곳을 찾아서 고치게된다. 그것이 2개라면 2개를 찾아가서 고치게되는 경우가 된다.
- 사용자며의 최소 길이가 변경이 되는 상황
-
void UpdateUser(string id, string name) { if(name == null) throw new ArgumentNullException(nameof(name)); if(name.Length < 3) throw new ArgumentException("사용자명은 3글자 이상이어야 함", nameof(name)); //생략 }
9.1 값 객체를 적용하여 한곳만 수정
class UserName
{
private readonly string value;
public UserName (string value)
{
if(value == null) throw new ArgumentNullException(nameof(value));
if(name.Length < 3)throw new ArgumentNullException("사용자명은 3글자 이상이어야 함", nameof(name));
this.value = value;
}
}
- 위를 이용해서 개선해보면
void Create(stirng name)
{
var userName = new UserName(name);
var user = new User(userName);
//생략
}
void UpdateUser(string id, string name)
{
var userName = new UserName(name);
//생략
}
- 저렇게 따로 값객체를 만들어서 코드의 중복을 줄일 수 있고, 수정시 용이하다.
10.정리
- 값객체 개념은
- 시스템 고유의 값을 만드는 단순한 것
- 원시타입으로 만들수 있지만 지나치게 범용적이기 때문에 아무래도 표현력이 부족
- 도메인에는 다양한 규칙이 포함
- 값객체를 정의하면 이러한 규칙을 값 객체 안에 기술해 코드 자체가 문서의 역할함
- 값객체는 도메인 지식을 코드로 녹여내는 도메인 주도 설계의 기본 패턴
- 도메인 개념을 정의할 때는 우선 값 객체에 개념인지 검토해 보기
728x90
반응형
'DDD' 카테고리의 다른 글
22.02.01_[05DDD].데이터와관계된처리를분리하자-리포지토리 (0) | 2022.02.01 |
---|---|
22.01.29_[04DDD]부자연스러움을해결하는도메인서비스 (0) | 2022.01.29 |
22.01.18_[03DDD]생애주기를갖는객체-엔티티 (0) | 2022.01.18 |
댓글