본문 바로가기

SearchDeveloper/클린 아키텍처

[9] 27~29 서비스 기반 아키텍처 vs. 컴포넌트 기반 아키텍처

23.10.23

[27] ‘크고 작은 모든' 서비스들

서비스 기반 아키텍처 vs. 컴포넌트 기반 아키텍처

서비스 아키텍처의 통설 및 이의

통설 1) 서비스 사이의 결합이 철저하게 분리된다.

이의 제기) 결합 분리의 오류

어느 정도 일리는 있지만

  • 서비스 단위로 다른 프로세서에서 실행되고, 다른 서비스 변수에 직접 접근할 수 없고, 인터페이스는 잘 정의되어있어야 한다.

꼭 그런 것만은 아니다

  • 서비스들이 한 장비 내에 존재한다면 네트워크 상의 공유 자원 때문에 결합될 가능성이 여전히 존재한다.
  • 서비스는 분리해도 DB 를 공유하면 DB 데이터가 변경됐을 때 관련된 모든 서비스들이 변경돼야 한다.

통설 2) 개발 및 배포 독립성을 지원한다.

이의 제기) 개발 및 배포 독립성의 오류

어느 정도 일리가 있지만

  • 대규모 엔터프라이즈 시스템을 서비스 단위로 전담팀을 구성하면 시스템 개발, 유지보수, 배포, 운영을 팀 단위로 분할 할 수 있다.

극히 일부일 뿐이다.

  • 서비스 기반이 아니라 모노리틱 시스템이나 컴포넌트 기반 시스템으로도 구축하고 확장할 수 있다.
  • 결합 오류 분리에 따르면 데이터나 행위에서 어느 정도 결합되어 있으면 개발, 배포, 운영을 조정해야만 한다.
    • (elsboo) API, API 간에 필드가 바뀌더라도 둘 다 배포해줘야 함

예시) 야옹이 문제 - 택시 통합 시스템

  • 많은 택시업체를 알 고 있고, 소객은 승차 요청을 할 수 있다.
  • 비용, 고급 택시 여부, 운전사 경력 등 다양한 기준에 따라 택시를 선택할 수 있다.

서비스 아키텍처라면

추가된 서비스 - 야옹이 배달

  • 야옹이를 승차 지점에서 도착 지점까지 배달해야 한다.
  • 참여를 원하는 업체도 있고 거부한 업체도 있다.
  • 고양이 알러지가 있는 운전자는 제외돼야 한다.
  • 배차를 신청한 고객이 알러지가 있으면 지난 3일 사이에 야옹이를 배달했던 차량은 배차되지 않아야 한다.

기능이 추가되면 서비스들 전부가 변경돼야 한다. 모두 결합되어 있어서 독립적으로 개발, 배포, 유지될 수 없다.

컴포넌트 기반 아키텍처

클래스 다이어그램

-다형적으로 확장할 수 있는 클래스 집합을 생성해 새로운 기능을 처리

-배차에 특화된 로직은 Rides 컴포넌트로, 야옹이 신규 기능은 Kittens 컴포넌트로 들어간다.

-템플릿 메서드나 스트레티지 패턴으로 오버라이드

  • ex) TaxiSuppliers 의 템플릿 메소드 적용: 공통 - 택시 찾기 / 개별 - 후보 조건

-TaxiUI 는 어쩔 수 없이 변경되지만, 그 외의 것들은 변경할 필요가 없다.

-야옹이 기능을 구현한 새로운 jar 파일을 시스템에 추가하고 런타임에 동적으로 로드하도록 한다.

→ 야옹이 기능은 결합이 분리되며, 독립적으로 개발하여 배포할 수 있다.

서비스 다이어그램

(서비스 아키텍처랑 비슷한데 서비스 별 구현체만 추가된듯)

횡단 관심사

아키텍처 경계는 서비스 사이에 있지 않다. 오히려 서비스를 관통하며, 서비스를 컴포넌트 단위로 분할한다.

아키텍처 경계를 정의하는 것은 서비스 내에 위치한 컴포넌트다.

(대충 모든 서비스의 횡단 관심사를 처리하려면 컴포넌트 기반으로 아키텍처를 설계해야한다는 뜻 같음)

  • 서비스 내부는 컴포넌트 기반 아키텍처를 준수하도록 설계해야 한다.

[28] 테스트 경계

테스트도 시스템 컴포넌트

