한 줄 정의

서비스 기반 아키텍처

도메인 단위로 큼직하게 쪼갠 서비스들하나의 모놀리스 데이터베이스 를 공유하며 큰 덩어리 단위로 분산된 계층 구조를 이루는, 가장 실용적이고 가성비 좋은 분산 아키텍처입니다.

쉽게 말하면

마이크로서비스로 한 번에 가기는 부담스럽고, 모놀리스로는 더 못 버틸 때 선택하는 “분산은 하되 너무 잘게 쪼개지는 않은” 중간 형태입니다. 이커머스를 예로 들면, OrderService, CustomerService, InventoryService 정도로 도메인별 7~12개 서비스로 나누고, 데이터베이스는 한 덩어리로 유지합니다. 마이크로서비스의 “결제 실패 시 보상 트랜잭션을 어떻게 짤지”라는 분산 트랜잭션 지옥을 회피하면서도 “도메인별 독립 배포”라는 핵심 이점은 챙깁니다.

왜 이렇게 설계했는가?

  • 해결하는 문제: 모놀리스의 배포 단위 문제(작은 변경에도 전체 재배포)와 마이크로서비스의 복잡성·운영 비용 사이의 적절한 타협점 입니다. 도메인별로 따로 배포하고 싶지만, 분산 트랜잭션·데이터 동기화·서비스 간 통신 같은 마이크로서비스의 무거운 짐은 지고 싶지 않을 때의 정답입니다.
  • 이게 없다면: 모놀리스라면 결제 모듈 한 줄 고치자고 주문·재고·회원까지 묶인 거대한 WAR 파일을 통째로 재배포해야 합니다. 반대로 처음부터 마이크로서비스로 가면, 사용자 가입 한 번에 5개 서비스 호출 + Saga 패턴 + 서킷브레이커 + 분산 추적까지 갖춰야 해서 팀 규모 대비 운영 비용이 폭주합니다.

토폴로지

서비스 기반 아키텍처의 기본 토폴로지는 분리 배포된 UI, 분리 배포된 큼직한 도메인 서비스들, 하나의 모놀리스 데이터베이스(생략 가능) 로 구성된 큰 덩어리 단위의 분산 계층 구조 입니다. 원서 용어로는 distributed macro-layered structure라고 부릅니다.

flowchart TB
    UI[사용자 인터페이스]
    UI --> S1[도메인 서비스 1]
    UI --> S2[도메인 서비스 2]
    UI --> S3[도메인 서비스 3]
    UI --> S4[도메인 서비스 4]
    S1 --> DB[(모놀리스 데이터베이스)]
    S2 --> DB
    S3 --> DB
    S4 --> DB
도메인 서비스의 특징

이 스타일의 서비스는 보통 시스템 내 특정 도메인이나 하위 도메인을 대표하므로 도메인 서비스(domain service) 라고 부릅니다. 주문 이행, 주문 배송처럼 시스템 기능 중에서 꽤 큰 덩어리 를 통째로 담당합니다.

  • 도메인 서비스들은 대개 서로 독립적으로 동작하며 따로 배포됩니다.
  • 컨테이너화가 필수는 아닙니다(필요하면 도커·쿠버네티스로 배포 가능).
  • 단일 모놀리스 DB를 쓰는 경우 도메인 서비스 수를 12개 이하 로 권장합니다. 변경 관리·확장성·내결함성 문제를 방지하기 위해서입니다.
  • 일반적으로 각 도메인 서비스는 단일 인스턴스 로 배포됩니다. 처리량·내결함성 요구가 높으면 인스턴스를 여러 개 두고 UI와 도메인 서비스 사이에 부하 분산을 둡니다.
UI → 서비스 호출

UI는 보통 REST 같은 원격 접근 프로토콜로 서비스를 호출합니다. 메시징, RPC, 프록시 또는 게이트웨이 API 계층, SOAP 등 다른 호출 방법도 가능합니다. UI에 서비스 로케이터 패턴 을 적용해 UI가 직접 서비스를 찾아 호출하게 하는 경우가 많고, 서비스 로케이터를 API 게이트웨이나 프록시에 내장하는 것도 가능합니다.

핵심 내용

도메인 서비스의 내부 구조

도메인 서비스는 한 덩어리가 크기 때문에 그 안을 다시 정리할 필요가 있습니다. 보통 다음 두 가지 방식으로 내부를 설계합니다.

계층형 설계 (기술적 분할)

각 도메인 서비스를 API 퍼사드(facade) 계층 + 비즈니스 로직 계층 + 영속성 계층 으로 구성된 계층형 아키텍처로 설계합니다.

