2022-10-04-TIL
2022-10-04-TIL
Today I Learned
Layered Architecture Best Practice
계층형 아키텍처는 애플리케이션을 표현(presentation), 비즈니스(business 또는 service), 영속성(persistence), 데이터베이스 같은 수평 계층으로 나누고, 각 계층이 바로 아래 계층에만 의존하게 하는 구조다. 계층마다 관심사가 분리되어 있어서 화면 기술이나 DB 접근 방식을 바꿀 때 영향 범위가 한 계층으로 좁혀진다.
자주 이야기되는 원칙은 다음과 같다.
- 요청은 계층을 건너뛰지 않는다. 컨트롤러가 리포지토리를 직접 부르기 시작하면 비즈니스 규칙이 여러 곳에 흩어진다.
- 반대로 아무 일도 하지 않고 아래로 전달만 하는 계층이 많아지면 구조만 무거워진다. 계층을 지키는 비용과 얻는 이점을 함께 봐야 한다.
- 의존 방향은 위에서 아래지만, 비즈니스 계층이 영속성 기술의 세부 사항에 묶이지 않도록 리포지토리 인터페이스를 비즈니스 쪽에 두고 구현을 영속성 계층에 두는 의존성 역전(DIP)을 적용할 수 있다. CODE Magazine 글이 계층형 아키텍처와 DI, DIP의 관계를 다룬다.
서비스 계층과 비즈니스 계층을 구분하는 설계도 있다. Stack Overflow 답변들은 비즈니스 계층은 비즈니스 로직 구현에만 집중하고, 표현 계층이나 외부 시스템이 호출할 API를 노출하는 중개 역할은 서비스 계층이 맡는다고 설명한다.
- https://medium.com/design-microservices-architecture-with-patterns/layered-n-layer-architecture-e15ffdb7fa42
- https://www.codemag.com/article/0705071/Layered-Architecture-Dependency-Injection-and-Dependency-Inversion
- https://www.ideamotive.co/blog/software-architecture-design-best-practices-you-should-know
- https://subscription.packtpub.com/book/application-development/9781786463593/8/ch08lvl1sec70/layered-architecture
- https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.html
- https://stackoverflow.com/questions/4108824/service-layer-vs-business-layer-in-architecting-web-applications#:~:text=The%20Service%20Layer%20is%20usually,objects%20to%20be%20persisted%2C%20etc.
- https://learn.microsoft.com/en-us/previous-versions/msp-n-p/ee658124(v=pandp.10)?redirectedfrom=MSDN
Kafka Messaging Semantics
at-most-once, at-least-once, exactly-once의 의미와 Kafka가 멱등 프로듀서와 트랜잭션으로 exactly-once를 제공하는 방식은 8월 9일 TIL에 정리했다. 여기 링크의 0.8 문서는 멱등 프로듀서가 없던 시절의 설명이라, 당시에는 프로듀서 재전송으로 인한 중복을 피할 수 없어 기본 보장이 at-least-once였다는 맥락으로 읽으면 된다.
- https://medium.com/@andy.bryant/processing-guarantees-in-kafka-12dd2e30be0e
- https://dhkdn9192.github.io/apache-kafka/kakfa-exactly-once-delivery/
- https://joooootopia.tistory.com/29
- https://kafka.apache.org/08/documentation.html#semantics
RabbitMQ Prefetch vs Kafka
RabbitMQ는 브로커가 컨슈머에게 메시지를 밀어 주는 push 모델이라, 컨슈머가 처리 속도보다 많은 메시지를 받지 않도록 제어 장치가 필요하다. 그것이 consumer prefetch(basic.qos)다. 컨슈머가 ack하지 않은 채 들고 있을 수 있는 메시지 수의 상한을 정한다. RabbitMQ에서는 이 한도가 AMQP 명세와 달리 채널 전체가 아니라 채널 위의 컨슈머마다 적용되고, 0은 무제한을 뜻한다(RabbitMQ: Consumer Prefetch). 값이 너무 크면 한 컨슈머가 메시지를 쌓아 두고 다른 컨슈머는 놀고, 너무 작으면 왕복 대기로 처리량이 떨어진다.
Kafka는 컨슈머가 직접 가져가는 pull 모델이라 이런 장치가 필요 없다. 컨슈머가 poll()로 자기 속도만큼 가져가고, 한 번에 가져올 양은 max.poll.records 같은 컨슈머 설정으로 조절한다. 대신 병렬성은 파티션 수에 묶인다.
- https://jack-vanlightly.com/blog/2017/12/4/rabbitmq-vs-kafka-part-1-messaging-topologies
- https://www.interviewbit.com/blog/rabbitmq-vs-kafka/
- https://www.simplilearn.com/kafka-vs-rabbitmq-article#:~:text=Deciding%20Between%20Kafka%20and%20RabbitMQ&text=Your%20decision%20will%20however%20depend,for%20both%20Kafka%20and%20RabbitMQ.
Kafka vs MSK
Amazon MSK(Managed Streaming for Apache Kafka)는 오픈소스 Kafka를 그대로 실행해 주는 관리형 서비스다. 클러스터 생성, 변경, 삭제 같은 컨트롤 플레인과 브로커 장애 감지·교체를 AWS가 맡고, 토픽 생성과 메시지 생산·소비 같은 데이터 플레인은 기존 Kafka 클라이언트로 그대로 사용한다(Amazon MSK Developer Guide). 직접 운영하는 Kafka와 비교하면 운영 부담은 줄지만 브로커 설정과 버전 선택의 자유는 줄어든다. 링크의 비교 페이지는 경쟁 서비스인 Confluent Cloud 쪽에서 작성한 것이라 그 관점을 감안해서 읽어야 한다.
- https://www.confluent.io/confluent-cloud-vs-amazon-msk/
Akka
Akka는 JVM에서 액터 모델로 동시성·분산 애플리케이션을 만드는 툴킷이다. 액터는 상태를 내부에 감추고 메시지로만 소통하며, 한 번에 메시지 하나씩 처리하므로 락 없이 상태를 다룰 수 있다. 액터가 실패하면 부모(감독자)가 재시작 같은 처리 방식을 정한다. 참고로 2022년 9월 7일 Akka는 라이선스를 Apache 2.0에서 BSL 1.1로 바꾼다고 발표했다. BSL은 3년 뒤 Apache 2.0으로 전환되는 조건이 붙어 있다(Akka Blog).
- https://azderica.github.io/00-akka-starter/
Reactive Programming
리액티브 프로그래밍은 데이터 흐름(스트림)과 변화의 전파를 중심으로 비동기 코드를 작성하는 방식이다. 리액티브 선언문은 리액티브 시스템의 네 가지 특성을 든다. 가능한 한 제때 응답하고(Responsive), 장애가 나도 응답성을 유지하며(Resilient), 부하가 변해도 응답성을 유지하고(Elastic), 비동기 메시지 전달로 컴포넌트 경계를 나눈다(Message Driven)(Reactive Manifesto). 리액티브 프로그래밍은 이런 시스템을 만드는 도구 중 하나이고, 생산자가 소비자보다 빠를 때 소비자가 받을 양을 요청하는 backpressure가 Reactive Streams 명세의 핵심이다.
- https://developers.redhat.com/blog/2017/06/30/5-things-to-know-about-reactive-programming
- https://als2019.tistory.com/71
- https://www.reactivemanifesto.org/ko
RxJava vs Coroutines
Dan Lew의 글은 둘의 선택을 성숙도와 기능의 문제로 본다. RxJava는 오래 검증되었고 API가 안정적이다. Kotlin 코루틴은 멀티플랫폼을 지원하고, 비동기 스트림을 표현하는 타입이 적어 API가 단순하며, 구조화된 동시성으로 리소스 누수를 막고 backpressure를 기본으로 처리한다는 장점이 있다. 글의 결론은 한 번에 갈아엎기보다 점진적으로 옮겨 가자는 것이다.
- https://blog.danlew.net/2021/01/28/rxjava-vs-coroutines/
Blocking Queue
BlockingQueue는 큐가 비어 있으면 꺼내는 쪽이, 가득 차 있으면 넣는 쪽이 기다리는 스레드 안전한 큐다. 생산자-소비자 패턴을 락과 조건 변수 없이 구현할 수 있다. 메서드는 동작 방식별로 나뉜다. put/take는 무한정 기다리고, offer/poll은 즉시 또는 지정한 시간만 기다린 뒤 실패를 반환하며, add/remove는 실패 시 예외를 던진다. 크기를 정하는 ArrayBlockingQueue, 선택적으로 상한을 두는 LinkedBlockingQueue가 대표 구현이고, ThreadPoolExecutor의 작업 큐로도 쓰인다.
- https://www.baeldung.com/java-blocking-queue
Mono vs Flux Reactive Stream
Project Reactor의 두 기본 타입이다. Flux는 0개부터 N개의 요소를 비동기로 내보낸 뒤 완료 또는 에러 신호로 끝나는 Publisher이고, Mono는 최대 1개의 결과만 내보낸다(Reactor Reference: Flux). 단건 조회나 저장 결과처럼 하나뿐인 값은 Mono로, 목록이나 이벤트 스트림은 Flux로 표현하면 반환 타입만으로 결과 개수를 알 수 있다.
- https://tedblob.com/mono-vs-flux-reactive-stream/#:~:text=A%20Flux%20object%20represents%20a,convenient%20to%20have%20a%20Mono.
댓글
아직 댓글이 없습니다