테스트도 시스템의 일부이다. 테스트는 세부적이고 구체적인 것으로, 의존성은 항상 테스트 대상이 되는 코드를 향한다.

가장 세부 사항이므로 가장 바깥 원이고 시스템 내부의 어떤 것도 테스트에 의존하지 않는다.

테스트를 고려한 설계

시스템에 너무 강하게 결합된 테스트라면 시스템이 조금만 변경되더라도 수많은 테스트를 망가뜨릴 수 있다.

  • ex) GUI 를 통해 로그인 화면부터 시작해서 업무 규칙을 검증하는 테스트를 만들면 GUI 가 조금만 바껴도 테스트는 깨질 것임

→ GUI 같은 변동성이 큰 컴포넌트에 의존하지 않고도 업무 규칙만 테스트를 할 수 있게 만든다.

그렇게 하려면, 테스트 API를 만들어라.

  • 보안 제약 사항을 무시할 수 있고
  • DB 같은 비싼 자원은 건너뛰고 (@Mock)
  • 시스템을 특정 상태로 강제하는 힘(@MethodSource)을 지녀야 한다.
  • 테스트 구조와 애플리케이션 구조의 독립적인 진화를 위함이다. 앺을 수정해도 테스트엔 영향이 없고 테스트를 수정해도 앺엔 영향이 없도록 말이다.

[29] 클린 임베디드 아키텍처

소프트웨어는 닳지 않지만, 펌웨어와 하드웨어는 낡아가므로 결국 소프트웨어도 수정해야 한다.

펌웨어란

  • 하드웨어 장치에 프로그래밍된 소프트웨어 프로그램 혹은 명령어 집합
  • 읽기 전용 메모리(ROM)에 쓰여진 소프트웨어

코드에 SQL 를 심어놓거나, 안드로이드 앱 개발자가 업무 로직과 안드로이드 API 를 결합시켜 놓는다면 결국 펌웨어를 작성하는 셈이다.

펌웨어와 소프트웨어를 분리함으로써 펌웨어를 수없이 양산하는 일을 멈추고 코드에게 유효 수명을 길게 늘릴 수 있는 기회를 주어야 한다.

그런데 임베디드 소프트웨어가 왜 그렇게 펌웨어로 많이 변할까?

소프트웨어 구축하는 세 가지 활동이 있다.

  • 먼저 동작하게 만들어라
  • 그리고 올바르게 만들어라 : 코드 리팩토링을 해 이해가 잘 가게 만들고 요구 사항 변경되면 코드를 개선할 수 있게 만들어라
  • 그리고 빠르게 만들어라

그런데 앱이 동작하는지만 확인하는 테스트 (앱-티튜드 테스트) 만 확인하다 보니 특정 장치 관련된 코드가 포함되어도 신경쓰지 못한다.

그래서 임베디드 소프웨어 아키텍처를 깔끔하게 유지할 수 있는 방법을 알아보자.

1. 클린 임베디드 아키텍처는 테스트하기 쉬운 임베디드 아키텍처다.

임베디드 개발자들은 코드를 테스트하는 환경이 특정 타깃 하드웨어로 국한되는 문제가 발생할 수 있는데, 이를 타깃-하드웨어 병목현상이라고 한다.

이를 막기 위해 HAL(Hardware Abstraction Layer, 하드웨어 추상화 계층), OSAL(Operating System Abstraction Layer, 운영체제 추상화 계층) 를 끼운다.

2.인터페이스를 통하자

ex. printf() 함수도 인터페이스를 통해 호출해서 다른 사람이 만든 printf 서비스가 생기더라도 쉽게 대체할 수 있도록 하자.

3.DRY(Don’t Repeat Yourself) 원칙: 조건부 컴파일 지시자를 반복하지 말라

ex. #ifdef BOARD_V2 처럼 같은 조건부 컴파일 지시자를 한 번만 쓰는 건 괜찮은 데 수천 번 사용하지 말자 대신 HAL 에서 인터페이스 같은걸 제공하여 대체하자.

'SearchDeveloper > 클린 아키텍처' 카테고리의 다른 글

[11] 33~34 사례 연구: 비디오 판매  (1) 2024.01.14
[10] 30~32 세부사항  (1) 2024.01.14
[8] 24~26 경계  (0) 2024.01.13
[7] 20~23 업무규칙 & 아키텍처  (1) 2024.01.13
[6] 17~19 경계  (1) 2024.01.13