개발자 끄적끄적
소프트웨어 아키텍처 본문
<소프트웨어 아키텍처>
- 상위수준에서 소프트웨어를 설계하는 기본 틀을 제공
- 아키텍처를 결정할 때 기능보다도 '품질 속성'이 보다 많은 비중을 차지한다
같은 기능을 구현한 두 개의 구조가 다를 수 있는 것은 달성하고자 하는 품질 속성이 다르기 때문이다
- 모든 품질 속성을 만족시키는 아키텍처를 구성하기는 사실상 어렵기 때문에 품질 속성에 우선순위를 두어 아키텍처를 결정한다
- 보안성, 성능과 같은 품질 솏성은 아키텍처 결정에 많은 영향을 준다
<계층 아키텍처(Layered Architecture)>
- 가장 일반적으로 많이 사용되는 아키텍처 시스템을 여러 계층으로 나누어 설계하여 구현한다
- 계층 아키텍스에서 보통 계층 N은 N+1 계층의 서비스를 제공하는 역할을 수행한다
즉, 계층(Layer)은 보다 상위에 위치한 계층들에게는 서비스를 제공하고,
하위 계층들로부터는 서비스를 제공받는 클라이언트의 입장이 된다
- 동일한 게층을 다르게 구현한다 할지라도 각 계층에 동일한 인터페이스를 제공한다면 상호 대체가 가능하다
- 모든 계층을 동일한 개발자가 모두 개발할 필요가 없으며 다른 개발자가 개발한 계층을 구입하여 사용할 수 있다
<3-계층 아키텍처>
- 프리젠테이션 계층(Presentation layer) : 화면을 조작하고 사용자와의 소통을 수행
- 비즈니스 로직 계층(Business Logic Layer) : 애플리케이션 로직을 실행
- 데이터 접근 계층(Data Access Layer) : 비즈니스 로직에 수행에 필요한 여러 정보를 외부 영ㅅ속 장치(DB 등)에서 읽거나 기록하기 위한 계층
<파이프&필터>
- 시스템을 구성하는 각 컴포넌트를 '필터(Filter)'라 부르며 필터간의 연결관계를 '파이프(Pipe)'라고 한다
- 각 '필터'는 입력된 데이터를 변환하여 최종적으로 출력 데이터를 생산하는 데이터 '데이터 변환기 역할'을 수행하고
'파이프'는 한 필터의 출력을 다른 필터의 입력으로 전달하는 역할을 수행
- ex) 유닉스 웰 프로그렘
- cat file.text | sort | grep "architecture
<고려사항>
- '필터'들은 서로 상태정보를 '공유하지 않아야'한다
- '필터'는 자신에 앞서 수행하는 필터(Upstream Filter)나 자신의 출력 결과를 받는 필터(Downstream Filter)에 대한 정보를 알지 못한다
- '필터'는 '병렬적'으로 수행되면 '파이프'를 통해 동기화가 이루어진다
<블랙보트>
- 해결책이 명확하지 않은 음성인식, 차량번호판인식, 자율주행과 같은 도메인에 사용된다
- Controller, Knowledge, Source, Blackboard로 구성된다
- Blackboard : 시스템의 현재 상태(해결책)를 저장하는 중앙의 '자료 저장소'
- Knowlege source : 블랙보드를 통해 시스템의 (부분적인) '해결책을 제시'하고 이를 '블랙보드에 지속적으로 반영'
- Controller : Knowledge source를 시스템에 등록하고 스케줄링
- 만약 자료 저장소의 상태에 변화가 생기면 Controller는 Knowledge source들에 이 사실을 알리고
컴포넌트로 하여금 적절한 작업을 수행하도록한다
<구조>
<고려사항>
- 컴포넌트들이 직접적으로 서로 소통을 하지 않기 때문에 컴포넌트 재사용이 용이하고
새로운 서비스를 제공하기 위한 컴포넌트를 쉽게 추가하는 것이 가능하다
- 그러나 자료 저장소와 컴포넌트간의 '결합도가 매우 강하여' 자료 저장소의 구조 변화에 따라
컴포넌트들도 변경되어야 할 가능성이 커진다
<MVC>
- 시스템의 컴포넌트를 3가지 유형으로 구분된다 : 모델, 컨트롤러, 뷰
- 모델 컴포넌트를 애플리케이션의 핵심이 되는 데이터와 기능을 제공하는 컴포넌트들이며
모델 컴포넌트는 뷰 컴포넌트와 컨트롤러 컴포넌트에 영향 받지 않도록 설계한다
- 뷰는 모델의 입력을 받아 사용자에게 보여주는 인터페이스 컴포넌트이다
- 컨트롤러는 사용자로부터의 요청을 처리하여 모델을 생성하고 필요한 애플리케이션 로직을 실행한다
또한, 사용자의 요청에 대한 적절한 뷰를 선정하는 책임도 수행한다
사용자는 오직 컨트롤러를 통해서만 시스템과 상호작용한다
- 모델과 뷰 간의 결합관계를 약화하여 동일한 모델에 대하여 다양한 뷰를 쉽게 추가할 수 있게 해주며
뷰가 변경되어도 모델에 영향을 주지 않도록 한다
- MVC 아키텍처 스타일을 사용한 대표적인 예는 스프링 프레임워크이다
<마이크로서비스(Microservice) 아키텍처>
- 마이크로 서비스는 대형 소프트웨어 프로젝트의 기능들을 작고 독립적이며 느슨하게 결합 된 모듈로 분해하여
서비스를 제공하는 아키텍처
<모니리식(Monolithic) 아키텍처>
- 한 프로젝트의 덩치가 너무 커져서 어플리케이션 구동시간이 늘어나고 빌드, 배포 시간도 길어진다
- 조그마한 수정사항이 있어도 전체를 다시 빌드하고 배포를 해야한다
- 많은 양의 코드가 몰려 잇어 개발자가 모두를 이해할 수 없고, 유지보수도 힘들다
- 일부분의 오류가 전체에 영향을 미친다
- 기능별로 알맞은 기술, 언어, 프레임워크를 선택하기 까다롭다
<마이크로서비스 아키텍처>
- 서비스가 개별적으로 독립적인 단위의 애플리케이션이기 때문에 변경이 용이하고 그 변경이 다른 서비스에 미치는 영향이 적다
- 개별 서비스 단위의 배포가 가능하기 때문에 하루에도 필요에 따라 여러 번 배포를 하는 것이 가능
- 부하가 집중되는 특정 서비스를 위해 전체 애플리케이션을 스케일 아웃할 필요가 없기 때문에 불필요한 자원의 낭비를 줄일 수 있다
- 해당 서비스의 개선과 수정 작업이 다른 서비스의 이해 당사자들과 독립적으로 진행될 수 있기 때문에 의사결정이 빠르고
독립적인 테스트의 구축이 용이하기 때문에 품질이 개선
<이벤트 기반 아키텍처(Event-Driven Architecture)>
- Request-Response Model
- ex) Service1(주문 서비스) -> Service2(결제 서비스)
- 주문한 후에 결제가 끝난 후에야 새로운 주문을 받을 수 있다
만약 결제 서비스에 장애가 발생한다면 더 이상 주문을 받지 못한다
이는 '동기화 통신'으로 인한 서비스들 간의 '강한 결합'이 발생하기 때문이다
- EDA는 서비스1에서 이벤트를 보낸다 그리고 서비스2같은 경우 이 이벤트만 알고 서비스1의 존재는 모른 채 이벤트를 처리한다
- 즉, 서비스1은 서비스2가 있다는 사실을 알 필요가 없고, 서비스2도 마찬가지이다 -> 오직 '이벤트'라는 존재를 알면 된다
- Service(Publisher) -> Event(브로커) -> Service2(Subscriber)
- Broker : Publisher(송신자)로부터 전달받은 메시지를 Subscriber(수신자)로 전달해주는 중간 역할이며 응용 소프트웨어 간에 메시지를 교환할 수 있게 한다
- 브로커는 '비동기 통신'을 지원하므로 결제 서비스에 장애가 발생하더라도 주문을 계속 받을 수 있다
- EDA는 서비스 간의 결합을 약하게 하므로 동일한 이벤트를 실행(소비)하는 서비스를 쉽게 추가할 수 있다
- 일반구조 : 한 서비스가 다운될 경우 전체 통신 안됨
- ex) Service <-> Service2 (X)
<Hexagonal 아키텍처>
- Ports And Adapters 패턴이라고도 한다
- 시스템의 핵심이 되는 부분(domain/business logic)을 사용자 인터페이스나 데이터베이스 접근과 같은 '저수준의 영역'과 분리하는 것을 목적
- 즉, 저수준 영역의 변경(UI나 DB의 변화)에 핵심 영역이 영향 받지 않도록 설계
<DIP와 Hexagonal Architecture)>
- 의존성 역전 위칙
- 첫째, 상위 모듈은 하위 모듈에 의존해서는 안된다
상위 모듈와 하위 모듈 모두 추상화에 의존해야 한다
- 둘쨰, 추상화는 세부 사항에 의존해서는 안된다
세부사항이 추상화에 의존해야한다
- 노란색 부분 : Core(핵심부분, 고수준의 영역)
- 빨간색 부분 : Port(추상화, interface를 구현)
- 파란색 부분 : Adapter(저수준의 영역, interface영역)
시스템 구동(Primary/Driving Adpater)
- Input Port
- API UI
- Console
- Test Scripts
Secondary/Driven Adapter
- Output Port
- Database
- Message Queue
- HTTP
<Layerd Architecture와 Hexagonal Architecture>
- Layered : 단방향
- dependency가 단방향, 인프라가 변경이 되면 핵심(core)에 문제가 될 수 있다
- Hexagonal : 양방향
- dependency가 핵심(core)에 있는 부분에 연결되어 있다
<Hexagonal Architecture와 DIP 구현 방법>
<<interface>>
FooUI, FooRepo : 'Port'
FooUIAdapter : adapter