도메인 모델이란?
그레디 부치는 모델을 단순하게 표현한 실제 세계라고 표현한다.
소프트웨어에서 모델은 해결해야 하는 문제를 바라보는 다양한 관심사 중 하나를 선택해서 문제를 설명하는데 필요한 것만을 표현하는 것을 말한다.
이는 관심사의 분리라는 개념과도 연관이 되는데, 관심사의 분리는 구성 요소 관 관계를 정의하는 것으로 이를 통해, 높은 응집도와 구성 요소간 낮은 결합도를 만들어 유지보수에 도움이 된다.
1.1 도메인 로직 패턴
도메인 로직을 어디에 두는 것이 좋을까란 질문에서 마틴 파울러는 애플리케이션 아키텍쳐 패턴에서 도메인 로직을 구현하는 방법을 네가지로 정리했다.
1.1.1 트랜잭션 스크립트 패턴
트랜잭션 스크립트는 클라이언트가 요청한 비즈니스 로직을 하나의 프로시저가 모두 처리한다. 이는 반복적인 CRUD를 처리할 수 있는 경우에 적합하다. 이를 이용한 기술은 서블릿 또는 JSP가 있다.
1.1.2 테이블 모듈 패턴
테이블 모듈은 데이터 베이스 테이블 단위로 비즈니스 로직을 처리하는 클래스를 분리한다. JDBC가 제공하는 ResultSet을 주로 사용한다. 데이터 베이스 테이블에 대응하는 클래스를 테이블 모듈로 선언하고 대상 테이블의 데이터를 조회하거나 변경하는 프로시저를 포함한다.
1.1.3 서비스 레이어 패턴
서비스 레이어에 시스템 통합과 전체 흐름을 조정하는 책임을 부여한다. 현대 소프트웨어에서 가장 많이 사용하는 패턴으로 서비스 레이어는 비즈니스 로직뿐만 아니라, 로깅, 권한 체크와 같은 공통 기능을 구현하기에 가장 적합한 후보다.
서비스 경계를 사용하는 클라이언트와 통합을 위해 추가한 서비스 발행 레이어를 보여준다.
서비스 발행 레이어(API 레이어를 뜻하는 것 같다.) 는 서비스 레이어를 변경하지 않으면서 HTTP뿐만 아니라 Socket, gRPC 등 다양한 프로토콜을 요구하는 클라이언트를 지원하게 확장할 수 있다.
1.1.4 도메인 모델 패턴
도메인 로직을 도메인 객체에 적절하게 구현하면, 다양한 객체 지향 설계 원칙을 준수하면서 변경 요청에 유연하게 대응할 수 있다.
exception을 어떻게 정의하는 것이 좋을까?
1. 시스템명 + Exception : 상황에 맞는 메세지를 생성자 파라미터로 전달
2. 구체적인 예외클래스 선언 : 시스템을 이해 및 분석하는데 수월 (ex. ItemLimitExceedException)
1.2 헥사고날 아키텍쳐
외부 요청을 수신하여, 비즈니스 로직을 시작시키는 인바운드 어댑터와 비즈니스 로직을 실행하면서 영구저장소에 데이터를 저장하거나 다른 시스템과 협력하는 아웃 바운드 어댑터로 구분한다.
1.3 도메인 주도 설계
도메인 주도 설계는 관심사를 바운디드 컨텍스트로 한정짓고, 바운디드 컨텍스트 간 관계와 바운디드 컨텍스트 내에서 효과적인 모델링을 위한 실용적인 접근법이라 할 수 있다.
1.3.1 유비쿼터스 언어
개발 프로세스나 방법론에서 말하는 용어집과 유사하며, 비즈니스 전문가와의 대화에서 이 언어를 적극적으로 사용할 것을 강조한다.
유비쿼터스 언어는 사용하는 컨텍스트에 따라 달리질 수 잇어, 사용할 수 있는 범위를 한정했을 때 비로소 '보편적'이라고 할 수 있다.
1.3.2 엔티티
엔티티는 테이블과 관계중심의 데이터 모델을 만들고, 테이블에 대응하는 클래스로 매핑하면 엔티티는 가장 기본적인 책임을 가진 인포메이션 홀더의 역할을 가져 getter와 setter 메소드만 제공한다.
엔티티에서 무엇보다 중요한 것은 식별성이다. 어떤 클래스의 인스턴스를 시스템의 다른 인스턴스와 구분해야 하면 엔티티로 설계해야 한다.
식별자는 데이터베이스 시퀀스나 UUID를 사용할 수 있습니다. 객체 지향에서는 OID(Object Identifier) 라고도 한다.
1.3.3 값 객체
식별자가 필요하지 않는 객체로, 다른 객체를 수식하기 위해 존재한다. 값 객체의 중요한 특징은 불이다. setter가 없어야 하고, 필요하다면 외부에서 호출할 수 없도록 private 로 선언해야 한다. 값 객체를 직접 접근해서, 속성을 변경하지 않고, 엔티티 객체를 통해서만 해야 한다.
값 객체는 원칙적으로 불변이지만, 특별한 상황에서는 setter를 이용한 변경을 허용한다.
- 값이 자주 변경돼 메모리를 효율적으로 사용하지 못할 때,
- 객체 생성이나 삭제에 많은 비용이 드는 경우,
- 교체로 인해 클러스터링이 제한되는 경우,
- 값을 공유할 일이 많지 않거나 클러스터링을 향상시키기 위해 또는 다른 기술적인 이유로 공유가 보류된 경우