Domain Model에 대해서
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은 내 API 요청/응답으로부터 독립적이어야 한다. (Seperated Presentation)
- Domain Model(의 설계)은 data source로 부터 독립적이어야 한다. (Persistence Ignorance)
- 외부 데이터를 어디서 가져오는지(DB, external api, …)
- 어떤 라이브러리를 써서 가져오는지 (MyBatis, JPA, …)
- 어떤 형태로 가져오는지(json, xml, …)
- 어떤 필드를 가져오는지 Domain Model은 가능한 POJO로 유지한다.
- 하지만 절대적인 원칙은 아니다. (SW 설계에서의 모든 원칙이 그렇다. Good design is all about trade-offs.) 우리의 궁극적인 목표는 유지보수가 쉬운 시스템을 만드는 것 이다.
- 엄격하게 POJO로 유지하기 보다는, Domain Model의 개념을 해치지 않도록 설계된 annotation에 한해서는 도메인 모델에 바로 붙이는 것이 더 자연스럽고 실용적이다. (e.g., ORM annotation / 이 부분에 대해서는 찬반이 갈린다)
- ORM @Entity를 Domain layer의 Entity로 썼을 때 단점
위와 같은 원칙은 도메인 모델의 책임이 아닌, 다른 요인이 도메인 모델의 설계에 영향을 주는 것을 방지하기 위해 필요하다.
- 보통은, Domain Model이 주가 되는 Business layer와 기타 layer를 분리하며, Domain Model과 DTO를 분리한다.
- e.g. front-end가 요구하는 데이터 그대로 비즈니스 레이어에서 Domain Model을 만들면 안된다.
- 이렇게 만든 Domain Model은 front-end에 크게 의존적이다.
- 뷰가 변경되어 해당 뷰에서 가져와야 하는 속성이 하나 늘어나면, 그를 담기 위해 Domain Model을 수정해야 한다.
- 해당 뷰만을 위한 모델이기 때문에 재사용성이 떨어지며, 재사용성이 떨어지니 비슷한 모델을 또 만드는 결과로 이어진다.
- e.g., 뷰의 datatable에서 발주번호, 제품이름, 입고날짜를 요구해서 이를 묶은 Domain Model을 만들었는데, 뷰가 변경되어 업체이름 이라는 속성을 추가해야 한다면?
- 이렇게 만든 Domain Model은 front-end에 크게 의존적이다.
- 즉, 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 까지 가지고 있는 방식에 대응된다.
- 반대어는 Rich Domain Model
참고
- https://martinfowler.com/eaaCatalog/domainModel.html
- https://martinfowler.com/bliki/ValueObject.html
- https://stackoverflow.com/questions/63393388/annotations-in-domain-objects-with-jpa-violates-database-is-a-detail
- https://stackoverflow.com/questions/10099636/are-persistence-annotations-in-domain-objects-a-bad-practice
- DDD - aggregate에 대해서
This post is licensed under CC BY 4.0 by the author.