쉽게 말하는, DDD - 값 객체

jay·2022년 5월 20일
5
post-thumbnail

오늘 말하는 도메인은 'URL'이 아닙니다.

아시겠죠?

도메인

도메인을 실생활에서 쓰는 용어로 바꾸어보면 area, 영역, 관심사로 나타낼 수 있습니다.

포크레인, 시공, 클라이언트는 건축 도메인에서 사용되는 말이며

GC, OS, 클라이언트는 소프트웨어 공학 도메인에서 사용되는 말입니다.

이 때 클라이언트는 여러 도메인에 걸쳐 비슷한 의미로 사용됨을 알 수 있습니다.

도메인 주도 설계

도메인 주도 설계(DDD)는 소프트웨어를 제작함에 있어 클라이언트가 어떤 도메인(환경)에서 소프트웨어를 만드는지 이용자 관점에서 먼저 이해한 후 이를 기반으로 객체를 설계하는 개발 방법을 의미합니다.

도메인 지식에 초점을 맞춘 설계 기법이라고 할 수 있습니다.
많은 객체지향 관련 서적에서 상속과 다형성을 자동차 라는 일상 도메인의 영역의 도메인 지식을 기반으로 설명하고는 합니다.
이는 책을 읽는 사람들의 문제가 익숙하지 않은 개념을 마주치는데 겪는 어려움이라는 것을 정확히 이해하고 이를 기반으로 사용자 도메인(환경)에서 문제를 해결하려고 하는 방법(비유법)을 택한 것입니다.

도메인 주도 설계가 왜 중요한가

앞서 말씀드린 상속과 다형성에 대한 예시와 같이 특정 문제에 있어 적절한 해결법을 찾는 것은 소프트웨어 개발자에게 가장 중요한 역량입니다.
즉, 개발자가 맞닥뜨리는 환경과 사건, 개념을 이해하고 해결에 유용한 지식을 뽑아내는 것이 매우 중요한 역량이라고 할 수 있습니다.

이는 비단 클라이언트를 이해시키는 언어적 구사 능력에만 특정되는 것이 아닌 소프트웨어 설계에 있어서 동일하게 적용시킬 수 있습니다.

특정 개념을 프로그래밍 언어로 표현할 때 우리는 자바의 경우 클래스, 자바스크립트의 경우 함수를 사용해 객체를 표현합니다.
그리고 가독성과 유지보수성을 높이기 위해 객체지향 설계 기법을 사용하고는 합니다.
협업을 위해 주석이나 자바스크립트의 경우 jsdoc을 사용하기도 합니다.

도메인 기반 지식이 잘 스며든 코드와 그렇지 않은 코드는 협업을 함에 있어 얼마나 적절하게 표현된 객체인지를 판가름하는 좋은 판단 기준이 됩니다.

단순 가독성만 보더라도 커밋을 남긴 사람에게 직접 물어보지 않아도 이해가 되는 클래스는 유지보수에 있어 큰 힘이 됩니다.

도메인 모델링은 왜 어려운가

모델링은 추상화 과정입니다. 이렇게 말하면 간단한 것 같지만
도메인에 맞게 객체를 모델링 하고 설계를 모델링 하는 것은 매우 까다로운 일이기도 합니다.
왜냐하면 코드를 직접 작성하기 앞서 주어진 문제에 대한 통찰과 정리가 먼저 되어야 하기 때문입니다. 또한 모델링에 쓰이는 벽돌(코드), 청사진(클래스)들은 언제든 변경되기 좋은 구조로 작성되어야 합니다.
이 뿐만 아니라 어떤 문제에 대한 도메인의 정의가 동료들과 다를 수도 있으므로 여러 분야의 사람들이 협력하여 모델링을 해야 합니다.

도메인 객체

도메인 객체는 도메인 모델링을 코드를 사용해 하나의 모듈로 만든 것을 의미합니다.

실상활에 사용되는 자동차는 바퀴와 부속 부품이 언제든지 변경될 수 있기 때문에, 그리고 자동차의 경우 단순 굴러가는 기능 뿐만 아니라 날씨에 따라 히터와 에어컨의 기능을 다르게 해줘야 하는 요구사항이 있기 때문에 도메인 객체도 이러한 요구사항에 따라 유연하게 변화를 나타내야 합니다.

오늘은 도메인 객체 중 값 객체에 대해 설명해보려고 합니다.

값 객체

자바스크립트에는 6까지 primary 타입이 있습니다. 그리고 우리는 이것을 불변 타입이라고 의미합니다.

갑자기 불변성에 대해 이야기를 하는 것이 당혹스러울 수 있지만,

값 객체의 가장 큰 특징은 바로 이 불변성에 있습니다.

근데 불변하다는 것은 정확히 무슨 뜻일까요?

불변성

let name = "Dante";
name = "Jay";

자바스크립트에서 string은 primary 타입이며 불변성을 띕니다. (제가 불변객체라고 부르지 않는 이유는 저는 primary 타입을 객체가 아니라고 보기 때문입니다.)
그런데 name이 Jay로 바뀌었네요?

여기서 name이 바뀌는 것은 불변성과 관련이 없습니다.

여기서 Dante이기 때문에 Dante는 여전히 Dante입니다.

안녕?

name은 Dante에서 Jay로 치환되었을 뿐입니다.