flowchart TB
    subgraph DS["도메인 서비스"]
        L1[API 퍼사드 계층]
        L2[비즈니스 로직 계층]
        L3[영속성 계층]
        L1 --> L2 --> L3
    end
도메인 설계 (도메인 분할)

각 도메인 서비스를 하위 도메인별로 분할해서 컴포넌트로 만듭니다. 모듈형 모놀리스와 유사한 구조입니다.

flowchart TB
    subgraph DS["도메인 서비스"]
        F[API 퍼사드 계층]
        F --> C1[컴포넌트]
        F --> C2[컴포넌트]
        F --> C3[컴포넌트]
        F --> C4[컴포넌트]
    end

API 퍼사드는 필수

어떤 설계 방식을 쓰든 UI가 비즈니스 기능성을 실행하려면 UI와 상호작용하는 API 접근 퍼사드 를 도메인 서비스에 반드시 포함해야 합니다. API 접근 퍼사드는 주로 UI의 비즈니스 요청 흐름을 조율(orchestration) 하는 역할을 담당합니다.

데이터 무결성과 일관성 (ACID vs BASE)

도메인 서비스는 한 단위가 크기 때문에 표준적인 데이터베이스 커밋·롤백에 대해 ACID 트랜잭션 이 지원됩니다. 단일 도메인 서비스 안에서 데이터베이스 무결성이 보장됩니다.

반면 마이크로서비스처럼 극도로 분산된 아키텍처에서는 BASE(basic availability, soft state, eventual consistency; 기본 가용성, 소프트 상태, 최종 일관성) 트랜잭션 같은 분산 트랜잭션 기법이 쓰입니다.

결제 실패 시나리오 비교

전자상거래에서 신용카드 만료로 결제가 거절되는 경우를 비교해보면 차이가 분명해집니다.

flowchart TB
    subgraph SBA["서비스 기반 아키텍처 (ACID)"]
        direction LR
        A1[주문 시도] --> A2[OrderService]
        A2 -->|결제 거절| A3[표준 트랜잭션 롤백]
        A3 --> A4[고객에게 알림]
    end
    subgraph MS["마이크로서비스 (BASE)"]
        direction LR
        B1[주문 시도] --> B2[OrderPlacement]
        B2 -->|주문 등록| B3[(주문 DB)]
        B2 --> B4[PaymentService]
        B4 -->|결제 실패| B5[보상 갱신<br/>compensating update]
        B5 --> B6[OrderPlacement에서<br/>주문 취소]
    end

서비스 기반에서는 결제 처리가 하나의 서비스 안에서 원자적(atomic) 트랜잭션으로 묶이므로, 지금까지 데이터베이스에 추가된 모든 내역을 표준 트랜잭션 롤백으로 되돌립니다.

마이크로서비스에서는 OrderPlacement 서비스가 주문을 먼저 등록한 뒤 원격으로 PaymentService 를 호출합니다. 결제가 실패하면 주문은 등록됐는데 결제는 실패한 데이터 일관성이 깨진 상태 가 되어, 별도의 보상 갱신(compensating update) 을 추가로 적용해야 합니다.

마이크로서비스와의 결정적 차이: 조율의 위치

앞의 결제 실패 시나리오에서 두 아키텍처가 본질적으로 어디서 갈리는지를 정리하면 “조율(orchestration)을 어디서 하느냐” 한 줄로 요약됩니다.

전자상거래에서 고객이 상품을 주문하는 요청은 OrderService 도메인 서비스 안의 API 접근 퍼사드에 전달됩니다. 퍼사드는 서비스 내부에서 주문 등록, 주문 ID 생성, 결제 적용, 품목별 재고 갱신을 모두 조율합니다.

마이크로서비스라면 같은 요청을 개별적으로 배포된 여러 단일 목적 서비스들이 서비스 외부에서(네트워크 너머로) 상호 조율해야 합니다.

구분서비스 기반마이크로서비스
조율 위치도메인 서비스 내부 (클래스 수준)도메인 서비스 외부 (서비스 간)
호출 비용함수 호출 (in-process)네트워크 호출
트랜잭션ACID 가능BASE/Saga 필요

조율을 내부에서 하면 ACID와 함수 호출의 단순함을 얻고, 외부에서 하면 독립 배포와 잘게 쪼갠 단위의 확장성을 얻습니다. 이 단위 크기의 차이가 두 아키텍처 스타일의 가장 큰 분기점 입니다.

단위가 큰 설계의 트레이드오프

도메인 서비스는 한 단위가 큰 덕분에 데이터 무결성과 일관성을 효과적으로 유지할 수 있지만 그만큼 큰 대가 가 따릅니다.

