본문으로 바로가기
728x90

업무 규칙

앞서 업무 규칙이란 말이 많이 나왔는데 업무 규칙에 대해 알아보자.

업무 규칙은 자동화와 관계 없이 사업적으로 수익을 얻거나 비용을 줄일 수 있는 규칙 또는 절차다.

일반적으로 업무 규칙은 데이터를 요구하는데 이를 ‘핵심 업무 데이터’ 라고 하고 둘은 본질적으로 결합되어 있기 때문에 객체로 만들기 좋은 후보이다. 그리고 이 객체를 ‘엔티티’ 라고 하고 핵심 업무 데이터를 직접 포함하거나 접근하는데 도움을 준다.

반면에 애플리케이션에 특화된 업무 규칙을 설명하는, 예를 들어 사용자의 입력, 출력, 출력을 위한 처리 단계등 자동화된 시스템이 사용되는 방법을 설명하는 것은 ‘유스케이스’ 라고 부른다.

위에 나온 ‘핵심 업무 데이터’, ‘엔티티’ 그리고 ‘유스케이스’ 로 나눠 업무 규칙이 저수준의 관심사로 오염되지 않도록하여 많이 재사용할 수 있는 코드형태로 남겨둬야한다.

 

소리치는 아키텍처

앞서 나왔지만 좋은 아키텍처는 유스케이스를 중심으로 두기에 프레임워크나 도구, 환경에 구애받지 않고 유스케이스를 지원하는 구조를 아무런 문제 없이 기술 하고 지엽적인 관심사에 대한 결합을 분리시켜야한다.

프레임워크와 무관하게 유스케이스에 대해 전부 테스트를 할 수 있어야 한다. 또한 데이터베이스, 웹 서버 없이도 독립적으로 테스트 할 수 있어야 한다. 이를 위해 엔티티 객체는 Plain Object 여야 한다.

이러한 유스케이스를 보면 ‘무슨 시스템’ 인지 파악할 수 있다.

 

클린 아키텍처

다양한 시스템 아키텍처가 있지만 모두 ‘관심사 분리’가 핵심이다. 그리고 이러한 아키텍처는 프레임 워크 독립성, 테스트 용이성, UI 독립성, 데이터베이스 독립성, 외부 에이전시에 대한 독립성을 갖는다. 그리고 클린 아키텍처는 이 전부를 실행 가능하도록 하는 하나의 아이디어다.

바깥쪽이 저수준, 안으로 갈수록 고수준이며 반드시 의존성 규칙이 바깥에서 안쪽으로 향해야한다.

  • 엔티티는 전사적인 핵심 규칙을 캡슐화한다. 바깥 계층에 의해 영향을 받아서는 안된다.
  • 유스케이스 계층은 모든 유스케이스를 캡슐화하고 구현한다. 운영 관점에서 변경된다면 영향을 받고 소프트웨어 코드에도 영향이 가지만 그 외 데이터베이스 등 외부 계층에 의해선 변경되서는 안된다.
  • 인터페이스 어댑터 계층은 일련의 어댑터로 데이터를 유스케이스와 엔티티에 편한 방식으로 변환하는데 도움을 준다. 대표적인 예로 프리젠터, 뷰, 컨트롤러 그리고 데이터를 외부적인 형식에서 유스케이스나 엔티티에서 사용되는 내부적인 형식으로 변환하는 어댑터가 있다.
  • 그 외 데이터베이스, 웹 프레임 워크와 같은 도구들은 세부사항에 위치한다.

이 경계를 횡단할 때 의존성 제어흐름과 반대여야 한다면 의존성 역전 원칙을 사용해야 하고 데이터 전송 객체(Data Transfer Object, DTO) 와 같은 간단한 형태로 골라 전달할 수 있다.

 

프리젠터와 험블 객체

험블 객체 패턴은 디자인 패턴으로, 테스트하기 어려운 행위와 쉬운 행위를 단위 테스트 작성자가 분리하기 쉽게 하는 방법으로 고안되었다. 기본적인 본질을 남기고 테스트 하기 어려운 행위를 모두 험블 객체로 옮긴다. 그리고 또 다른 모듈에 테스트하기 쉬운 행위만 옮기는 방법이다.

뷰는 테스트하기 어렵지만 프리젠터는 어플리케이션으로부터 데이터를 받아 화면에 표현할 수 있는 포맷으로 만드는것이기에 테스트하기 쉬운 객체이다.

데이터베이스를 예로보면, 유스케이스 계층은 SQL을 허용하지 않아 필요한 메서드를 게이트웨이 인터페이스를 통해 호출하고 이는 데이터베이스 계층에 위치한다. 이는 험블 객체이다. 반면, 인터랙터는 어플리케이션 특화된 업무 규칙을 캡슐화하기에 험블 객체가 아니고 스텁이나 테스트 더블로 게이트웨이를 적당히 교체할 수 있기 때문이다. 반면에 ORM과 같은 맵퍼는 데이터베이스 계층에 존재하고 게이트웨이 인터페이스와 데이터베이스 사이에 또 다른 험블 객체 경계를 형성한다.

이처럼 아키텍처 경계마다 험블 객체 패턴을 발견할 수 있고 이를 사용하면 시스템의 테스트 용이성을 크게 높힐 수 있다.

부분적 경계

경게를 완벽하게 만드는덴 비용이 많이드는데 애자일 방법론에서는 부분적 경계를 구현해볼 수 있다. 독립적으로 컴파일하고 배포할 수 있도록 만들어두고 대신 단일 컴포넌트에 그대로 모아만 두는 것이다. 다수의 컴포넌트를 관리하고 추적을 위한 버전, 배포 관리와 같은 마지막 단계를 건너 뛰게 해준다.

또 다른 방법으로는 추후 완벽한 형태의 경계로 확장할 수 있도록 Strategy Pattern 을 고려할 수 있다. Facade Pattern을 통해서도 단순한 경계를 둘 수 있는데, 모든 서비스 클래스를 메서드 형태로 정의하고 서비스 호출이 발생하면 해당 서비스 클래스로 호출을 전달해 직접 접근하지 못하도록 할 수 있다. 대신 퍼사드를 바라보고 있는 Client는 모든 클래스에 추이종속성을 갖는 단점이 생긴다.

사실 두 가지 패턴 모두 의존성 역전에 용이한 패턴이다. 아키텍처 경계가 언제 어디서 존재할지는 위 패턴 뿐만 아니라 경계를 완벽하게할지 부분적으로 구현할지는 아키텍처가 결정해줘야한다.