- 출처: 도메인 주도 설계 - 에릭 에반스
- 불명확한 개념의 모델링
객체지향 패러다임을 적용하는 경우 어떠한 객체를 찾는데 집중하게 된다. 객체지향 관련 책을 보다보면 명사와 동사에 기반하여 객체를 찾으라고 하지만 "발생" 처럼 명사나 동사로 표현하기 힘든 모델도 존재하며 객체 모델로서 중요한 요소가 될 수 있다.
- 명시적인 제약조건
제약조건은 중요한 범주의 모델 개념을 형성한다. 보통 이런 개념은 암시적으로 존재하지만 이를 명시적으로 표현해주면 설계를 개선할 수 있다.
간단한 제약조건의 경우 객체의 메서드에 포함되는것이 자연스러운 경우가 있다. 어떤 "Bucket" 객체에 내용물을 저장할 대 제한된 용량을 초과할 수 없다는 불변식을 만족한다고 가정해보자. 이를 아래와 같이 코드로 표현할 수 있다.
type Bucket struct {
capacity int
contents int
}
func (c *Bucket) pourIn(volume int) error {
if c.contents+volume > c.capacity {
return fmt.Errorf("content must not exceed capacity")
}
c.contents += volume
return nil
}
이렇게 간단한 제약조건의 경우 특정 연산안에서 조건문을 사용해서 표현할 수 있다.
하지만 제약조건이 언제나 이렇게 간단한것은 아니다. 제약조건이 복잡해지면 객체의 연산이 본질적으로 무엇을 하고자 하는지 한눈에 들어오지 않게 된다. 이런 경우 제약조건을 위한 별도의 메서드로 분리할 수 있다.
type Bucket struct {
capacity int
contents int
}
func (b *Bucket) pourIn(volume int) error {
if err := b.constrainedToCapacity(volume); err != nil {
return fmt.Errorf("constrained to capacity: %w", err)
}
b.contents += volume
return nil
}
func (b *Bucket) constrainedToCapacity(volume int) error {
if b.contents+volume > b.capacity {
return fmt.Errorf("content must not exceed capacity")
}
return nil
}
이렇게 메서드로 분리해줘도 더 명시적으로 표현해주어야 하는 경우도 존재한다. 객체가 수행하려고 했던 책임에 필요한 데이터 뿐만 아니라 제약조건을 위해 의도하지 않았던 데이터도 가지는 경우가 있기 때문이다. 만약 제약조건을 포함한 객체의 설계가 어딘가 잘못되었다면 아래와 같은 신호를 보내올것이다.
- 제약조건을 평가할 때 객체 정의에 적합하지 않은 데이터가 필요하다.
- 관련 규칙이 여러 객체에서 나타난다.
- 설계/요구사항은 제약조건에 초점이 맞추어지지만 구현에서는 절차코드에 묻힌다.
- 도메인 객체로서의 프로세스
여기서 얘기하는 프로세스란 도메인의 프로세스를 의미한다. 프로세스를 수행하는 방법이 1가지 이상이라면 알고리즘 자체나 일부를 하나의 객체로 만들 수 있다. 우리가 흔히 사용하는 전략패턴에서 각 객체는 각기 다른 전략을 표현한다.
그렇다면 모든 프로세스를 명시적으로 하면 좋은가? 그렇다면 모두 전략패턴을 적용하여 객체화 해야 하는가? 답은 이 부분을 언급하면서 얘기했던 첫번째 문장에 있다. 여기서 명시적으로 표현해야하는 프로세스란 도메인 전문가가 말하는 프로세스이다. 만약 컴퓨터 프로그램의 매커니즘이라면 이는 오히려 숨겨야할 프로세스에 해당한다.
'Concepts > SW Design' 카테고리의 다른 글
DDD - Inteface (0) | 2022.10.01 |
---|---|
DDD - Specification (0) | 2022.09.25 |
DDD - Repository (0) | 2022.09.18 |
DDD - Factory (0) | 2022.09.12 |
DDD - 도메인 객체의 lifecycle과 Aggregate (0) | 2022.09.10 |
댓글