항목서비스 기반마이크로서비스
변경 영향 범위OrderService 의 주문 처리만 바꿔도 결제 처리 등 모든 기능 테스트·재배포 필요잘게 쪼갠 OrderPlacement 만 변경. PaymentService 는 영향 없음
변경 위험한 서비스에 많은 기능성이 모여 있어 다른 기능에 영향 미칠 위험 높음각 서비스가 한 가지 책임만 짊어져 부작용 가능성 적음

큰 단위로 묶어 ACID를 얻은 대가로 변경의 폭발 반경(blast radius)이 넓어진다 는 트레이드오프입니다.

사용자 인터페이스 옵션들

서비스 기반 아키텍처는 다양한 UI 변형을 허용합니다. 이게 이 스타일의 유연성을 높여주는 큰 이유 중 하나입니다.

변형 1: 단일 모놀리스 UI
flowchart TB
    UI[사용자 인터페이스]
    UI --> S1[도메인 서비스]
    UI --> S2[도메인 서비스]
    UI --> S3[도메인 서비스]
    UI --> S4[도메인 서비스]
    S1 --> DB[(데이터베이스)]
    S2 --> DB
    S3 --> DB
    S4 --> DB
변형 2: 도메인 기반 UI

도메인 영역 단위로 UI를 둘로(혹은 N개로) 분리합니다.

flowchart TB
    UI1[사용자 인터페이스 A]
    UI2[사용자 인터페이스 B]
    UI1 --> S1[도메인 서비스]
    UI1 --> S2[도메인 서비스]
    UI2 --> S3[도메인 서비스]
    UI2 --> S4[도메인 서비스]
    S1 --> DB[(데이터베이스)]
    S2 --> DB
    S3 --> DB
    S4 --> DB
변형 3: 서비스 기반 UI

도메인 서비스마다 별도의 UI를 둡니다. 시스템의 확장성·내결함성·민첩성이 가장 높아집니다.

flowchart TB
    UI1[UI 1] --> S1[도메인 서비스]
    UI2[UI 2] --> S2[도메인 서비스]
    UI3[UI 3] --> S3[도메인 서비스]
    UI4[UI 4] --> S4[도메인 서비스]
    S1 --> DB[(데이터베이스)]
    S2 --> DB
    S3 --> DB
    S4 --> DB

전형적인 주문 시스템에는 고객이 주문을 입력하는 UI, 포장 담당자용 UI, 고객 지원용 UI 처럼 사용자 그룹별로 별도 UI가 있을 수 있습니다.

API 게이트웨이 옵션들

UI와 도메인 서비스 사이에 역방향 프록시(reverse proxy)API 게이트웨이 로 구성된 API 계층을 추가할 수 있습니다.

flowchart TB
    UI[사용자 인터페이스]
    UI --> GW[API 계층<br/>프록시 또는 게이트웨이]
    GW --> S1[도메인 서비스]
    GW --> S2[도메인 서비스]
    GW --> S3[도메인 서비스]
    GW --> S4[도메인 서비스]
    S1 --> DB[(데이터베이스)]
    S2 --> DB
    S3 --> DB
    S4 --> DB

이 구성은 다음과 같은 경우에 유용합니다.

  • 도메인 서비스 기능을 외부 시스템에 노출 해야 할 때
  • 공통 횡단 관심사들(지표 측정, 보안, 감사, 서비스 디스커버리 등)을 모아서 게이트웨이로 옮길 때
  • 인스턴스가 여러 개인 서비스의 부하 분산 이 필요할 때

데이터 토폴로지

서비스 기반 아키텍처는 분산 아키텍처이면서도 모놀리스 데이터베이스를 효과적으로 지원 한다는 점에서 독특합니다. 필요하면 단일 모놀리스 DB를 다수의 데이터베이스로 분리할 수 있고, 심지어 개별 도메인 서비스에 도메인 범위의 데이터베이스를 두는 것도 가능합니다(마이크로서비스와 비슷한 방식).

데이터베이스 토폴로지 변형
flowchart TB
    subgraph V1["단일 모놀리스 DB"]
        direction TB
        s1[서비스] & s2[서비스] & s3[서비스] & s4[서비스] --> db1[(공유 DB)]
    end
    subgraph V2["부분 분리"]
        direction TB
        s5[서비스] --> db2[(전용 DB)]
        s6[서비스] & s7[서비스] & s8[서비스] --> db3[(공유 DB)]
    end
    subgraph V3["도메인별 분리"]
        direction TB
        s9[서비스] --> db4[(DB)]
        s10[서비스] --> db5[(DB)]
        s11[서비스] --> db6[(DB)]
        s12[서비스] --> db7[(DB)]
    end

데이터베이스 호출보다 데이터 공유

