계층형 아키텍처데이터 액세스 계층서비스 계층프레젠테이션 계층계층형 아키텍처 설계의 원칙애플리케이션 정보 아키텍처DB/SQL 중심의 로직 구현 방식거대한 서비스 계층 방식오브젝트 중심 아키텍처데이터와 오브젝트도메인 오브젝트 사용의 문제점빈약한 도메인 오브젝트 방식풍성한 도메인 오브젝트 방식스프링 애플리케이션을 위한 아키텍처 설계상태 관리와 빈 스코프
계층형 아키텍처
데이터 액세스 계층, 서비스 계층, 프레젠테이션 계층은 수평관계임
각각의 계층 안에서 기술의 추상화, 세부적인 기술들이 수직적관계인 것
데이터 액세스 계층
- 외부 시스템을 호출해서 서비스를 이용하는 것은 기반(infrastructure) 계층으로 따로 분류하기도 함.
- 데이터 액세스 계층은 대개 장기적인 데이터 저장을 목적으로 하는 DB 이용이 주된 책임
서비스 계층
- 서비스 계층은 DAO 계층을 호출하고 이를 활용해서 만들어짐
- 때론 데이터 액세스를 위한 기능 외에 서버나 시스템 레벨에서 제공하는 기반 서비스를 활용할 필요도 있음
- 예를 들어 웹 서비스와 같은 원격 호출을 통해 정보를 가져오거나 메일 또는 메시징 서비스를 이용하는 것
- 이런 기반 서비스는 3계층 어디에서나 접근이 가능하도록 만들 수 있고, 아키텍처를 설계하기에 따라서 반드시 서비스 계층을 통해 사용 되도록 제한할 수도 있음
- 이상적인 서비스 계층은 백엔드 시스템과 연결되는 데이터 액세스 계층이 바뀌고, 클라이언트와 연결되는 프레젠테이션 계층이 모두 바뀌어도 그대로 유지될 수 있어야 한다.
- 엔터프라이즈 어플리케이션에서 가장 중요한 자산은 도메인의 핵심 비즈니스 로직이 들어 있는 서비스 계층이어야 한다.
- 기반서비스 계층 : 트랜잭션, 보안, 리모팅, 메일, 메시징, 스케줄링 …
프레젠테이션 계층
- 프레젠테이션 계층은 가장 복잡한 계층. 매우 다양한 기술과 프레임워크의 조합을 가질 수 있음
계층형 아키텍처 설계의 원칙
- 오브젝트와 그 관계에 적용했던 대부분의 객체 지향 설계의 원칙은 아키텍처 레벨의 계층과 그 관계에도 동일하게 적용할 수 있다. 각 계층은 응집도가 높으면서 다른 계층과는 낮은 결합도를 유지할 수 있어야 한다.
- 각 계층은 자신의 계층의 책임에만 충실해야 한다
- 데이터 액세스 계층에 비즈니스 로직을 담거나 웹 파라미터를 파싱하는 코드나 결과를 화면에 어떻게 뿌릴지 결정하는 코드가 들어간다면 응집도가 낮아진다.
- 각 계층은 자신의 역할에만 충실해야 하고 자신과 관련된 기술이 아닌 다른 기술 API의 사용을 삼가해야 한다.
- 계층 간에 사용되는 인터페이스 메소드에는 특정 계층의 기술이 최대한 드러나지 않게 만들어야 한다. 그렇지 않으면 계층 사이에 결합도가 높아질 뿐 아니라, 계층 간의 기술이나 역할이 서로 침범하는 일이 일어난다.
- 흔히 저지르는 실수 중의 하나는 프레젠테이션 계층의 오브젝트를 그대로 서비스 계층으로 전달하는 것이다. 서블릿의
HttpServletRequest
나HttpServletResponse
,HttpSession
같은 타입을 서비스 계층 인터페이스 메소드의 파라미터 타입으로 사용하면 안 된다. - 어떤 경우에라도 계층 사이의 낮은 결합도를 깨뜨리지 않도록 설계해야 한다. 당연히 계층 사이의 호출은 인터페이스를 통해 이뤄져야 한다. 인터페이스를 하나 더 만드는 것이 번거롭다고 그냥 클래스를 이용해서는 안 됨
- 인터페이스에 아무 생각 없이 클래스의 모든 public 메소드를 추가한다면 인터페이스를 사용하는 가치가 떨어짐. 한번 정의돼서 다른 계층에서 사용하기 시작한 인터페이스 메소드는 변경이 매우 까다롭고 비용이 많이 듦. 따라서 매우 신중하게 결정해야 하며 계층 내부의 예상되는 변화에도 쉽게 바뀌지 않도록 주의해서 만들어야 함.
애플리케이션 정보 아키텍처
아키텍처는 애플리케이션이 다루는 정보의 관점에서 데이터 중심과 오브젝트 중심으로 구분할 수 있다.
데이터 중심의 아키텍처의 특징은 계층 사이의 결합도가 높은 편이고 응집도는 떨어진다는 점이다. 화면을 중심으로 하는 일부 트랜잭션 단위로 코드가 모이기 때문에 처음엔 개발하기 편하지만 중복이 많아지기 쉽고 장기적으로 코드를 관리하고 발전시키기 힘들다는 단점이 있다.
- 특정 요구사항에 필요한 데이터만 SQL 로 뽑아서 가져오는 것
DB/SQL 중심의 로직 구현 방식
- DB/sql 중심의 로직 구현 방식은 변화에 매우 취약함. 각 계층의 코드가 긴밀하게 연결되어 있기 때문에
- 잘 작성된 복잡한 SQL 하나가 수백 라인의 자바 코드가 필요한 비즈니스 로직을 한번에 처리할 수도 있지만 이게 과연 바람직한 것인지 고민해봐야 함. 이렇게 복잡한 SQL 코드를 누구나 쉽게 이해하고 필요에 따라 유연하게 변경할 수 있을지, 또 복잡한 SQL 을 처리하기 위해 제한된 자원인 DB에 큰 부담을 주는 게 과연 바람직한 일인지도 생각해볼 필요가 있음.
- 상대적으로 어플리케이션 서버와 그 안에 담긴 오브젝트는 비용이 적게 듬
- SQL 이나 저장 프로시저에 담긴 로직은 테스트하기도 힘들다. 반면 오브젝트에 담긴 로직은 간단히 검증할 수 있음. 또한 요즘 유행하는 객체지향 분석과 모델링의 결과로 나온 모델을 가져다 쉽게 오브젝트로 만들어낼 수 있다
- 따라서 DB 에는 부하를 가능한 한 주지 않는 간단한 작업만 하고 복잡한 로직은 오브젝트에 담아서 애플리케이션 내에서 처리하도록 만드는 편이 낫다. 애플리케이션 서버는 저렴한 비용으로 손쉽게 서버를 추가할 수 있다.
거대한 서비스 계층 방식
- DB 에서 가져온 데이터가 애플리케이션에 흘러다니는 정보의 중심이 되는 아키텍처이긴 하지만 DB에 많은 로직을 두는 개발 방법의 단점을 피하면서 애플리케이션 코드의 비중을 높이는 방법 → DB에는 부하가 걸리지 않도록 저장 프로시저의 사용을 자제하고 복잡한 SQL을 피하면서 주요 로직은 서비스 계층의 코드에서 처리하도록 만드는 것
- 상대적으로 단순한 DAO 로직을 사용하고 비즈니스 로직의 대부분을 서비스 계층에 집중하는 이런 접근방법은 결국 거대한 서비스 계층을 만들게 된다.
오브젝트 중심 아키텍처
- 오브젝트 중심 아키텍처가 데이터 중심 아키텍처와 다른 가장 큰 특징은 도메인 모델을 반영하는 오브젝트 구조를 만들어두고 그것을 각 계층 사이에서 정보를 전송하는 데 사용한다는 것임
- 그래서 오브젝트 중심 아키텍처는 객체지향 분석과 모델링의 결과로 나오는 도메인 모델을 오브젝트 모델로 활용한다.
- 대개 도메인 모델은 DB의 엔티티 설계에도 반영되기 때문에 관계형 DB의 엔티티 구조와도 유사한 형태일 가능성이 높다. 물론 DB에는 없지만 비즈니스 로직에만 존재하는 모델도 있기 때문에 항상 일치하는 것은 아니다.
데이터와 오브젝트
어떤 업무를 분석해보니 카테고리와 상품이라는 두 가지 엔티티가 나온다고 해보자.
카테고리 하나에는 여러 개의 상품이 포함되고 전형적인 1:N 관계가 구성된다.
조건에 맞는 모든 카테고리와 상품 정보를 가져와서 화면에 출력하는 기능을 만든다고 할 때
- 데이터 중심 아키텍처에서는 SQL과 DB 관점에서 생각한다.
- 데이터 중심 아키텍처에서는 DAO가 만드는 SQL의 결과에 모든 계층의 코드가 의존하게 됨. 도메인 분석을 통해 작성된 모델정보는 DB에 대한 SQL을 작성할 때 외에는 코드에 반영되는 일이 없다.
- 반면 오브젝트 방식에서는 애플리케이션에서 사용되는 정보가 도메인 모델의 구조를 반영해서 만들어진 오브젝트 안에 담긴다. 도메인 모델은 애플리케이션 전 계층에서 동일한 의미를 가짐. 따라서 도메인 모델이 반영된 도메인 오브젝트도 전 계층에서 일관된 구조를 유지한 채로 사용될 수 있다.
도메인 오브젝트 사용의 문제점
- 최적화된 SQL을 매번 만들어 사용하는 경우에 비해 성능 면에서 조금은 손해를 감수해야 할 수도 있다.
- DAO는 비즈니스 로직의 사용 방식을 알지못하므로, 도메인 오브젝트의 모든 필드 값을 다 채워서 전달하게 되는데 그 중 드물게 사용되는 필드도 있을 수 있는 것임.
- 어떤 비즈니스 로직에서 필요한 정보가 몇 개의 필드 뿐이라면 DAO에서 도메인 오브젝트의 모든 필드 정보를 채워서 전달하는 것은 낭비일 수도 있다.
- 결국 최적화를 고려해서 DAO를 작성하려면 DAO는 비즈니스 로직에서 각 오브젝트를 어디까지 사용해야 하는지 어느 정도 알고 있어야 한다. 그래서 데이터 중심 접근 방법의 단점이라고 봤던, DAO와 비즈니스 로직 코드의 결합도가 높아지는 문제가 발생할 수도 있다.
- lazy loading
- 가장 이상적인 방법은 JPA나 하이버네이트와 같은 오브젝트 RDB 매핑(ORM) 기술을 사용하는 것
이 문제를 해결하는 접근 방법
빈약한 도메인 오브젝트 방식
도메인 오브젝트는 자바오브젝트다. 오브젝트는 원래 데이터를 저장하기 위해서만 사용하는 것이 아니고, 내부의 정보를 이용하는 기능도 함께 갖고 있어야 한다. 클래스는 속성과 행위의 조합임. 필드와 그에 대한 접근자, 수정자만 갖고 있는 오브젝트는 반쪽짜리다.
- 도메인 오브젝트에 정보만 담겨 있고 정보를 활용하는 아무런 기능도 갖고 있지 않다면 이는 온전한 오브젝트라고 보기 힘들고 그래서 이런 오브젝트를 빈약한 오브젝트라고 부름.
- 빈약한 도메인 오브젝트 방식에서는 비즈니스 로직이 서비스 계층에 존재. 다루는 정보의 구조가 다를 뿐, 빈약한 도메인 오브젝트 방식은 데이터 중심 아키텍처의 거대 서비스 계층 구조와 비슷하다.
- 도메인 오브젝트라는 일관도된오오브젝를트 용하기 때문에 SQL에 의존적인 데이터 방식보다는 훨씬 유연하고 간결하지만, 여전히 서비스 계층의 메소드에 대부분의 비즈니스 로직이 들어 있기 때문에 로직의 재사용성이 떨어지고 중복의 문제가 발생하기 쉽다.
- 하지만 비즈니스 로직이 복잡하지 않다면 가장 만들기 쉽고 3계층 구조의 특징을 잘 살려서 개발할 수 있는 유용한 아키텍처다.
풍성한 도메인 오브젝트 방식
- 빈약한 도메인 오브젝트의 단점을 극복하고 도메인 오브젝트의 객체지향적인 특징을 잘 사용할 수 있도록 개선한 것
- 어떤 비즈니스 로직은 특정 도메인 오브젝트나 그 관련 오브젝트가 가진 정보와 깊은 관계가 있다. 이런 로직을 서비스 계층의 코드가 아니라 도메인 오브젝트에 넣어주고, 서비스 계층의 비즈니스 로직에서 재사용하게 만드는 것이다.
- 도메인 오브젝트 안에 메소드로 들어가는 로직들은 대부분 해당 오브젝트나, 긴밀한 연관관계를 맺고 있는 관련 오브젝트의 정보와 기능만을 활용한다. 여러 종류의 도메인 오브젝트의 기능을 조합해서 복잡한 비즈니스 로직을 만들었다면 특정 도메인 오브젝트에 넣기는 힘들다. 이런 비즈니스 로직은 서비스 계층의 오브젝트에 두는 것이 적당하다.
스프링 애플리케이션을 위한 아키텍처 설계
- 위의 내용은 3단계로 역할을 분리하는 계층형 아키텍처와 정보를 다루는 방법에 따른 아키텍처의 종류를 살펴보았고, 그 외에도 다양한 기술 조합과 업무조건, 시스템환경에 따른 많은 결정요소와 변수가 있다.
- 그 중에서 계층구조를 어떻게 나눌 것인가와 애플리케이션 정보를 어떻게 다룰지를 결정하는 것이 기본이 된다. 그리고 그 위에 각 계층에 사용될 구체적인 기술의 종류와 수직 추상화 계층의 도입, 세세한 기술적인 조건을 결정하는 일이 남음
상태 관리와 빈 스코프
- 아키텍처 설계에서 한 가지 더 신경 써야 할 사항은 상태 관리다. 크게는 사용자 로그인 세션 관리부터, 작게는 하나의 단위 작업이지만 여러 페이지에 걸쳐 진행되는 위저드 기능까지 애플리케이션은 하나의 HTTP 요청의 범위를 넘어서 유지해야 하는 상태정보가 있다.
- 엔터프라이즈 애플리케이션은 특정 사용자가 독점해서 배타적으로 사용되지 않고 동시에 수많은 사용자의 요청을 처리되기 때문에 서버 자원이 특정 사용자에게 일정하게 할당되지 않고
stateless
하다.
- 스프링에서는 싱글톤 외에도 다른 스코프를 갖는 빈을 간단히 만들 수 있다. 빈스코프를 잘 활용하면 스프링이 관리하는 빈이면서 사용자별로 또는 단위 작업별로 독립적으로 생성되고 유지되는 오브젝트를 만들어 상태를 저장하고 이를 DI를 통해 서비스 빈에서 사용하게 만들 수 있다.