배경

  • 팀 코드를 읽어보게 되었는데, 트랜잭션 스크립트 패턴에 가까운 형태를 취하고 있었다.
  • 지금까지 도메인 모델 패턴을 사용해온 입장에서는 상당히 절차지향 적이고 유지보수하기 힘들어보였는데, 이 두 가지 패턴이 어떤 장단점이 있는지 비교해본다.
  • 개인적인 견해는 강조 표시로 표기했다.

트랜잭션 스크립트 패턴

  • 프레젠테이션 레이어에서 오게되는 단일 요청별로 처리되는 메서드가 각각 존재하는 패턴이다. 비즈니스 로직을 처리하는 하나의 기능을 스크립트라고 표현한다.

    • 일반적으로 유스케이스 하나당 하나의 클래스를 가진다.
    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 처리 처리가 분리되어 단위 테스트가 가능해진다.
  • 단점

    • 도메인을 설계하는 과정이 복잡하다.
    • 비즈니스 로직이 도메인 객체들로 분산되면서 하나의 요청에 대한 전체적인 흐름을 파악하기 힘들다.

내 생각

  • 우리 팀의 프로젝트는 이미 규모가 커져서 충분히 복잡한 비즈니스 로직을 가지고 있다. 트랜잭션 스크립트 패턴을 유지하면 갈수록 코드의 복잡도가 올라가고, 객체지향의 이점을 가지기도 힘들 것이다.
  • 하지만, 한 번에 모든 코드를 리팩토링 하는 것은 절대 불가능하다고 생각하고 위험부담도 크다. 신규 개발이 있다면, 그 때부터는 도메인 모델 패턴을 사용해보고 기존 코드들도 점진적으로 리팩토링하는 것이 좋다고 생각한다.

참고 자료