배경
- 팀 코드를 읽어보게 되었는데, 트랜잭션 스크립트 패턴에 가까운 형태를 취하고 있었다.
- 지금까지 도메인 모델 패턴을 사용해온 입장에서는 상당히 절차지향 적이고 유지보수하기 힘들어보였는데, 이 두 가지 패턴이 어떤 장단점이 있는지 비교해본다.
- 개인적인 견해는
강조 표시
로 표기했다.
트랜잭션 스크립트 패턴
프레젠테이션 레이어에서 오게되는 단일 요청별로 처리되는 메서드가 각각 존재하는 패턴이다. 비즈니스 로직을 처리하는 하나의 기능을 스크립트라고 표현한다.
- 일반적으로 유스케이스 하나당 하나의 클래스를 가진다.
class BuyProductBO { List<Product> getProducts(string filter) { /* some database query */ } void putInCart(int productId, int amount) { ... } void checkout(...) {...} }
장점
- 단순한 구조를 가지고 있다.
- 단순한 논리를 갖는 비즈니스 데이터베이스 시스템에서 빠른 구현이 가능하다.
요청별로 로직을 분리하기 때문에 각 요청마다의 흐름을 한 번에 파악할 수 있다.
단점
- 코드 중복이 발생하기 쉽고, 코드 재사용성을 줄인다.
- 비즈니스 로직을 구현할 때 객체지향 방식으로 논리를 구현할 수 없다.
- 객체지향의 특징인 캡슐화와 정보 은닉을 위반하고 있다.
- 모델 객체의 유효성 검사가 외부에 배치되기 때문에 어떤 순간에도 정확성을 보장할 수 없다.
서비스에 비즈니스 로직과 DB/UI 처리가 섞여있어서 유연한 단위 테스트가 불가능하다.
도메인 모델 패턴
비즈니스 로직의 많은 부분이 도메인(엔티티)로 이동하고, 서비스에서는 도메인을 호출하는 정도의 얇은 비즈니스 로직을 가지게 된다.
class UserBO { void buyProduct() { ... user.updatePaymentInfo() ... } } class User { String getPaymentInfo() {...} void updatePaymentInfo() {...} }
장점
- 도메인 객체 뒤로 비즈니스 로직을 옮기면서 복잡도를 낮추고, 유연성이 올라간다.
- 코드 중복을 줄일 수 있다.
- 유효성 검사를 도메인 객체에 두게되면서, 도메인 객체가 항상 정확성을 보장할 수 있게 된다.
비즈니스 로직과 DB/UI 처리 처리가 분리되어 단위 테스트가 가능해진다.
단점
- 도메인을 설계하는 과정이 복잡하다.
비즈니스 로직이 도메인 객체들로 분산되면서 하나의 요청에 대한 전체적인 흐름을 파악하기 힘들다.
내 생각
- 우리 팀의 프로젝트는 이미 규모가 커져서 충분히 복잡한 비즈니스 로직을 가지고 있다. 트랜잭션 스크립트 패턴을 유지하면 갈수록 코드의 복잡도가 올라가고, 객체지향의 이점을 가지기도 힘들 것이다.
- 하지만, 한 번에 모든 코드를 리팩토링 하는 것은 절대 불가능하다고 생각하고 위험부담도 크다. 신규 개발이 있다면, 그 때부터는 도메인 모델 패턴을 사용해보고 기존 코드들도 점진적으로 리팩토링하는 것이 좋다고 생각한다.