뇌피셜 주의!! 공부하는 단계에서 도메인과 엔티티에 대해 찾아보며 제 생각을 정리해 본 글입니다.
도메인이란?
위키백과에는 다음과 같이 나와있다.
도메인은 일반적인 요구사항, 전문 용어, 그리고 컴퓨터 프로그래밍 분야에서 문제를 풀기 위해 설계된 어떤 소프트웨어 프로그램에 대한 기능성을 정의하는 연구의 한 영역이다. 도메인 엔지니어링 이라고도 알려져 있다.
쉽게 말하자면 소프트웨어에서 도메인이란 소프트웨어를 통해 해결하고자 하는 관심사의 영역이라고 할 수 있다.
그리고 도메인 지식이란 해당 도메인 영역을 해결하기 위해 필요한 배경 지식이다.
예를 들어 ‘지하철 역과 노선을 관리하는 애플리케이션’을 만든다고 가정해 보자.
애플리케이션을 개발하기 위해선 ‘지하철 역’과 ‘지하철 노선’이라는 도메인이 필요하고 해당 도메인을 애플리케이션에서 사용하기 위한 여러 규칙들(도메인 지식)이 필요할 것이다.
따라서 코드 안에서는 ‘Station’이라는 도메인 객체와 ‘Line’이라는 도메인 객체가 도출될 수 있다.
설계에 따라 역과 노선의 관계를 표현하기 위해 지하철 구간을 뜻하는 ‘Section’이라는 도메인 객체도 추가될 수도 있다.
위 도메인 객체들은 ‘지하철 노선을 관리하는 애플리케이션’을 만들기 위해 필요한 관심사들을 객체로 표현한 것이다.
그럼 엔티티는 무엇인가
그럼 엔티티란 무엇일까?
사실 도메인과 엔티티는 크게 다르지 않은 것 같다.
‘도메인 객체에 id 필드가 추가된 것이 엔티티다’라고 말해도 아예 틀린 말은 아닌 듯하다.
id 필드? 식별자?
그럼 이 ‘id’ 필드는 어디서 튀어나와서 도메인과 엔티티를 헷갈리게 하는 것일까?
우아한테크코스 레벨1 과정을 떠올려보자.
간단한 애플리케이션(자동차 경주, 로또, 블랙잭)을 만들 때도 각 도메인을 객체로 만들어서 사용했었다.
자동차 경주에서의 Car, 로또에서의 Lotto, 블랙잭에서의 Card, Player 등등 다양한 도메인 객체를 만들었지만 그들에게 id는 없었다.
이 차이가 어디서 왔느냐 하면 데이터베이스를 사용하기 시작하면 서다.
DB를 사용하기 전에는 애플리케이션이 휘발성이라서 한 번 실행이 끝나면 전의 정보를 저장해 줄 필요가 없었다.
하지만 지속적으로 도메인 관련 정보를 유지하고 싶기 때문에 데이터베이스가 도입되었다.
그리고 애플리케이션이 실행되다 보면 도메인 객체들의 상태가 변화한다.
예를 들어 블랙잭 게임에서 Player의 이름을 관리하고 싶다고 해보자.
Player 도메인 객체는 Name 필드를 가질 것이다.
그런데 이름을 수정한다고 했을 때 이름을 수정하면 Player는 다른 객체가 되는 것일까?
당장 나를 예로 들어봐도 내가 개명을 한다고 해서 내 정체성이나 다른 사람들에게 인식되는 ‘나’라는 존재는 그대로다.
100전 100승을 한 Player가 이름을 수정했다고 해서 갑자기 승부 기록이 0전 0승으로 초기화되어버리면 어이가 없을 것이다.
따라서 도메인 객체의 상태가 변화해도 그 객체를 고유하게 식별하기 위한 식별자 id가 추가된 것이고 이는 테이블의 기본키와 역할이 크게 다르지 않다.
아무리 객체의 상태가 변화되어도 식별자가 같다면 같은 객체로 취급될 것이며 Player의 백전백승 기록은 사라지지 않을 것이다.
(객체의 상태에 따라서 동등성이 판단된다면 그것은 VO에 가까울 것이다.)
그래서 엔티티란?
위의 내용을 정리하면 식별자를 가지는 객체이다.
실제로 엔티티의 가장 큰 특징이 식별자를 갖는다는 것이고 식별자 외에 상태가 변경된다고 해서 다른 객체가 되지 않는다는 점이다.
데이터베이스의 테이블과 유사한 스키마를 갖는데 단순한 테이블과 비교되는 점은 행위, 즉 비즈니스 로직을 갖고 있을 수도 있다는 점이다.
엔티티에 비즈니스 로직이 있어도 될까?
그렇다면 엔티티와 도메인은 식별자의 유무만 다른 것일까?
차이점을 굳이 찾아본다면 도메인엔 비즈니스 로직이 있는 것이 자연스러우나 엔티티에는 마냥 자연스러워 보이진 않는다는 것이다.
테이블의 기본키를 필드로 갖고 있으니 데이터베이스의 테이블로서의 역할만 해야하지 않을까 생각할 수도 있다.
엔티티를 단순한 '데이터'로 취급하고 스프링의 Service 레이어에서 로직을 처리하면 되지 않을까?
엔티티가 비즈니스 로직을 가지고 객체 지향의 특성을 적극 활용하는 것을 도메인 모델 패턴이라 한다.
반대로 엔티티에는 비즈니스 로직이 거의 없고 서비스 계층에서 대부분의 비즈니스 로직을 처리하는 것을 트랜잭션 스크립트 패턴이라 한다.
트랜잭션 스크립트 패턴을 사용하여 엔티티에 로직, 즉 행위를 갖지 않게 하고 테이블처럼 값과 값의 getter만 표현하면서 설계할 수도 있을 것이다.
이렇게 되면 Service 레이어에서 비즈니스 로직을 모두 처리하기 때문에 Service 객체가 매우 비대해질 것이다.
뿐만 아니라 로직을 처리하는 데 getter가 난무하여 객체지향적으로 설계가 어려워지게 될 것이다.
이처럼 엔티티를 데이터처럼 다루다 보면 자칫 객체지향에서 멀어져 데이터 중심의 설계를 할 수도 있다.
하지만 도메인 모델 패턴을 사용하여 엔티티에 책임을 부여하여 비즈니스 로직을 처리하게 한다면 Service의 비대함도 막고 각 도메인 객체의 응집도 또한 높일 수 있다.
트랜잭션 스크립트 패턴에 비해 도메인 모델 패턴이 가지는 단점은 설계하기가 어렵고 시간이 많이 걸린다는 점이다. (사실 이건 절차 지향과 대비되는 객체 지향이 갖는 단점이기도 한 듯)
찍먹 하는 DDD
실제로 데이터 중심 접근법을 탈피하기 위해 순수 도메인 로직에 집중하는 설계 방법을 도메인 주도 설계, DDD(Domain-Driven Design)라고 부른다.
DDD에서 엔티티는 고유 식별자를 가지는 테이블 모델이면서 동시에 해결하고자 하는 비즈니스 로직을 처리하는 도메인의 역할도 겸하고 있는 것이다.
엔티티가 비즈니스 로직을 가짐으로써 DB를 사용하는 개발을 하면서도 다음과 같은 장점을 느낄 수 있었다.
- 객체지향스러운 개발을 할 수 있다.
- 도메인(=엔티티) 객체의 응집도를 높일 수 있다.
- DI를 줄일 수 있다.
- 서비스 레이어가 비대해져서 서비스 클래스도 늘어난다면 해당 서비스를 DI 할 필요가 생길 수도 있다.
- 서비스 계층 코드가 간결해진다.
- Repository 등 다른 계층과 협력하는 로직만 담당할 수 있다.
- 코드가 간결하니 비즈니스 로직 흐름이 잘 보여 유지보수가 용이
- 테스트를 작성할 때 스프링 컨테이너 의존적이지 않은 단위 테스트를 많이 작성할 수 있다.
- 서비스가 비대해졌으면 @SpringBootTest를 붙인 테스트가 늘어났을 수도 있고, 슬라이스 테스트를 한다고 해도 그 준비 과정이 번거롭다.
이 글은 DDD에 대해 다루고 있는 것은 아니기에 짧게 넘어가지만 언젠가 DDD에 대해서도 제대로 공부해 보고자 한다.
정리
- 도메인 객체는 소프트웨어를 통해 해결하고자 하는 관심사를 표현한 객체로서 비즈니스 로직을 지니고 있다.
- 애플리케이션을 휘발성이 아닌 지속적으로 관리하고 싶어 져 DB를 사용하게 되면서 도메인 객체를 상태와 상관없이 식별할 필요가 생겼다.
- DB 테이블과 어느 정도 일치하면서 식별자(id)를 갖게 된 도메인 객체가 등장했고 이를 엔티티라고 한다.
- 하지만 엔티티를 도메인 객체와는 다르게 데이터로써만 취급할 수도 있고 이 때문에 설계가 객체지향과 멀어질 수도 있다.
- DDD는 데이터 중심 설계 탈피를 위한 설계이며 이를 위해선 테이블에 대응되는 엔티티가 객체로서 도메인의 역할을 하며 역할과 책임을 다 하게 된다.
참고
https://ko.wikipedia.org/wiki/도메인_(소프트웨어_공학)
https://multifrontgarden.tistory.com/186
https://incheol-jung.gitbook.io/docs/q-and-a/architecture/ddd
'방법론' 카테고리의 다른 글
소프트웨어와 복잡성 관리 (0) | 2024.09.01 |
---|---|
전략패턴과 OCP (2) | 2022.02.18 |
MVC(Model-View-Controller) 패턴이란 (0) | 2022.02.15 |
TDD(Test-Driven-Development) 강의 복습 (0) | 2022.02.13 |