데이터베이스를 여러 개로 사용하는 경우 한 도메인 서비스를 위한 DB의 데이터를 다른 도메인 서비스가 필요로 하는 일이 없는지 확인해야 합니다. 그런 일이 벌어지면 도메인 서비스 사이에 상호 통신이 발생합니다. 일반적으로 이 아키텍처에서는 도메인 서비스가 다른 도메인 서비스를 호출하는 것보다는 그냥 데이터를 공유하는 것이 낫습니다.

엔티티 객체 공유 라이브러리: 안티패턴과 해결책

데이터베이스 테이블 스키마를 나타내는 공유 클래스 파일(이른바 엔티티 객체)은 보통 모든 도메인 서비스가 사용하는 커스텀 공유 라이브러리(JAR, DLL 파일 등)에 포함됩니다.

안티패턴: 단일 공유 라이브러리에 모든 엔티티를 담는 방식

flowchart TB
    UI[사용자 인터페이스]
    UI --> S1[서비스]
    UI --> S2[서비스]
    UI --> S3[서비스]
    UI --> S4[서비스]
    S1 -.- L1["single_shared_lib<br/>(모든 엔티티)"]
    S2 -.- L2["single_shared_lib<br/>(모든 엔티티)"]
    S3 -.- L3["single_shared_lib<br/>(모든 엔티티)"]
    S4 -.- L4["single_shared_lib<br/>(모든 엔티티)"]
    S1 --> DB[(데이터베이스 + 스키마)]
    S2 --> DB
    S3 --> DB
    S4 --> DB

단일 공유 라이브러리의 함정

데이터베이스 테이블 구조를 변경하면 그에 대응되는 엔티티 객체 라이브러리도 바꿔야 하며, 그러면 해당 테이블에 접근하지 않는 서비스들까지 모두 갱신·재배포 해야 합니다. 공유 라이브러리 버전 관리 기법으로 어느 정도 완화할 수 있지만, 어느 서비스가 테이블 변경의 영향을 받는지는 결국 사람이 수작업으로 분석 해야 알 수 있습니다.

해결책: 데이터베이스 논리적 분할 + 분할별 공유 라이브러리

flowchart TB
    UI[사용자 인터페이스]
    UI --> S1[서비스 A]
    UI --> S2[서비스 B]
    UI --> S3[서비스 C]
    UI --> S4[서비스 D]
    S1 -.- L1["customer_entities_lib<br/>common_entities_lib"]
    S2 -.- L2["invoicing_entities_lib<br/>common_entities_lib"]
    S3 -.- L3["order_entities_lib<br/>customer_entities_lib<br/>common_entities_lib"]
    S4 -.- L4["tracking_entities_lib<br/>common_entities_lib"]
    S1 --> DB
    S2 --> DB
    S3 --> DB
    S4 --> DB
    DB[("데이터베이스<br/>tracking | order | invoicing<br/>customer | common")]

특정 논리적 도메인(예: Invoicing)의 테이블에 변화가 생긴 경우 해당 엔티티 객체의 공유 라이브러리만(필요시 SQL도 포함) 바꾸면 됩니다. 그 변경은 해당 공유 라이브러리를 사용하는 서비스들에만 영향을 주고, 다른 서비스들은 다시 테스트·재배포할 필요가 없습니다.

common 라이브러리는 여전히 위험

common_entities_lib 같은 공통 라이브러리는 모든 서비스가 사용하므로, 이 테이블들을 변경하면 모든 서비스를 조율해야 합니다.

공통 엔티티 잠금 전략

공통 테이블 변경의 파급 효과를 줄이는 방법은 버전 관리 시스템에서 공통 엔티티 객체 파일을 잠그고 데이터베이스 팀만 변경할 수 있게 하는 것 입니다(버전 관리 시스템을 사용한다고 할 때). 변경 관리가 수월해지고 모든 서비스가 사용하는 공통 테이블 변경의 중요성이 강조됩니다.

데이터 거버넌스 원칙

서비스 기반 아키텍처에서 데이터베이스 변경 사항을 더 잘 관리하려면 데이터베이스를 가능한 한 세밀하게 논리적으로 분할하되, 데이터 도메인들은 잘 정의된 상태로 명확하게 유지 해야 합니다.

클라우드 환경 고려 사항

서비스 기반 스타일은 분산 아키텍처이므로, 비록 도메인 서비스들이 대체로 한 단위가 크지만 클라우드 환경과 잘 어울립니다.

  • 도메인 서비스는 범위가 넓기 때문에 서버리스 함수보다는 컨테이너화 해서 구현하는 것이 일반적입니다.
  • 클라우드 파일 저장소, 데이터베이스, 메시징 서비스 등을 수월하게 활용할 수 있습니다.

일반적인 위험

