본문 바로가기
IT 초보코딩의 세계/취미 코딩

[MicroService] Micro Service Application Architecture(내부 아키텍쳐, 애그리거트 패턴, 도메인 모델 패턴, 트랜잭션 스크립트 패턴 ) 1장

by 조이럭키7 2023. 4. 24.
반응형

Micro Service의 내부 구조 정의

바람직한 Micro Service의 내부 아키텍처: 클린 Micro Service

      ● 지금까지 언급한 아키텍처 구조는 점점 복잡해지는 모노리스 소프트웨어를 통제하기 위해 오랫동안 고민해온 결과로 그에 비해 Micro Service는 복잡해진 모노리스의 각 기능들을 쪼개기 때문에 어느 정도 복잡성을 덜어낼 수 있지만 분리해도 복잡성은 이전되고 그 안의 복잡성을 통제할 필요가 있다는 사실은 변하지 않음

      ● Micro Service의 내부 구조를 정의할 때 반드시 고려해야 할 한 가지는 Micro Service 시스템에서 정의해야 할 마이크로서 비스의 내부 구조가 다양할 수 있다는 것인데 왜냐하면 Micro Service는 앞에서 살펴본 것처럼 자율적인 Micro Service 팀에 의한 폴리글랏한 내부 구조를 가질 수 있기 때문

      ● 마이크로소프트에서는 Micro Service 설계에 대한 유용한 가이드를 자사의 개발자 사이트를 통해 공유하고 있음

      ● Micro Service는 언어로 ASP.NET Core, 저장소로 SQL Server. 아키텍처로 DDD(Domain Driven Design) & CQRS 패턴을 채택했고 Micro Service는 언어로 자바, 저장소로 오라클 데이터베이스, 아키텍처로 DDD 선택한 것을 볼 수 있는데 각 서비스의 개발 언어와 저장소가 다양하고 아키텍처 구조까지도 다양하다는 사실을 알 수 있음

      ● 이처럼 Micro Service 아키텍처에서 각 서비스는 각기 목표 와 활용도에 따라 명확하게 분리돼야 하고 각 서비스의 목적에 따라 적절한 개발 언어 및 저장소, 내부 아키텍처를 정의하는 것이 바람직

      ● 조회나 아주 간단한 기능의 경우 헥사고날 아키텍처나 클린 아키텍처 방식의 구조를 고수할 필요는 없을 것이지만 비즈니스 규칙이 복잡한 서비스는 헥사고날 아키텍처 나 클린 아키텍처의 구조를 기반으로 정의하는 것이 바람직

 

      ● 헥사고날 아키텍처나 클린 아키텍처가 지향하는 원칙

         ⊙ 지향하는 관심사에 따라 응집성을 높이고 관심사가 다른 영역과는 의존도를 낮추게 해야 함

         ⊙ 업무 규칙을 정의하는 비즈니스 로직 영역을 다른 기술 기반 영역으로부터 분리하기 위해 노력

         ⊙ 세부 기술 중심, 저수준의 외부 영역 과 핵심 업무 규칙이 정의된 고수준의 내부영역으로 구분

         ⊙ 고수준 영역은 저수준 영역에 의존하지 않게 해야 하며 저수준 영역이 고수준 영역에 의존하게 해야 함

         ⊙ 저수준 영역은 언제든지 교체, 확장 가능해야 하며, 이 같은 변화가고수준 영역에 영향을 줘서는 안됨

         ⊙ 자바처럼 인터페이스 및 추상 클래스를 지원하는 언어의 경우 저수준 영역의 구체 클래스가 고수준 영역의 추상 인터페이스에 의존하게 하는 의존성 역전의 원칙을 적용

         ⊙ 인터페이스는 고수준의 안정된 영역에 존재해야 하며, 저수준의 어댑터가 이를 구현

      ●  Micro Service의 내부 구조

         ⊙ 내부 영역에서는 맨 안쪽에 도메인이 존재하고 도메인을 서비스가 감싸는데 도메인에는 핵심 비즈니스 개념과 규칙을 구현하며 서비스에서는 도메인을 호출해서 업무를 처리하는 절차를 기술하고 외부 영역과 연계하기 위해 서비스 인터페이스를 보유

         ⊙ 서비스 인터페이스는 외부에서 내부 영역을 사용할 수 있도록 API를 제공하고 서비스가 이를 구현

         ⊙ 내부 영역에 있는 또 다른 인터페이스는 저장소 처리를 위한 인터페이스로 리포지토리(Repository) 인터페이스

         ⊙ 리포지토리 인터페이스는 외부 영역에서 정의하지 않고 내부 영역에서 정의하는데 비즈니스를 처리하는 데 필요한 기본적인 저장소 처리 사항을 추상화 해 정의하고 그렇게 하면 외부 영역의 저장소 어댑터는 이 리포지토리 인터페이스를 각 저장소에 맞는 저장소 처리 세부 기술로 구현

         ⊙ 외부 영역에는 저장소 처리 어댑터 뿐 만 아니라 다양한 인바운드, 아웃바운드를 처리하는 어댑터가 위치하는데 REST API 처리하는 어댑터, 이벤트 메시지를 처리하는 어댑터, 메시지를 구독하는 메시지 컨슈머 어댑터, 다른 Micro ServiceAPI 호출하는 프락시 어댑터 등이 위치하는데 이 같은 저장소 처리 어댑터, 이벤트 발행 어댑터, API 호출 프락시 어댑터 등 모든 아웃바운드 어댑터는 의존 관계 역전의 원칙을 적용해 외부 영역에서 내부 영역에 의존하도록 설계