만약 Dante가 불변성이 아니어야 한다면 다음 처럼 Dante를 Dante가 아닌 꽃으로 불러야 합니다.



"Dante".changeValue("단테는 그저 단테였다. 내가 그를 꽃이라 불렀을때 그는 내게로 와 꽃이 되었다");

console.log("Dante") // "단테는 그저 단테였다. 내가 그를 꽃이라 불렀을때 그는 내게로 와 꽃이 되었다"

이는 굉장히 어색하고 이상한 상황입니다.
"Dante"는 항상 "Dante"이어야지 꽃이 되지는 않습니다.
불변성이 보장되지 않는다면 우리는 primary type 값들을 마음 편히 사용하지 못하게 되었을 수도 있습니다.

값 객체는 시스템의 일부분을 표현하는 객체입니다.

우리는 객체지향 프로그래밍에서 주로 클래스를 사용해 객체를 표현합니다.

class Computer {
  cpu;
  keyboard;
  constructor(cpu, keyboard){
    this.cpu = cpu;
    this.keyboard = keyboard 
  }

  fullParts = function(){
   	return {
      cpu: "m1",
      keyboard: "magic"
    }
  }
    
}

IT 도메인에서 컴퓨터를 표현하는 Computer 값 객체입니다.
즉, 원시타입이 아니어도 값 객체가 될 수 있으며

이 때, 자바스크립트 언어적 한계로 인해 Computer의 속성 값을 강제로 변경하지 않는 이상 Computer 객체는 setter 메서드를 따로 제공하여 내부 속성 값을 변경하게 허용하지 않고 있습니다.

앞서 말한 것과 같이 값 객체는 불변성을 띄어야 하기 때문에 private property descriptor를 사용하여 정의한다고 했을 때 (그래도 언어적 한계로 바꿀 수는 있지만)
setter 메서드가 주어지면 안됩니다.

불변성을 띄는 것과 치환이 되는 것은 전혀 다른 의미입니다.

아까 제 이름을 예시로 든 코드와 같이 name 변수는 readonly 타입이 아닌 이상 다른 값으로 치환될 수 있습니다.
name 이 "Dante"가 아니라 객체 Dante {} 로 표현되었다면 Flower {}로 치환되어도 전혀 문제가 되지 않는 것입니다.

따라서 어떤 변수가 값 객체를 참조하고 있을 때 다른 객체를 참조하게 하고 싶다면, 항상 기존 값 객체를 변경시키는 것이 아닌 치환을 통해 참조를 변경시켜야 합니다.

등가성

등가성은 두 객체를 비교할 수 있는 가에 대한 질문입니다.

자바스크립트 클래스 인스턴스의 경우 (실제로 인스턴스화 되지는 않지만)

인스턴스를 값 객체로 표현헀다면 이 때 비교는 reference 비교가 아닌 객체를 구성하는 속성을 통해 비교됩니다. (물론 instanceOf는 같아야 합니다)

모든 것을 값 객체로 만들어야 하는가

앞서 보았던 Computer 객체의 경우 속성 keyboard, cpu은 원시 타입인 string 타입이 될 수도 있지만 또 다른 객체 타입으로도 나타낼 수 있습니다.
표현의 용이를 위해 정적 타입을 사용하겠습니다.

class Computer {
  keyboard: Keyboard
  cpu: CPU
  
  constructor(keyboard: Keyboard, cpu: CPU) {
    ...
  }
  
  ...
}

원시 타입(원시 타입은 값 객체가 아닙니다.) 이든 값 객체든 모두 Computer의 속성이 될 수 있습니다.

이 때 Computer 도메인이 Apple 사의 컴퓨터로 국한되어 있다면 cpu 와 키보드의 종류 또한 제한 될 수 있기 때문에
이렇게 표현하려는 도메인과 상황에 따라 값 객체로 나타내는 것이 더 좋을 수도 있고, 그럴 필요가 없을 수도 있습니다.

행동를 할 수 있는가

값 객체의 중요한 특징 중 하나는 특정 역할을 수행할 수 있는 행동을 정의할 수 능력이 있는가 입니다.

class Keyboard {
 changeKeyCap(cap){
   ... 
 }
}

키보드 값 객체의 경우 키캡을 커스터마이징하는 역할을 수행해야 할 수 있기 때문에, 항상 이러한 역할을 수행하는 메서드를 가지고 있지는 않더라도 이렇게 기능을 추가할 수 있는 능력이 있어야 합니다.

값 객체가 왜 중요한가

앞서서 도메인 주도 개발이 중요한 이유에는 협업의 관점에서 가독성이 좋고 유지보수성을 좋게 한다는 장점을 이야기 했습니다.

앞서 클래스들을 보니까 장점이 느껴지지 않으신가요?

각 객체에 대한
표현성 증가, 불변성 보장, 역할 수행이라는 장점을 볼 수 있었습니다.

특히 역할 수행에는 유효성 검사와 같은 기능을 constructor 내부에 선언함으로써 잘못된 값 객체의 표현을 사전에 차단시켜줄 수도 있습니다.

협업하는 입장에서 도큐먼트나 맨투맨으로 누군가에게 물어보지 않더라도 해당 유효성 검사 로직만 보고서 이 객체가 무슨 시스템을 표현한 것인지 이해할 수 있을 것입니다.

profile
성장을 향한 작은 몸부림의 흔적들

0개의 댓글