도메인 서비스 사이의 과도한 통신

도메인은 독립적이어야 한다

마이크로서비스 아키텍처에서는 서비스 사이의 통신이 흔하지만, 서비스 기반 아키텍처에서는 가급적 이를 피하려 합니다. 이상적으로 도메인은 독립적이어야 하고 결합은 데이터베이스 수준에서만 발생해야 합니다.

도메인 서비스 사이에 통신이 많다는 것은 아키텍트가 도메인 분할을 잘못했거나 주어진 문제에 이 아키텍처 스타일이 적합하지 않음을 강력하게 시사하는 신호입니다.

도메인 서비스를 너무 많이 만드는 것

12개의 마법 숫자

또 다른 일반적 위험은 도메인 서비스를 너무 많이 만드는 것입니다. 실용적인 관점에서 하나의 시스템에 적합한 최대 도메인 서비스 개수는 약 12개 정도 입니다. 이를 초과하면 테스트, 배포, 모니터링, 데이터베이스 연결 및 변경 관리에서 문제가 계속 발생합니다.

거버넌스

순환 복잡성, 확장성, 반응성 등 일반적인 구조적·운영적 거버넌스 외에, 서비스 기반 아키텍처의 구조적 완결성 을 보장하기 위한 좀 더 특화된 거버넌스 테스트들이 있습니다.

도메인 독립성 검증

이 스타일에서 가장 먼저 챙겨야 할 점은 변경이 여러 도메인 서비스에 걸쳐 발생하지 않도록 하는 것 입니다. 하나의 변경이 여러 도메인 서비스에 영향을 끼친다는 것은 두 가지 중 하나를 시사합니다.

  1. 도메인 경계가 적절하게 정의되어 있지 않다
  2. 서비스 기반 아키텍처가 해당 문제에 적합한 스타일이 아니다
통신량 관리

서비스 간 통신이 불가피한 경우에는 도메인 서비스들 사이의 통신량을 관리 하는 데 초점을 두어야 합니다. 예를 들어 OrderProcessing(주문 처리) 도메인이 주문 상태 정보를 고객에게 이메일로 보내기 위해 CustomerNotification(고객 알림) 도메인과 정보를 교환해야 할 수 있습니다.

오케스트레이션은 위로 올린다

대부분의 경우 도메인 서비스들이 서로 독립적으로 작동하게 만들고, 오케스트레이션은 UI 또는 API 게이트웨이 레벨에서 이루어지도록 설계해야 합니다.

팀 토폴로지 고려 사항

서비스 기반 아키텍처는 도메인 단위로 분할 되므로, 팀들도 도메인 영역별로 정렬(alignment)되어 있을 때 가장 효과적입니다. 각 도메인을 전담하는 전문성을 갖춘 교차 기능 팀(CFT) 을 두면 좋습니다. 기술적으로 분할된 팀들(UI 팀, 백엔드 팀, 데이터베이스 팀 등)과는 잘 맞지 않습니다.

팀 유형적합도이유
스트림 정렬 팀 (기능 전담 팀)높음도메인 경계가 적절히 정렬되어 있다면 잘 어울립니다. 각 스트림이 특정 하나의 도메인에 초점을 둔다면 특히 그렇습니다. 여러 도메인 서비스로 정의된 경계들을 넘나드는 스트림이 존재하면 적용이 어려워지므로, 그럴 때는 도메인 경계와 서비스를 어떤 단위로 쪼갤지를 다시 분석해 스트림에 맞게 재조정하거나 다른 아키텍처 스타일을 선택해야 합니다
활성화 팀 (역량 코칭 팀)낮음도메인 서비스의 한 단위가 크기 때문에 활성화 팀 토폴로지와의 조합은 그리 효과적이지 않습니다. 단, 각 도메인 서비스 안의 컴포넌트들을 세심하게 식별해서 모듈성을 높이면 전문 인력이나 교차 기능 팀원이 그 컴포넌트들에 기반해서 무언가를 제안하거나 실험을 수행할 수 있습니다
난해한 하위시스템 팀 (전문 도메인 팀)높음이 아키텍처의 도메인 및 하위 도메인 수준 모듈성이 난해한 하위시스템 팀에 도움이 됩니다. 다른 팀이나 서비스와는 독립적으로 난해한 도메인 또는 하위 도메인 처리에 집중할 수 있습니다
플랫폼 팀 (공통 기반 팀)높음모듈성이 높으므로, 다른 팀들은 플랫폼 팀이 제공하는 공통의 도구와 서비스, API, 작업들을 활용함으로써 플랫폼 팀 토폴로지의 혜택을 누릴 수 있습니다

장단점

