🧐TIL

[DDD] 애플리케이션 서비스, Application Service

date
Mar 5, 2023
thumbnail
slug
ddd-appservice
author
status
Public
summary
“도메인 주도 설계 철저 입문”을 읽으며 공부한 내용을 정리합니다.
type
Post
category
🧐TIL
tags
TIL
DDD
Architecture
updatedAt
Jun 18, 2023 05:57 AM

애플리케이션 서비스란?

서비스의 정의 복습

앞서 도메인 서비스를 공부하면서 서비스란 ‘하나의 대상이 다른 대상에게 무언가를 해주는 것’으로 정의했다. 즉, 클라이언트를 위해 무언가를 해주는 것이며 그 무언가란 보통 사물보다는 활동이나 행동인 경우가 많다.
값 객체와 엔티티에도 행동이 정의되지만 그 행동들은 객체 자신을 위한 행동들인 반면 서비스는 자신을 위한 행동이 아니기 때문에 도메인 모델에 정의하면 어색함을 유발하므로 도메인 서비스라는 별도의 객체로 정의했었다.

애플리케이션

애플리케이션은 이용자의 목적에 부합하는 프로그램을 말한다. 애플리케이션의 목표는 이용자의 필요에 부응하고 목적을 달성하도록 도와주는 것이다.
DDD에서는 하나의 프로그램 내에서 도메인 지식을 표현하기 위한 패턴과 애플리케이션을 구성하기 위한 패턴을 구분하는데, 도메인 지식 패턴은 이용자가 도움을 필요로하는 분야에 대한 지식을 코드로 표현한 것일뿐 그 자체만으로는 이용자에게 도움을 줄 수 없다.
실질적인 프로그램으로서의 역할을 하기 위해서는 도메인 객체의 힘을 하나로 엮고 조작하여 목적을 달성하도록 이끄는 애플리케이션 객체의 구성이 필요하다.

애플리케이션 서비스

서비스와 애플리케이션의 개념을 합치면 애플리케이션 서비스를 정의할 수 있다. 애플리케이션 영역에서 클라이언트를 위해 요구사항에 따라 도메인 객체를 조작하는 행동을 정의한 것이 애플리케이션 서비스이다.
결론적으로 클라이언트를 위한 행동을 정의한 것으로 도메인 서비스와 애플리케이션 서비스는 같다고 볼 수 있는데 다만 정의하는 행동이 도메인 지식에 해당하는지 애플리케이션 고유의 행동인지에 따라 영역이 달라지는 것이다.

애플리케이션 서비스 구현

애플리케이션 서비스를 구현하기 위해서는 어떤 객체들이 필요한지를 정의하고 활용하면 된다.
유저 관련 CRUD 작업이 클라이언트를 위해 구현되어야할 서비스라고 정의했다면, 해당 애플리케이션 서비스를 구현하기 위해서는 User 엔티티와 User 엔티티의 속성으로 부여할 UserId, UserName 등의 값 객체가 필요할 것이다. 그리고 User 정보를 조회하고 복원할 UserRepository 리포지토리 객체도 필요할 것이다.
위 객체들을 사용하여 서비스에 필요한 속성과 동작을 구현하면 애플리케이션 서비스가 된다.

DTO의 사용

유저 정보를 확인할 수 있도록 하는 애플리케이션 서비스를 만든다면 해당 서비스가 클라이언트에게 User 인스턴스를 그대로 반환할 것인지 말지를 정의해야 한다.
User 인스턴스를 그대로 반환해도 되지만 그럴 경우 도메인 객체가 클라이언트에게 공개되고 클라이언트가 도메인 객체의 메소드를 호출할 수 있게 된다. 애플리케이션 서비스가 아닌 객체가 도메인 객체를 조작할 수 있게 될 경우 도메인 객체를 조작하는 책임이 애플리케이션 서비스 밖으로 분산되어 코드가 흩어질 수 있고 도메인 객체에 대한 의존이 여기저기에 발생할 수 있다.
이런 위험부담을 줄이는 방법으로 클라이언트에게 정보를 반환할 때는 데이터 전송을 위한 객체인 DTO(Data Transfer Object)를 만들어 해당 객체에 정보를 옮겨 담아 반환하는 방법을 사용할 수 있다.

DTO 생성자 정의시 유의사항

