Domain Model이란 해당 도메인에서 비즈니스적인 의미를 가지는 object 다.

 

An object model of the domain that incorporates both behavior and data. - P of EAA

 

Domain Model은 id 여부에 따라 두 가지로 구분할 수 있다.

Entity

  • id가 있어 각각의 개체를 고유하게 식별 할 수 있는 경우
  • 엄밀히 불변은 아니고 시간이 지나면서 상태가 변경될 수 있는 대상임. (그러나 이와 별개로 앱단에서는 불변 객체로 처리하는 것이 좋다. 함수형.)
  • e.g., Member

 

VO ( value object )

  • id가 없음
  • To avoid aliasing bugs I follow a simple but important rule: value objects should be immutable.
  • 필드 값 상태가 같다면 같은 객체로 처리해도 되는 경우
    • 그래서 이름이 'value' object임. (반대 되는 개념 : reference object)
    • 참조가 아니라 값으로 동등함을 비교하는게 더 자연스러운 대상들.
    • 따라서 equals, hashCode 구현 필수
  • e.g., Money
  • 특별히 비즈니스 적인 의미를 가지지 않아도, 데이터 뭉치를 하나로 묶어주는 클래스도 VO로 본다 - 리팩터링 6.8절
    • 지금은 아니더라도, 이렇게 만들어진 VO는 시간이 지나면서 로직과 책임을 가지게 되어 진정한 Domain Model이 되는 경우가 많다.
    • larger structures can often be programmed as value objects if they don't have any conceptual identity or don't need share references around a program.

 

 

Domain Model은 어떻게 설계해야 하는가

  • Domain Model은 책임으로 설계한다. [책임 ➔ 객체 ➔ 메시지 ➔ 메서드 ➔ 상태]
  • Domain Model은 가능한 POJO로 유지한다.
    • 하지만 절대적인 원칙은 아니다. (SW 설계에서의 모든 원칙이 그렇다. Good design is all about trade-offs.) 우리의 궁극적인 목표는 유지보수가 쉬운 시스템을 만드는 것이다.
    • 엄격하게 POJO로 유지하기 보다는, Domain Model의 개념을 해치지 않도록 설계된 annotation에 한해서는 도메인 모델에 바로 붙이는 것이 더 자연스럽고 실용적이다. (e.g., ORM annotation / 이 부분에 대해서는 찬반이 갈린다)
    • Repository와 DataMapper의 책임 (w/o ORM) - '참고) JPA를 쓴다면?' 부분 참고
  • Domain Model은 내 API 요청/응답으로부터 독립적이어야 한다. (seperated presentation)
  • Domain Model(의 설계)은 data source로 부터 독립적이어야 한다.
    • 외부 데이터를 어디서 가져오는지(DB, external api, ...)
    • 어떤 라이브러리를 써서 가져오는지 (MyBatis, JPA, ...)
    • 어떤 형태로 가져오는지(json, xml, ...)
    • 어떤 필드를 가져오는지

 

위와 같은 원칙은 도메인 모델의 책임 이외의 요인이 도메인 모델의 설계에 영향을 주지 않도록 하기 위해 필요하다.

  • 보통은, Domain Model이 주가 되는 Business layer와 기타 layer를 분리하며, Domain Model과 DTO를 분리한다.
  • 간단히 예를 들면, front-end가 요구하는 데이터 그대로 비즈니스 레이어에서 Domain Model을 만들면 안된다. 
    • 이렇게 만든 Domain Model은 front-end에 크게 의존적이다.
      • 뷰가 변경되어 해당 뷰에서 가져와야 하는 속성이 하나 늘어나면, 그를 담기 위해 Domain Model을 수정해야 한다.
      • 해당 뷰만을 위한 모델이기 때문에 재사용성이 떨어지며, 재사용성이 떨어지니 비슷한 모델을 또 만드는 결과로 이어진다.
    • e.g., 뷰의 datatable에서 발주번호, 제품이름, 입고날짜를 요구해서 이를 묶은 Domain Model을 만들었는데, 뷰가 변경되어 업체이름 이라는 속성을 추가해야 한다면?
  • 즉, layer를 나눠 놓은 의미가 없어진다.
  • 우연히 front-end에서 요구하는 데이터 집합과 Domain Model의 모양이 똑같을 수는 있겠지만, 책임에 따라 설계한 Domain Model과 표현에서 요구하는 데이터는 언제나 불일치가 발생 할 수 있다.

 

 

 

 

 

안티패턴 - AnemicDomainModel

  • https://martinfowler.com/bliki/AnemicDomainModel.html  
  • Domain Model 클래스에 field, getter, setter만 두고 단순히 DTO 처럼 사용하며 로직은 다 Service에 넣어버리는 것은 안티패턴이라는 의견. (생각보다 흔히 보인다)
  • 나도 그렇게 생각하는 것이, 객체는 data(state) 및 그와 연관된 logic을 가지고 있는, 단일 책임을 지니고 있는 프로그래밍의 기본 단위다. 어떤 actor를 제대로 표현하려면 state + logic이어야 한다.
  • anemic domain model 구조에서는 data와 그를 다루는 logic이 서로 다른 객체에 분리되어 있는데, logic이 없는 Domain Model은 그 책임을 다 한다고 말할 수 없으며 이는 프로그램의 응집도 감소로 이어진다.
  • 이 안티패턴에 빠지지 않기 위해서는, 단순 요청/응답/전달을 위한 DTO와 비즈니스 로직에서 사용하는 actor 그 자체인 Model 클래스를 다르게 바라보아야 한다.
    • e.g., Model : User / DTO : UserRequest & UserResponse
  • 관련 문서 https://martinfowler.com/bliki/TellDontAsk.html  
    • ask는 Domain Model은 field만 가지고 있고 Service에 logic이 위치한 방식.
    • tell은 Domain Model이 logic 까지 가지고 있는 방식에 대응된다.

 

참고