아키텍처 특성평가
전반적인 비용낮음 ($$)
분할 방식도메인
퀀텀 개수1 이상
단순성★★★☆☆
모듈성★★★☆☆
유지보수성★★★★☆
테스트성★★★★☆
배포성★★★★☆
진화성★★★★☆
반응성★★★☆☆
확장성★★★☆☆
탄력성★★☆☆☆
내결함성★★★★☆

별 5개 항목은 없지만 여러 핵심 분야에서 별 4개를 받은 만큼, 전반적인 평가는 상당히 좋은 편입니다. 유연성, 적절한 가용성, 빠른 출시 속도 가 종합적인 강점입니다.

민첩성·테스트성·배포성 ★★★★☆

애플리케이션을 도메인별로 분할해서 독립적으로 배포하므로 변경이 더 빠르고(민첩성), 도메인 범위에 기반한 모듈성 덕분에 테스트 커버리지도 좋습니다(테스트성). 게다가 모놀리스 아키텍처에 비해 배포가 더 잦고 위험이 적습니다(배포성). 이 세 특성 덕분에 제품을 좀 더 일찍 출시할 수 있고 새 기능 제공·버그 수정도 빨라집니다.

내결함성·전체 애플리케이션 가용성 ★★★★☆

도메인 서비스의 한 단위가 꽤 큼에도 별이 네 개입니다. 이는 대체로 서비스들이 자기완결적이고, 코드 및 데이터베이스 공유 덕분에 서비스 간 통신이 필요 없기 때문 입니다. 그래서 도메인 서비스 하나가 중단된다 해도 나머지 서비스들은 영향을 받지 않습니다. 고잉 그린의 경우 Receiving 서비스가 다운돼도 다른 여섯 서비스는 정상 작동합니다.

확장성 ★★★☆☆ / 탄력성 ★★☆☆☆

서비스 한 단위가 크기 때문에 평가가 그다지 높지 않습니다. 프로그래밍적 확장성과 탄력성을 이 스타일에서도 확보할 수 있지만, 마이크로서비스처럼 더 잘게 쪼갠 아키텍처보다는 기능성이 더 많이 중복 됩니다. 그만큼 비용 효율이나 자원 활용 면에서 불리합니다.

일반적으로 서비스 기반 아키텍처에서는 각각의 서비스에 대해 인스턴스가 하나만 유지 됩니다. 처리량이나 장애 조치(failover) 요구가 높다면 다수의 인스턴스를 둘 수 있습니다. 단일 인스턴스 서비스에서는 단일 메모리 내 캐싱(in-memory caching) 이나 데이터베이스 연결 풀링 을 쉽게 활용할 수 있습니다.

단순성·전체 비용

이 아키텍처 스타일의 강점입니다. 마이크로서비스나 이벤트 기반 아키텍처 같은 더 비싸고 복잡한 분산 아키텍처들과 확실히 대조되는 측면이고, 심지어는 공간 기반 아키텍처 보다도 간단하고 저렴합니다. 구현이 쉽고 비용 효율이 가장 높은 분산 아키텍처 중 하나 로 간주됩니다.

트레이드오프 정리

Quote

별 4개로 평가된 특성들(확장성, 탄력성, 내결함성 등)을 개선하려면 비용과 복잡성이 높아져야 합니다. 유연성이 뛰어나고 별 3~4개짜리 아키텍처 특성이 여러 개라는 점에서 매우 실용적인 스타일입니다.

ACID 친화성

분산 아키텍처에서는 데이터베이스 트랜잭션의 유지와 조정이 항상 골칫거리입니다. 분산 아키텍처들이 ACID 트랜잭션 대신 최종 일관성(eventual consistency) 에 의존하기 때문입니다. 하지만 서비스 기반 아키텍처는 한 서비스의 단위가 크기 때문에 다른 분산 아키텍처들보다 ACID를 활용하기가 수월합니다. 트랜잭션 범위가 각 도메인 서비스 단위로 정해지므로, 대부분의 모놀리스 애플리케이션에서 볼 수 있는 커밋 후 롤백(commit-and-rollback) 트랜잭션 기능성을 그대로 적용할 수 있습니다.

적합한 상황

사용하면 좋은 경우

  • 도메인 분할은 필요하지만 마이크로서비스의 운영 복잡도는 감당하기 어려운 경우: 가장 흔한 적용 시나리오
  • 다른 분산 아키텍처로 가기 전 디딤돌(stepping stone)이 필요할 때: 기존 모놀리스를 마이크로서비스로 옮기는 중간 단계
  • ACID 트랜잭션을 유지하면서 분산 배포의 이점이 필요할 때: 결제·청구처럼 강한 일관성이 중요한 도메인
  • 빠른 출시·민첩성·테스트성·배포성 이 핵심 가치인 비즈니스 애플리케이션
  • 시간과 예산이 제한된 분산 시스템 도입

