[번역글] MSA 서비스별 데이터베이스 패턴(Database per service pattern)
해당 포스팅은 해당 게시글을 번역한 포스팅임을 명시합니다.
서론
마이크로서비스 아키텍처 패턴을 사용하여 온라인 스토어 애플리케이션을 개발한다고 가정해 봅시다. 대부분의 서비스들은 하나의 종류의 데이터베이스에 데이터를 저장해야 합니다. 예를 들어, Order Service는 Order에 대한 데이터를 저장하고 Customer Service는 Customer에 대한 데이터를 저장합니다.
Problem
마이크로서비스 애플리케이션의 데이터베이스 아키텍처는 무엇입니까?
Forces
- 서비스는 독립적으로 개발, 배포 및 확장될 수 있도록 느슨한 결합이 필요합니다.
- 일부 비즈니스 트랜잭션은 여러 서비스에 걸쳐 처리되도록 불변성을 적용해야 합니다. 예를 들어, Place Order 유스케이스에서는 새로운 주문이 발생할 때, 고객의 신용 한도를 초과하지 않는지 확인해야 합니다. 다른 비즈니스 트랜잭션은 여러 서비스가 소유한 데이터를 업데이트 해야 합니다.
- 일부 비즈니스 트랜잭션은 여러 서비스가 소유한 데이터를 쿼리해야 합니다. 예를 들어, 사용 가능한 크레딧을 확인하기 위한 예제에서는 고객을 쿼리하여 크레딧 한도를 찾고 주문을 확인하여 미결제 주문의 총액을 확인하여 계산하는 과정이 필요합니다.
- 일부 쿼리는 여러 서비스가 소유한 데이터를 조인해야 합니다. 예를 들어, 특정 지역의 고객과 최근 주문을 찾기 위해서는 고객과 주문을 조인해야 합니다.
- 확장을 위해 데이터베이스를 복제하고 샤딩해야하는 경우가 있습니다.
- 서비스마다 데이터 저장 요구사항이 다를 수 있습니다. 어떤 서비스는 관계형 데이터베이스가 최선의 선택일 수 있고, 다른 서비스에는 복잡하고 구조화되지 않은 데이터를 저장하는데 능숙한 MongoDB 혹은 그래프 데이터를 효율적으로 저장하고 쿼리하도록 설계된 Neo4J같은 NoSQL 데이터베이스가 필요할 수 있습니다.
Solution
각 마이크로 서비스의 영구 데이터를 해당 서비스에 대해 private하게 유지하고 해당 API를 통해서만 엑세스할 수 있습니다. 서비스의 트랜잭션에는 해당 데이터베이스만 포함됩니다.
아래의 다이어그램은 이 패턴의 구조를 보여줍니다.
서비스의 데이터베이스는 사실상 해당 서비스 구현의 일부입니다. 다른 서비스에서 직접 엑세스할 수 없습니다. 서비스의 영구 데이터를 private 하게 유지하는 몇가지 방법들이 있습니다. 각 서비스에 대해서 데이터베이스 서버를 프로비저닝할 필요가 없습니다.
예를 들어, 관계형 디비를 사용하는 경우 아래와 같은 선택사항이 있습니다.
- 서비스 별 private 테이블 : 각 서비스는 해당 서비스에서만 엑세스해야 하는 테이블 집합을 가집니다.
- 서비스 별 스키마 : 각 서비스에는 해당 서비스에 대한 전용 데이터베이스 스키마가 있습니다.
- 서비스 당 데이터베이스 서버 : 각 서비스에는 자체 데이터베이스 서버가 있습니다.
서비스 별 private 테이블 및 서비스 별 스키마는 오버헤드가 낮은 편에 속합니다. 서비스별 스키마를 사용하면 소유권이 더 명확해지기 때문에 매력적일 수 있습니다. 일부의 높은 트래픽이 쏠리는 서비스에는 자체 데이터베이스 서버가 필요할 수 있습니다.
이러한 모듈성은 강해질수록 좋습니다. 예를 들어, 각 서비스에 다른 데이터베이스 사용자 ID를 할당하고 권한 부여와 같은 데이터베이스 엑세스 제어 매커니즘을 활용할 수도 있습니다. 캡슐화를 하기 위한 별도의 장벽이 없다면 개발자는 항상 서비스의 API를 우회하고 해당 데이터에 직접 엑세스하려고 들 것 입니다.
Example
FTGO 애플리케이션은 이 접근 방식을 사용하는 애플리케이션의 예입니다. 각 서비스에는 공유 MySQL 서버의 자체(논리) 데이터베이스에 대한 액세스 권한만 부여하는 데이터베이스 자격 증명이 있습니다. 자세한 내용은 이 블로그 게시물을 참조하세요.
Resulting context
서비스별로 데이터베이스를 사용하면 다음과 같은 이점이 있습니다.
- 서비스가 느슨하게 결합되었는지 확인하는데 도움이 됩니다. 한 서비스의 데이터베이스를 변경해도 다른 서비스에는 영향을 미치지 않습니다.
- 각 서비스는 요구 사항에 가장 적합한 데이터베이스를 선택해서 사용할 수 있습니다. 예를 들어, 텍스트 검색을 수행하는 서비스는 ElasticSearch를 사용할 수 있습니다. 소셜 그래프를 조작하는 서비스는 Neo4J를 사용할 수 있습니다.
서비스별로 데이터베이스를 사용하면 다음과 같은 단점이 있습니다.
- 여러 서비스에 걸쳐 있는 비즈니스 트랜잭션을 구현하려는 것은 간단하지 않습니다. 분산 트랜잭션은 CAP 이론 때문에 피하는 것이 가장 좋습니다. (CAP 이론)
- 현재 여러 데이터베이스에 있는 데이터를 결합하는 쿼리를 구현하는 것은 어렵습니다.
- 여러 SQL 및 NoSQL 데이터베이스 관리의 복잡성
여러 서비스에 걸친 트랜잭션 및 쿼리를 구현하기 위한 다양한 패턴이 있습니다.
- 서비스에 걸친 트랜잭션 구현 : SAGA 패턴을 사용합니다.
- 서비스에 걸친 쿼리 구현
- API Composition : 응용프로그램은 데이터베이스가 아닌 조인을 수행합니다. 예를 들어, 서비스(또는 API 게이트웨이)는 먼저 Customer Service에서 고객을 검색한 다음 Order Service를 쿼리하여 고객의 가장 최신 주문을 반환해주므로써 고객과 주문을 함께 검색할 수 있습니다.
- Command Query Responsibility Segregation(CQRS) : 여러 서비스의 데이터를 포함하는 하나 이상의 구체적인 뷰를 유지관리 합니다. 뷰는 각 서비스가 데이터를 업데이트할 때 발생하는 이벤트를 구독하는 서비스에 의해 유지됩니다. 예를 들어, 온라인 상점은 Customer와 Order를 결합하는 뷰를 유지함으로써 특정 지역의 고객과 최신 주문을 찾는 쿼리를 구현할 수 있습니다. 뷰는 Customer 및 Order 이벤트를 구독하는 서비스에 의해서 업데이트 됩니다.