본 글은 ‘Pepper’라는 고양이의 예시를 통해 이러한 문제를 설명합니다. 초기에는 고양이의 모든 행동(놀기, 먹기, 자기)이 Cat
클래스 내에 통합되어 있지만, 고양이가 잠든 상태에서는 먹을 수 없다는 제약이 추가되면서 행동이 상태에 의존하게 됩니다. 기존의 모델을 유지하기 위해 Ruby의 State Machine과 같은 추상화를 사용하는 방법이 제시되지만, 이는 코드를 복잡하게 만들고 DSL(Domain Specific Language) 사용에 대한 의문을 제기합니다.
저자는 비즈니스 시스템에서 ‘식별자’는 시간의 흐름에 따른 변화를 추적하는 데 중요하며, 행동은 이러한 식별자가 특정 시점이나 상태에서 수행할 수 있는 작업이라고 강조합니다. 예를 들어, 전자상거래 시스템에서 ‘주문-123’의 식별자는 변하지 않지만, ‘장바구니’ 상태에서는 배송될 수 없고 ‘주문 완료’ 상태에서는 다시 주문될 수 없는 것처럼 행동은 상태에 따라 변화합니다. 이러한 식별자와 행동 간의 본질적인 불일치는 도메인을 구조 대신 시간의 관점에서 생각할 때 더욱 명확해집니다.
이러한 관점에서, 저자는 식별자와 행동을 별개의 개념으로 모델링하는 대안을 제시합니다. AwakeCat
과 AsleepCat
과 같이 상태에 따라 다른 클래스를 사용하여 행동을 캡슐화하는 방식입니다. 이는 단순한 예시지만, 식별자와 행동이 동일하다는 가정을 버리면 현실과 코드 간의 매핑에 대한 새로운 이해를 얻을 수 있음을 시사합니다.
또한, Event Sourcing 패러다임에서는 ‘스트림’이라는 식별자만이 존재하고, 모든 상태는 해당 식별자에 대한 이벤트 로그로부터 파생된다는 점에서 이러한 분리가 극대화됩니다. 함수형 프로그래밍(FP)에서도 상태와 행동이 명확하게 구분되어 있어 이러한 마찰이 발생하지 않습니다.
저자는 OOP 개발자들이 이러한 방식으로 생각하는 경향이 있는 이유를 ‘실제 세계와의 유추’라는 기본적인 가정과 ORM(Object-Relational Mapping) 패턴에 대한 의존성에서 찾습니다. ORM은 데이터 지속성과 행동을 결합하여 데이터베이스 레코드, ORM 클래스, 그리고 그 클래스가 구현하는 행동을 모두 동일한 것으로 여기게 만듭니다. 저자는 이러한 도구들이 유용함에도 불구하고, 문제를 코드로 변환할 때 내재된 큰 가정들을 간파할 필요가 있다고 결론짓습니다.