사용하지 말아야 할 경우

  • 도메인 서비스가 12개를 넘어가는 큰 시스템
  • 도메인 간 통신이 본질적으로 많은 시스템: 도메인 분할이 안 맞는 신호
  • 잘게 쪼갠 단위의 확장성·탄력성 이 결정적으로 중요한 시스템 (마이크로서비스나 공간 기반이 더 적합)
  • 도메인 서비스 단위가 너무 커서 변경 영향 범위 자체가 비즈니스 요구를 못 따라가는 경우

예시

고잉 그린 전자제품 재활용 애플리케이션

flowchart TB
    subgraph 외부고객["외부 고객 대상 퀀텀"]
        UI1[고객 사용자 인터페이스<br/>웹과 키오스크]
        UI1 --> Q[견적 서비스]
        UI1 --> IS[품목 상태 서비스]
        Q --> DB1[(고객 대상 DB)]
        IS --> DB1
    end
    subgraph 내부운영["내부 운영 퀀텀"]
        UI2[수령 UI]
        UI3[재활용 및 회계 UI]
        UI2 --> RC[수령 서비스]
        UI2 --> AS[평가 서비스]
        UI3 --> RY[재활용 서비스]
        UI3 --> AC[회계 서비스]
        UI3 --> RP[보고 서비스]
        RC & AS & RY & AC & RP --> DB2[(내부 운영 DB)]
    end
    DB2 -.->|데이터베이스 미러링| DB1
처리 흐름
단계도메인 영역설명
1견적(quoting)고객이 웹사이트나 키오스크 통해 중고 기기 견적 문의
2수령(receiving)견적 만족 시 고객이 기기를 회사로 발송
3평가(assessment)기기 상태 평가·감정
4회계(accounting) / 품목 상태(item status)정상 작동 시 고객에게 대금 지급. 고객은 웹사이트로 상태 조회
5재활용(recycling)부품 안전 분해·재활용 또는 외부 판매 플랫폼(eBay, 페이스북 마켓플레이스)에 재판매
6보고(reporting)재활용 활동 재무·운영 보고서 주기적 작성
두 개의 퀀텀과 보안 강화

UI 애플리케이션을 고객 대상, 수령, 재활용 및 회계 라는 세 도메인으로 분리하고 데이터베이스를 외부 고객용/내부 운영용 두 개로 나눕니다.

이런 구조에서는 다음 효과를 얻을 수 있습니다.

  • 내부 데이터·연산을 외부와 분리된 네트워크 존(zone) 에 배치 → 보안 접근 제어와 데이터 보호 강화
  • 개별적인 아키텍처 퀀텀 형성
  • 내부 서비스는 고객 대상 정보 접근·갱신 가능, 방화벽 때문에 그 반대 방향 접근은 불가능
  • 데이터베이스 미러링 또는 외부 테이블 동기화로 두 데이터베이스 동기화
변경 빈도 격리

Assessment(평가) 서비스도 시장 변화(특정 제품 단종, 새 제품 출시)에 따라 자주 갱신해야 하는데, 서비스 기반에서는 이런 빈번한 변경이 하나의 도메인 서비스에만 국한 됩니다. 이 덕분에 민첩성·테스트성·배포성이 확보됩니다.

디딤돌 전략

서비스 기반 아키텍처는 다른 분산 아키텍처로 넘어가는 ‘디딤돌’ 로 삼기에 좋은 아키텍처입니다. 조직이 다른 분산 아키텍처 스타일로 이전(마이그레이션)하든, 새로운 분산 시스템을 밑바닥부터(from scratch) 만들든, 서비스 기반 아키텍처를 중간 단계로 삼으면 좋습니다.

마크 리처즈

애플리케이션의 모든 부분이 마이크로서비스일 필요는 없다.

목표가 마이크로서비스인 경우, 마이크로서비스에 기반한 시스템을 바로 구축하는 대신 기존 시스템을 서비스 기반 아키텍처로 전환(또는 새로 구축)하면서 도메인을 분석해 보면 아키텍처에서 반드시 마이크로서비스이어야 하는 부분과 그렇지 않은 부분 을 명확히 파악할 수 있습니다.

고잉 그린의 예에서 Recycling 이나 Accounting 은 더 쪼갤 필요 없이 도메인 서비스로 그대로 남기고, 변경이 잦고 높은 민첩성이 요구되는 Assessment 만 전자기기별 마이크로서비스로 세분화하는 식입니다. 이 단계를 생략하고 곧장 마이크로서비스로 가면 마이크로서비스가 될 필요가 없는 것까지 모두 잘게 쪼개는 우를 범하기 쉽습니다.