내부 영역 업무 규칙

      ● 업무 규칙을 정의하는 내부 영역에는 서비스 인터페이스, 서비스 구현체, 도메인, 리포지토리 인터페이스, 도메인 이벤트 인터페이스, API 프락시 인터페이스가 존재

      ● 서비스 인터페이스는 외부 영역이 내부 영역에 대해 너무 많이 알지 못하게 하는 역할을 담당

      ● 서비스 인터페이스가 없다면 추이 종속성이 발생할 수 있음(정보 은닉 효과도 있음)

      ● 리포지토리 인터페이스, 도메인 이벤트 인터페이스, API 프락시 인터페이스는 의존 관계 역전의 원칙을 지원

      ● 더 안정된 곳인 고수준 영역에 인터페이스가 존재하고 저수준의 외부 어댑터가 이러한 인터페이스를 구현

      ● 서비스와 도메인은 클린 아키텍처의 유스케이스엔티티의 역할과 같음

      ● 도메인은 비즈니스 개념을 표현하고 서비스는 도메인을 활용해 시스템 흐름 처리를 수행

      ● 내부 영역 특히 서비스와 도메인의 관계를 구현할 때 참고할 만한 유용한 패턴이 있는데 바로 마틴 파울러의 엔터프라이즈 애플리케이션 아키텍처 패턴에서 언급한 트랜잭션 스크립트 패턴과 도메인 모델 패턴

      ● 트랜잭션 스크립트 패턴

         ⊙ 트랜잭션 스크립트(Transaction Script) 패턴에서는 비즈니스 개념을 표현하는 도메인 객체가 행위를 가지고 있지 않기 때문에 모든 비즈니스 행위, 즉 무엇인가를 수행하는 책임은 서비스에 있음

         ⊙ 서비스가 비즈니스 절차에 따라 절차적으로 도메인 객체를 이용해 모든 처리를 수행

         ⊙ 이런 방식에서는 시간이 지남에 따라 서비스가 비대해지고 도메인 객체는 점점 정보 묶음의 역할만 수행하게 될 뿐

         ⊙ 서비스는 유스케이스 처리의 단위이고 대부분의 비즈니스 로직 처리가 서비스에서 이루어지므로 비슷한 유스케이스의 경우 서비스에 중복되는 코드가 계속 생겨날 수 있는데 이러한 점이 유지보수를 어렵게 할 수 있음

         ⊙ 트랜잭션 스크립트 패턴은 절차식 프로그래밍 방식과 같기 때문에 객체지향 지식이 없어도 일반적으로 쉽게 이해할 수 있는 구조이고 기존 데이터베이스 중심 아키텍처에 익숙하다면 더 쉽게 적응할 수 있음

         ⊙ 이 패턴은 비즈니스가 간단한 경우에는 쉽게 적용할 수 있지만 비즈니스가 복잡해질 경우 서비스 코드의 양이 점점 증가하는 등 데이터베이스 중심 아키텍처에서 겪었던 문제점이 발생할 여지가 크기 때문에 간단한 비즈니스를 처리할 때 적용하는 것이 좋음

      ● 도메인 모델 패턴

         ⊙ 도메인 모델(Domain Model) 패턴은 도메인 객체가 데이터 뿐만 아니라 비즈니스 행위를 가지고 있으며 도메인 객체가 소유한 데이터는 도메인 객체가 제공하는 행위에 의해 은닉

         ⊙ 도메인 객체는 각 비즈니스 개념 및 행위에 대한 책임을 수행하고 서비스는 비즈니스 유스케이스를 구현하기 위해 서비스의 행위를 도메인 객체에 일부분 위임해서 처리

         ⊙ 서비스의 책임들이 도메인으로 적절히 분산되기 때문에 서비스가 비대해지지 않고 서비스 메서드는 단순해 짐

         ⊙ 도메인 모델 패턴의 도메인 모델은 객체지향 설계의 객체 모델

         ⊙ 거대한 서비스 클래스 대신 각기 적절한 책임을 가진 여러 클래스로 구성되므로 이해하기 쉽고 관리 및 테스트하기 쉽고 여기서 더 진화해서 도메인 주도 설계의 애그리거트(Aggregate) 패턴을 적용할 수 있는 구조

         ⊙ 핵심은 도메인 모델이기 때문에 객체지향 지식에 대한 경험과 역량이 필요

         ⊙ 잘 만들어진 도메인 모델은 복잡한 비즈니스 로직을 처리하는 데 유용하며 잘 정의된 도메인 모델은 코드의 양을 줄이고 재사용성도 높임

         ⊙ 복잡한 비즈니스 로직이 많은 Micro Service의 구조로 선택 하는 것이 좋음

 

      ● 도메인 주도 설계의 애그리거트 패턴

         ⊙ 애그리거트 패턴은 에릭 에반스의 도메인 주도 설계에 등장하는 패턴으로 점점 복잡해질 수 있는 객체 모델링의 단점을 보완한 패턴이라 볼 수 있음

         ⊙ 도메인 모델링을 하다 보면 객체 간의 관계를 참조로 표현하게 되는데 참조로 정의할 경우 일대다(one-to- many) 관계의 객체를 쉽게 사용할 수 있다는 장점이 있지만 업무가 복잡해지면 참조로 인한 다단계 계층 구조가 생기고 점점 참조 관계가 복잡해지고 무거워질 수 있음

         ⊙ 복잡한 도메인 모델은 모델 내부의 경계가 불명확한데 예를 들면 어떤 도메인 모델이 일대다 관계를 맺고 있고 (many) 측에 있는 클래스의 총 개수를 일(one) 측에 있는 클래스에서 집계해야 하는 규칙이 있다고 보면 서비스에서 이러한 로직을 처리할 때 다(many) 측에 클래스가 추가되면 일(one) 측의 클래스에서 집계한 값을 수정해야 하는데 다 측의 클래스만 추가하고 집계한 값을 수정하지 않는다면 비즈니스 일관성이 깨질 것인데 도메인 모델이 커짐에 따라 이러한 문제가 복잡해지고 꼬일 수 있는데 이를 개선할 방안으로 최상위에 존재하는 엔티티(Root Entity) 중심으로 개념의 집합을 분리한 것이 애그리거트 패턴

         ⊙ 애그리거트 패턴

         ⊙ 애그리거트 패턴에서는 이처럼 애그리거트를 한 단위로 일관되게 처리하기 위해 다음과 같은 규칙을 부여

                → 애그리거트 루트만 참조

                → 애그리거트 내 상세 클래스를 바로 참조하지 않고 루트를 통해 참조

                → 수정할 때도 마찬가지인데 애그리거트 간의 참조는 객체를 직접 참조하는 대신 기본키를 사용

                → 기본키를 사용하면 느슨하게 연관되고 수정이 필요하지 않은 애그리거트를 함께 수정하는 실수를 방지

                → 하나의 트랜잭션으로 하나의 애그리거트만 생성 및 수정

 

반응형

댓글