본문 바로가기

Development/Spring

DDD에서 Controller, Service, Repository의 역할

Layered Architecture는 애플리케이션의 컴포넌트(component, 구성요소)들을 계층(layer) 별로 나눈 구조이다. 일반적으로 presentation layer(Controller 컴포넌트가 속함), business(service) layer(Service 컴포넌트가 속함), data access layer(Repository 컴포넌트가 속함)의 세 개의 계층으로 나뉜다. 설계에 따라 네 개의 계층으로 나뉘기도 하고, Service와 Repository 컴포넌트가 함께 domain layer에 속하기도 한다. 이렇게 계층별로 나뉘는 구조의 대표적인 특징은 다음과 같다.

 

  • 각 계층은 자신의 바로 하위 계층 의존성을 주입받는다.
  • 상위 계층에서 하위 계층으로만 접근이 가능하고, 역으로는 접근할 수 없다.
  • 각 계층은 각자의 관심사를 가지고 있고, 타 계층의 역할을 나누어 수행하는 등의 관심사 침범 할 수 없다.
  • 명확한 역할의 구분을 기반으로 가독성, 기능 구현, 코드 확장성에 유리하다.
  • 계층 간 의존성이 낮으므로, 단위 테스트가 용이하다.

 

 


 

 

Controller

 

 

Controller는 presentation layer에서 클라이언트하위 계층연결해 주는 역할을 한다. presentation layer는 비즈니스 로직을 포함하지 않고, UI와 API를 통해 클라이언트와 요청과 그에 대한 서버의 로직 처리 결과(응답)를 전송해 주는 역할을 담당한다. 일반적으로 모든 요청Controller를 통해 business layer로 전달된다. spring boot를 기준으로 통신 절차는 다음과 같다.

 

클라이언트가 값을 입력하면, http 프로토콜을 통해 servlet 컨테이너로 데이터가 전달된다. servlet 컨테이너는 servlet 객체(Spring에서 DispatcherServlet)의 생명주기를 관리하는 컨테이너다. servlet 객체handler mapping(요청 정보에 적합한 Controller를 매핑하기 위한 인터페이스)을 통해 요청된 URI(resource에 접근하기 위한 식별자)에 매핑Controller를 탐색한 뒤, handler adapter를 통해 Controller를 호출한다. Controller는 요청 수행에 적합한 method를 선택하여  business layer로 전달한 뒤, 처리 결과를 받아 다시 handler adapter응답한다. handler adapter는 이 응답을 view 이름과 함께 servlet 객체에 전달하고, 해당 view를 통해 시각화한 뒤 클라이언트에게 반환하는 방식이다.

 

절차가 다소 복잡하지만, 계층연결하고 클라이언트의 요청값과 business layer의 응답값전송하는 핵심 구간에 Controller가 위치해 있다는 사실을 알 수 있다. 통신 과정에서 저번 포스팅에서 다뤘던 데이터 전달용 객체인 DTO매개변수 이용한다.

 

view를 직접 구현하지 않는 경우, Controller는 DTO 객체를 JSON 등의 표현될 수 있는 형태로 변환한 뒤 API를 통해 클라이언트에게 반환한다.

 

 

Service

 

 

Service는 business layer에서 애플리케이션기능정의하고 대부분의 비즈니스 로직 구현한다. 이 외의 공통적으로 반복되는 서비스 로직AOP(Aspect Oriented Programming, 관점 지향 프로그래밍)에 따라 분리하고, spring framework에서 제공하는 advice 메소드를 통해 비즈니스 로직의 수행 전후에 처리한다.

 

도메인에 관계되는 일부 복잡한 로직은 DDD의 Rich Model 개념에 의거하여 Entity에서 구현할 수도 있다. 이는 Entity가 스스로의 상태를 관리하고 관련 비즈니스 로직을 직접 수행하도록 함으로써 명확한 도메인 로직유지보수용이성을 담보하기 위함이다.

 

Service는 Controller를 통해 전달받은 요청값에 필요한 로직을 수행한 뒤, 일반적으로 응답 데이터 DTO 변환하여 Controller 반환한다(다만 DTO를 어느 컴포넌트에서 변환할 지에 관한 사항은 프로젝트의 요구사항과 아키텍처에 따라 변동될 수 있으며, Controller에서 변환할 수도 있다). 해당 과정에서 데이터의 저장/변경/조회/삭제 등의 조작이 필요하다면 Repository를 통해 해당 조작을 수행한다.

 

VO불변성이 보장되어 생성 후 값을 변경할 수 없으며 스스로 비즈니스 로직포함할 수 있기 때문에, Entity를 보조하여 특정 도메인 개념을 보다 명확히 표현하는 데 유리한 특성을 가지고 있다. 그러나 VO는 불변성으로 인해 상태 변경의 반영이 어렵고, 대량데이터 처리 혹은 복잡한 데이터 구조를 다루는 데 있어 비효율적일 수 있다. 이러한 단점으로 인해 비즈니스 소프트웨어 개발 과정에서 자주 쓰이지는 않는 추세이다.

 

DTO와 VO 관련 내용은 이전 포스팅에서 다뤘다(https://hellmir.tistory.com/entry/DDDDomain-Driven-Design에서-Entity-DTO-VO의-비교).

 

 

DDD(Domain Driven Design)에서 Entity, DTO, VO 비교

Entity Entity는 주로 데이터베이스의 테이블과 매핑되는 객체다. 값이 쉽게 변경되면 객체의 일관성이 유지되지 않으며 다른 객체들에도 영향을 끼치게 되므로, Setter가 아닌 생성자를 사용하는 것

hellmir.tistory.com

 

 

Repository

 

 

DAO(Data Access Object)는 data access layer에서 데이터베이스접근하여 CRUD를 수행하는 역할을 담당하며, DDD에서는 보다 도메인 중심적인 접근을 할 수 있는 Repository가 DAO의 역할을 수행하게 된다. Repository는 단순한 CRUD 연산에 집중된 DAO의 기능을 확장하여 도메인 모델에 대한 직관적인 인터페이스를 제공한다.

 

DTO는 주로 Setter 메소드를 이용하여 값이 가변적이므로, 객체일관성이 중요한 데이터를 직접 다루기에 적합하지 않다. 따라서 데이터접근하기 위한 data access layer를 따로 분리해, Repository 컴포넌트에서 CRUD 등의 데이터 조작을 수행한다. 이때 데이터 조작은 DTO가 아니라 데이터베이스연관관계 매핑이 되어 있는 Entity 객체를 이용한다. Entity데이터베이스갱신에 직접 이용되므로, DTO와 달리 Setter 사용을 지양하고 생성자를 사용해 속성값을 setting하는 편이 바람직히다.

 

데이터 조작을 수행한 뒤, Entity일반적으로 Service에서 DTO로 변환되어 Controller로 전송된다.