아키텍처: 시스템 구조와 품질을 결정하는 핵심 설계 원칙
- 아키텍처는 기본적으로 시스템의 구조를 정의하는 것이다. 단순히 내부 구성이 어떻게 생겼는지를 설명하는 것을 넘어 이 구조가 왜 그렇게 되어야 하는지에 대한 깊은 고민은 담고 있다
아키텍처의 중요성: 품질 속성과 설계 기반
- 모든 아키텍처는 명확한 도입 목표를 가지고 있어야 한다. 특정 아키텍처 스타일이나 패턴을 채택하는 이유는 우리가 만들고자 하는 소프트웨어 시스템의 중요한 품질 속성(Quality Attributes)을 향상시키기 위함이다. 예를 들어 성능, 확장성, 보안, 유지보수성, 신뢰성 등 다양한 품질 목표 중 어떤 것을 우선적으로 달성할 것 인지가 아키텍처 선택의 핵심 기준이 된다
- 아키텍처는 시스템을 구성하는 다양한 컴포넌트(구성 요소)들의 설계를 결정하는 기반이자 핵심 개념이 된다. 단순히 큰 범위에서 시스템의 골격을 잡는 것에서 그치지 않고 선택된 아키텍처에 따라 개별 컴포넌트의 내부 구조, 심지어 코드 레벨의 구현 방식에까지 깊이 영향을 미친다. 이는 아키텍처가 곧 시스템 전체의 일관성과 효율성을 좌우하는 지침이 되기 때문이다
아키텍처의 제약 조건과 원칙
- 성공적인 아키텍처를 구축하기 위해서는 몇 가지 중요한 제약 조건(Constraints)과 원칙(Principles)을 준수해야 한다. 이러한 제약 조건은 기술적, 비즈니스적, 혹은 운영적 요구사항에서 비롯될 수 있으며, 아키텍처 설게의 방향과 범위를 제한한다. 예를 들어 기술 스택 사용 의무, 예산 제약, 개발 기간, 규제 준수 등이 제약 조건이 될 수 있다
계층형 아키텍처(Layered Architecture)
계층형 아키텍처는 시스템을 여러 개의 독립적인 계층(Layer)으로 나누어 구조화하는 가장 일반적이고 전통적인 아키텍처 스타일 중 하나이다. 마치 건물처럼 층층이 쌓아 올린 형태로 각 계층은 특정 역할과 책임을 가진다
핵심 원칙과 제약 조건
- 단방향 사용 관계: 계층 간의 사용 관계는 반드시 단방향으로 이루어져야 한다. 일반적으로 상위 계층이 하위 계층의 서비스를 사용하는 하향식 흐름을 가진다. 예를 들어 ‘프레젠테이션 계층’은 ‘도메인/비즈니스 계층’을 사용하고, ‘도메인/비즈니스 계층’은 ‘인프라/데이터 계층’을 사용한다. 하위 계층이 상위 계층을 직접 사용하는 것은 금지된다
- 계층의 격리 (캡슐화): 각 계층은 자신의 내부 동작 방식이나 구현 디테일을 다른 계층으로부터 완전히 감춰야 한다. 오직 제한된 공개 인터페이스(API)를 통해서만 기능을 노출하고 상호작용해야 한다. 이는 전형적인 캡슐화 원칙의 적용이며, 다양한 이점을 제공한다
- 변경의 영향 최소화: 한 계층의 내부 구현이 변경되더라도 공개된 인터페이스가 동일하게 유지된다면 상위 계층에는 영향을 미치지 않는다. 이는 시스템의 유지보수성과 유연성을 크게 향상시킨다
- 재사용성: 각 계층이 독립적으로 설계되므로 특정 계층을 다른 프로젝트나 환경에서 재사용하기 용이해진다
- 테스트 용이성: 각 계층을 독립적으로 테스트할 수 있어 단위 테스트 및 통합 테스트가 쉬워진다
계층형 아키텍처의 실제 적용
- 스프링 프레임워크와 같은 환경에서 서비스 빈이 인터페이스를 구현하도록 권장하는 이유도 바로 이 계층의 격리 원칙 때문이다. 서비스 계층이 상위 계층에 자신의 기능을 노출할 때, 구체적인 클래스가 아닌 인터페이스를 통해 노출함으로써 상위 계층이 하위 계층의 구현 디테일에 의존하지 않도록 만든다
완화된 계층형 아키텍처 (Relaxed Layered Architecture)
- 엄격한 계층형 아키텍처에서는 각 계층이 바로 아래 계층에만 사용할 수 있도록 제한한다. 그러나 현실적인 복잡성과 유연성을 위해 완화된 계층형 아키텍처는 특정 계층이 바로 아래 계층뿐만 아니라, 그 아래의 더 하위 계층도 직접 사용할 수 있도록 허용한다. 이 경우에도 단방향 사용 관계라는 핵심 원칙은 반드시 지켜져야 한다. 하위 계층이 상위 계층을 사용하는 역방향 의존성은 여전히 금지된다.
도메인 모델 패턴의 두 가지 스타일
도메인 모델 패턴은 비즈니스 로직의 핵심을 객체지향적으로 표현하는 방법이며 주로 두 가지 스타일로 나뉜다
단순 도메인 모델 (SImple Domain Model)
- 데이터베이스(DB) 설계와 유사하게 도메인 모델이 만들어진다. 대체로 DB 테이블 하나당 도메인 클래스 하나가 1:1로 매핑되는 구조를 가진다
- 데이터를 저장하고 관리하는 데 필요한 속성(데이터)외에도 그 데이터를 다루는 간단한 로직(행위)을 포함하고 있다면 이를 단순 도메인 모델이라고 할 수 있다
- 빈혈 걸린 도메인 모델 (Anemic Domain Model): 만약 도메인 객체가 속성만 가지고 있고 데이터를 활용하는 어떠한 로직도 포함하지 않는다면, 이는 ‘빈혈 걸린 도메인 모델’이라고 부른다. 이러한 모델은 사실상 도메인 모델 패턴이라고 보기 어렵다. 비즈니스 로직이 대부분 서비스 계층과 같은 외부 트랜잭션 스트립트에 집중되어 있기 때문이다
풍성한 도메인 모델 (Rich Domain Model)
- 객체지향 기술을 적극적으로 활용하여 도메인의 복잡한 비즈니스 규칙과 행위를 모델링한다
- DB 테이블과의 1:1 매핑에 얽매이지 않고, 상속, 다형성, 전략 패턴 등 다양한 객체지향 설계 패턴을 적용하여 도메인의 핵심을 더욱 유연하고 강력하게 표현한다
- 하나의 엔티티(Entity)나 값 객체(Value Object)가 다른 객체와의 연관관계(References)를 직접 가질 수 있다. 이는 DB의 외래 키(FK) 참조를 넘어 객체 간의 협력 관계를 코드에 직접 반영할 수 있게 하여 도메인 로직을 더 명확하게 표현한다
- ORM(Object-Relational Mapping) 기술의 활용: JPA와 같은 ORM 기술은 풍성한 도메인 모델과 복잡한 DB 매핑(예: 다대다 관계, 상속 구조)을 효율적으로 연결해주는 중요한 도구이다. 이를 통해 개발자는 DB 매핑의 복잡성보다는 도메인 로직 모델링에 더 집중할 수 있다
도메인 로직의 API 개발: 애플리케이션 서비스의 역할
도메인 모델 패턴은 도메인의 핵심 로직을 잘 캡슐화하고 표현하는 데 강점이 있지만 최종 사용자가 시스템과 상호작용 하는 구체적인 작업 단위(Task-oriented)의 API를 제공하는 데는 한계가 있다
문제점
- 도메인 모델 자체는 특정 비즈니스 작업의 전체 흐름을 직접적으로 정의하기보다는 도메인 엔티티 그 로직에 집중한다
- 하나의 비즈니스 작업은 종종 여러 도메인 객체의 협력을 필요로 하거나 외부 시스템(예: 결제 시스템, 메일 발송 서비스)과 연동을 포함할 수 있다
- 이러한 작업 흐름을 도메인 모델 내에 직접 구현하면 도메인 모델이 너무 비대해지고 순수한 도메인 로직이 외부 기술과 혼재될 수 있다
해결책: 애플리케이션 서비스 (Application Service)
- 이러한 문제를 해결하기 위해 애플리케이션 서비스라는 계층을 도입한다. 애플리케이션 서비스는 외부(UI, API 클라이언드 등)의 요청을 받아 특정 비즈니스 작업을 명확한 작업 단위의 절차형 API 형태로 제공한다
- 역할: 애플리케이션 서비스는 직접적으로 복잡한 도메인 로직을 구현하기보다는 도메인 모델을 조율하고 사용하며 필요에 따라 외부 시스템과의 연동을 처리한다
- 특징: 따라서 애플리케이션 서비스는 대개 코드가 간결하고 얇다. 이는 도메인 로직의 핵심이 도메인 모델에 위치하고 있기 때문이다
- 관문 역할(Facade 패턴): 애플리케이션 서비스는 외부와 도메인 모델 사이의 관문(Facade) 역할을 수행한다. 외부 요청은 애플리케이션 서비스를 통해 도메인 모델의 기능에 접근하며, 이는 도메인 모델의 캡슐화를 유지하고 외부로의 정보 유출을 방지한다
3계층 아키텍처 (Three-Tier Architecture)
3계층 아키텍처는 서버 및 엔터프라이즈 애플리케이션 개발에서 가장 널리 사용되는 전통적인 아키텍처 스타일이다. 시스템의 주요 관심사는 세 개의 논리적은 계층으로 분리하여 구조화한다. 각 계층은 고유한 역할과 책임을 가지며 계층형 아키텍처의 원칙에 따라 단방향으로 통신한다
프레젠테이션 계층 (Presentation Layer / UI Layer)
- 역할: 사용자 인터페이스를 담당하며 사용자로부터 요쳥을 받아들이고 처리 결과를 사용자에게 보여주는 역할을 한다
- 관심사: 데이터의 시각화, 사용자 입력 처리, 세션 관리 등이 주 관심사이다
- 과거: 웹 서버에서 HTML 뷰를 생성하여 응답하는 방식이 일반적이었다
- 현재: JSON과 같은 데이터 형식으로 API를 제공하고 클라이언트(웹 브라우저, 모바일 앱 등)가 이를 받아 랜더링하는 SPA(Single Page Application)나 모바일 앱 환경이 보편화되어있다
도메인/비즈니스 계층 (Domain Layer / Business Layer)
- 역할: 애플리케이션의 핵심 비즈니스 로직을 포함한다. 사용자들의 활동과 관심사를 코드로 표현한 도메인 모델이 이 계층이 위치한다
- 관심사: 핵심 비즈니스 규칙 적용, 데이터 유효성 검사, 복잡한 로직 처리 등이 주 관심사이다
- 이름: 과거에는 주로 ‘비즈니스 로직 계층(Business Logic Layer)’이라고 불렸으나, 도메인 모델 중심 개발이 발전하면서 ‘도메인 계층(Domain Layer)’이라는 이름이 더 자주 사용되기 시작했다
인프라스트럭처/데이터 계층 (Infrastructure Layer / Persistence Layer / Data Layer)
- 역할: 데이터 저장 및 관리를 담당하고 외부 시스템과의 연동을 처리한다. 도메인 계층이 외부 기술에 오염되지 않도록 분리되었다
- 관심사: 데이터베이스 CRUD(생성, 읽기, 업데이트, 삭제)작업, 파일 시스템 접근, 외부 API 호출, 메시지 큐 연동 등 기술적인 인프라 관련 처리가 주 관심사이다.
- 이름: 과거에는 주로 데이터베이스와 관련된 ‘영속성 계층(Persistence Layer)’이라 불렸으나, 현재는 데이터베이스 외에도 다양한 외부 엔터프라이즈 서비스나 클라우드 기반 서비스(메일 발송, 메시징, 원격 시스템 연동 등)를 이용하는 경우가 많아져 ‘인프라스트럭처 계층(Infrastructure Layer)’이라는 이름이 더 포괄적으로 사용된다
3계층 아키텍처와 도메인 모델 패턴의 결합
도메인 계층에 비즈니스 로직을 구혀하는 방식은 크게 두 가지가 있다
트랜잭션 스크립트 (Transaction Script)
- 하나의 트랜잭션에 대한 모든 비즈니스 로직을 단일 함수나 스크립트로 집중시켜 처리하는 방식을 의미한다
- 비즈니스 로직을 절차적인 형식의 API(메서드)로 서비스 빈을 구현하는 방식이다
- 프레젠테이션 계층에서 이 API를 직접 호출하여 사용하기 용이하며, 스프링 개발에서 오랫동안 널리 사용되어 왔다
도메인 모델 패턴
- 풍성한 도메인 모델을 객체지향적으로 구현하는 방식이다
- 이 경우, 도메인 모델 자체는 구체적인 작업 단위의 API를 직접 제공하기 어렵다. 따라서 3계층 아키텍처 내에서 애플리케이션 서비스 계층을 추가하여 도메인 모델의 기능을 조율하고 작업 단위 API를 제공한다
- 개념적으로는 여전히 3계층이지만, 도메인 로직을 처리하는 부분이 ‘애플리케이션 서비스’와 ‘도메인 모델’로 분리되어 사실상 4계층과 유사한 구조가 될 수 있다
- 도메인 계층의 순수성 유지: 도메인 모델이 외부 인프라 기술(DB 접근, 외부 API 호출 등)에 오염되는 것을 방지하기 위해 이러한 기술적인 작업은 애플리케이션 서비스에서 직접 인프라 계층을 사용하도록 하거나, 도메인 모델이 의존하는 인터페이스를 인프라 계층으로 구현하도록 설게한다. 이렇게 함으로써 도메인 모델은 순수한 비즈니스 로직에 집중할 수 있다
완화된 3계층 아키텍처 (Relaxed Three-Tier Architecture)
전통적인 3계층 아키텍처는 각 계층이 바로 아래 계층만 사용하는 것을 이상적으로 보지만, 실제로는 상위 계층이 중간 계층을 건너뛰고 더 아래의 계층을 직접 사용하는 경우가 발생할 수 있다(예: 프레젠테이션 계층이 도메인 계층을 거치지 않고 직접 인프라 계층의 간단한 데이터를 조회). 이를 완화된 계층형 아키텍처라고 부르며, 단방향 의존성 원칙만 지킨다면 허용될 수 있다