DTO를 생성할 때 속성 값을 하나하나 인자로 받아서 생성할 수도 있으나 그렇게 되면 클라이언트에게 전달할 정보가 추가될 때마다 DTO가 사용된 코드를 모두 찾아서 수정해줘야되는 문제가 발생한다.
DTO를 정의할 때는 생성자 메소드의 인자로 클라이언트에게 정보를 전달하고자 하는 도메인 객체를 직접 받게하면 수정해야 하는 곳을 줄일 수 있다.

커맨드 객체의 사용

애플리케이션 서비스의 처리내용에 따라 인자를 다르게 정의할 수 있다. 예를들면 유저 정보를 수정할 때 유저명을 인자로 받아 수정을 처리하도록 할 수 있다. 하지만 만약 수정가능한 속성이 늘어난다면? 유저 도메인 객체에 이메일 주소라는 속성이 추가되었고 애플리케이션 서비스를 통해 이메일 주소도 상황에 따라 수정이 가능하도록 하려면 인자까지도 수정을 해줘야 한다. 그리고 속성이 추가될 때매다 위 과정은 반복될 것이다.
이렇게 파라미터가 추가되더라도 매번 시그니처를 수정하지 않아도 되도록 하기 위해서 커맨드 객체를 정의해서 사용할 수 있다. 커맨드 객체를 사용하면 애플리케이션 서비스에서 처리되는 내용에 대한 정보를 간략화하여 보여주고 필요한 처리에 대해 클라이언트가 간접적으로 제어할 수 있게 해준다.

도메인 규칙의 노출 방지

애플리케이션 서비스는 필요에 따라 도메인 객체를 조작하는 책임만을 가지며 도메인 규칙을 알아서는 안된다. 도메인 규칙이 애플리케이션 서비스 내에 노출되어 있을 경우 도메인 규칙이 변경되었을 때 해당 도메인 규칙이 사용된 애플리케이션 서비스를 모두 수정해야 하는 문제가 발생한다. 또한 도메인 규칙이 단순할 때는 비교적 문제가 적겠지만 규칙이 복잡해질수록 애플리케이션 서비스의 코드를 파악하기도 어려워질 것이다.
문제를 해결하는 방법은 간단하다. 도메인 규칙이 사용되는 행동은 도메인 객체에 정의하면 된다. 앞서 리포지토리 챕터에서 유저 중복검사가 필요할 경우 중복검사라는 행동에는 도메인 규칙이 사용되므로 리포지토리에 중복검사 메소드가 정의되어서는 안된다고 했다. 마찬가지로 해당 책임은 애플리케이션 서비스에 부여되어서는 안된다.
중복검사는 도메인 서비스로 정의하고 애플리케이션 서비스에서는 해당 도메인 서비스 객체를 사용하여 중복검사를 요청하고 중복검사가 어떻게 이루어지는 지는 모르는채로 그 결과만을 받아 다룰 수 있도록 하는 것이 좋다

애플리케이션 서비스와 프로그램의 응집도

응집도는 모듈이 책임 범위가 얼마나 집중되어 있는지 나타내는 척도다. 응집도가 높다는 것은 모듈이 하나의 관심사에 집중하고 있다는 뜻으로 견고성, 신뢰성, 재사용성, 가독성 측면에서 바람직하다. 그렇다면 응집도를 높이기 위해 애플리케이션 서비스를 어떻게 구성하는 것이 좋을까?
간단하게는 클래스의 속성이 클래스의 모든 메소드에서 공통적으로 사용되도록 하고 만약 특정 메소드에만 사용되는 속성이 존재한다면 클래스가 분리되도록 하면 된다. 모든 케이스에 적용되는 것은 아니지만 유즈케이스 별로 애플리케이션 서비스 클래스를 나누면 애플리케이션 서비스 별로 하나의 메소드와 반드시 필요한 속성들만을 갖게 되므로 응집도를 높일 수 있다.
여기에 개념별로 연관된 클래스들을 패키지로 모아놓을 경우 개발자가 서로 관련된 처리를 찾기도 쉬워진다.
다만, 응집도는 절대적인 지표가 아니며 반드시 유즈케이스 마다 클래스가 분리되어야 하는 것은 아니므로 클래스의 인스턴스 변수와 메소드 대응 관계를 통해 보는 응집도는 코드를 정돈하기 위한 참고사항으로 기억해두면 된다.