비교 / 트레이드오프

스타일공통점차이점
Ch11 모듈형 모놀리스 아키텍처도메인 분할, ACID 트랜잭션 활용 가능모듈형 모놀리스는 단일 배포. 서비스 기반은 도메인별 독립 배포로 민첩성·내결함성 ↑
Ch13 마이크로커널 아키텍처모듈성 추구마이크로커널은 코어-플러그인의 비대칭 모놀리스. 서비스 기반은 동등한 도메인 서비스들의 분산 배치
Ch15 이벤트 주도 아키텍처분산 아키텍처이벤트 주도는 비동기 메시징 기반. 서비스 기반은 동기 호출(REST 등) 중심, 단순·저렴
Ch16 공간 기반 아키텍처분산 아키텍처공간 기반은 메모리 그리드 기반의 극단적 확장성. 서비스 기반은 단순·저렴함이 강점
Ch18 마이크로서비스 아키텍처도메인 분할, 분산 배포서비스 기반은 마이크로서비스의 단순화된 절충안. 한 서비스의 단위가 크고 DB 공유, ACID 가능, 운영 비용 훨씬 낮음

내 생각

  • 실무에서 가장 강력한 가치는 “마이크로서비스로 가지 않을 권리” 입니다. 컨퍼런스 발표를 들으면 다 마이크로서비스로 가야 할 것 같지만, 712명짜리 팀이 712개 마이크로서비스를 운영하면서 분산 추적, Saga, 서킷브레이커, 멀티 DB 마이그레이션을 동시에 감당하기는 거의 불가능합니다. 서비스 기반은 “DDD 바운디드 컨텍스트는 지키되 DB는 같이 쓰자”는 아주 합리적인 절충안입니다.
  • “호출보다는 데이터 공유가 낫다” 는 원칙이 핵심입니다. 마이크로서비스 마인드에 익숙해진 개발자가 서비스 기반에 들어오면 자기도 모르게 도메인 서비스 사이에 REST 호출을 만듭니다. 이 순간 ACID도 깨지고, 분산 트랜잭션 비용은 마이크로서비스만큼 비싸지면서 마이크로서비스의 독립성 이점은 못 누리는 최악의 하이브리드 가 됩니다. ArchUnit 같은 도구로 “도메인 서비스끼리 직접 호출 금지” 규칙을 강제하는 게 좋습니다.
  • 한국 SI/금융권에 가장 잘 맞는 스타일이라고 봅니다. 도메인 단위 ACID 트랜잭션이 살아있고, DBA 조직의 거버넌스가 여전히 강하며(공통 엔티티 잠금이 자연스러움), 12개 이하의 도메인 서비스 규모는 한국 기업의 보편적인 백엔드 팀 규모와 정확히 일치합니다.
  • common_entities_lib 는 거의 항상 시한폭탄 입니다. 처음에는 “공통이니까 한 번만 정의하자”로 시작하지만, 시간이 지나면 거의 모든 테이블이 “어딘가에선 공통”이라는 이유로 common에 끌려 들어오고, 결국 common 변경 한 번에 전체 서비스가 재배포됩니다. 버전 관리 시스템 잠금 + PR 리뷰어를 데이터베이스 팀으로 강제 지정 하는 거버넌스가 거의 필수입니다.
  • 서비스 기반 → 마이크로서비스로 가는 점진적 전환 의 실효성이 큽니다. “전체를 마이크로서비스로 가야 한다”가 아니라, “변경이 가장 잦은 한두 도메인만 더 잘게 쪼개자”라는 결정이 가능해지는 게 이 아키텍처의 진짜 힘입니다. Strangler Fig 패턴과 결합하면 위험을 분산할 수 있습니다.

더 알아볼 것

  • Spring Boot 멀티 모듈 프로젝트로 도메인 서비스 단위 패키징 구성하기
  • ArchUnit으로 “도메인 서비스 간 직접 호출 금지” 규칙 강제하기
  • Flyway/Liquibase로 도메인별 스키마 마이그레이션 분리 관리
  • 서비스 기반 → 마이크로서비스 전환 시 첫 번째로 분리할 도메인을 어떻게 식별하는지 (변경 빈도, 트래픽 패턴 기준)
  • API Gateway(Kong, AWS API Gateway, Spring Cloud Gateway)로 서비스 로케이터 + 횡단 관심사 처리
  • 단일 DB에서 도메인별 스키마 분리 거버넌스를 ERD/PR 리뷰 단계에서 자동화하는 방법

관련 개념

출처

  • 마크 리처즈, 닐 포드, 『소프트웨어 아키텍처 The Basics』, 14장 서비스 기반 아키텍처 스타일