AKKA는 다른 프레임워크에 탑재가능해 프레임워크라기보다 툴킷형태에 가까우며
Axon은 DDD를 하기위해 만들어진 프레임워크 그자체이기때문에 DDD채택이 프레임워크보다 중요할때 선택할수 있습니다.
필자는 전면채택보다 부분채택및 부부개선에 액터모델을 일부이용하고 있기때문에 전자를 조금더 선호하지만
DDD규칙을 프레임워크가 잡아주며 전술적패턴을 학습할수 있다란점에서 조사를 진행해보았습니다.
주요링크
차이점 - Axib VS Akka

1. CQRS/ES 패턴과 플랫폼의 필요성 [6]
CQRS(Command Query RESponsibility Separation)와 ES(Event Sourcing) 패턴은 시스템을 단순한 상태(State) 중심이 아닌 이벤트와 상태의 조합으로 바라보는 방식을 강조함 [6]
DDD(Domain Driven DESign)와 결합하면, 반응성(Reactive), 확장성(Scalable), 분산성(Distributed), 이벤트 기반(Event-Driven) 애플리케이션을 구축하는 데 핵심이 됨 [7]
이러한 패턴의 구현은 복잡하며, 자체적으로 프레임워크를 개발하면 실패 확률이 높아짐 [8]
목적에 맞게 설계된 플랫폼을 사용하는 것이 현명하며, 개발자가 비즈니스 코드에 집중할 수 있도록 다양한 도구와 생태계를 제공함 [10]
본 글에서는 Akka(Lightbend)와 Axon(AxonIQ) 두 플랫폼을 비교함 [11]
2. CQRS/ES 플랫폼의 핵심 구성요소 [14]
CQRS/ES 플랫폼은 두 가지 주요 측면을 가짐 [16]
논리적 인프라: DDD 개념을 활용한 CQRS/ES API 제공 [17]
물리적 인프라: 이벤트 저장/조회 및 메시지 라우팅(명령/질의/이벤트) 지원 [18]
이 두 측면이 모두 구현되어야 목적에 맞는 CQRS/ES 플랫폼으로 볼 수 있음 [14]
3. 논리적 인프라: Akka의 특징 [20]
Akka는 Actor 모델 기반으로, 장애 허용, 동시성, 분산 시스템 구축에 적합함 [21]
메시지는 각 Actor의 "메일박스"로 전달되어 비동기적으로 처리됨 [21]
Akka Persistence 모듈의 "Akka Persistence Typed API"(Scala/Java)로 ES/CQRS 구현 가능 [22]
"Aggregate" 개념을 활용하지만, DDD에서의 Aggregate를 명확히 표시하는 기능은 없음 [23]
어떤 엔티티든 이벤트 소싱 Actor로 만들 수 있음 [24]
Event Sourced Actor의 구성 요소:
상태(State), 명령(Command), 명령 핸들러(Command Handler), 이벤트(Event), 이벤트 핸들러(Event Handler), 응답(Reply) [25]
모든 것은 "EventSourcedBehavior"로 묶임 [26]
명령 메시지 처리 흐름:
Actor가 명령 메시지 수신 [28]
Command Handler가 과거 이벤트를 재생해 상태 로드 [29]
명령 검증 후, 실행 결과에 따라 Effect 반환(이벤트 저장, 무동작, Actor 중지, 예외 발생 등) [30]
이벤트 저장 성공 시, 이벤트 핸들러가 상태 변경 [32]
이벤트 핸들러는 순수 함수로 작성해야 하며, Aggregate 인스턴스화 및 이벤트 재생 시 사용됨 [33]
Akka 문서에서 Event Sourced Actor 작성 가이드 제공 [35]
Query 모델 구축을 위해 "Akka Projections" 제공
Akka Stream Source를 활용해 읽기/질의 모델 생성 가능
오프셋 추적, 재생, 복원력 향상 지원 [36]
스냅샷, 스키마 진화, 테스트, 상태 머신 등도 지원 [37]
4. 논리적 인프라: Axon의 특징 [38]
Axon Framework는 DDD 기반 CQRS/ES 애플리케이션 구축을 위한 완전한 API 제공 [39]
모든 명령/질의/이벤트를 "메시지"로 취급 [40]
위치 투명성(Location Transparency): 컴포넌트 간 명확한 메시지 전달 방식으로 상호작용 [41]
메시지 발신자가 수신자를 알 필요가 없어, 컴포넌트 분해 및 진화가 용이함 [42]
Spring Boot와의 통합 지원, 직접 구성도 가능 [43]
명령 메시지 처리 흐름:
"Command Bus"에 명령 메시지 등록, 적절한 Command Handler로 라우팅 [45]
컴포넌트 간 의존성 없이 확장/분산 가능 [47]
이벤트 소싱 Aggregate 구축을 위한 DDD/CQRS 기반 어노테이션 제공 [48]
Aggregate, Command Handler, Event Sourcing Handler, Event Handler 표시 가능 [49]
Command Handler가 이벤트를 발생시키고, Aggregate가 상태 변경 후 이벤트 저장소에 저장 [50]
Aggregate 내 Event Handler는 상태 변경만 담당 [51]
프로젝션/읽기 모델 구축을 위한 "Event Processor" 제공
오프셋 추적, 예외 처리, 세분화 등 지원 [52]
"Event Bus", "Query Bus"도 제공하여 이벤트 및 질의 메시지 라우팅 담당 [54]
스냅샷, 스키마 진화(Upcaster), 테스트, 사가(Saga) 등 지원 [56]
5. 물리적 인프라: Akka의 특징 [57]
이벤트/스냅샷 저장소로 Cassandra, JDBC, Slick DB 등 다양한 데이터 소스 사용 가능 [57]
프로젝션은 Kafka 등 다양한 Akka Stream Source 활용 가능 [57]
클러스터링 및 "Replicated Event Sourcing"(작성 시점 기준 WIP) 등 분산 기능 제공 [57]
6. 물리적 인프라: Axon의 특징 [58]
Axon Server: 이벤트 소싱에 최적화된 전용 이벤트 저장소 및 메시지 라우터 역할 [60]
Command Bus, Query Bus, Event Bus의 물리적 구현체로 동작 [61]
Axon Server SE(단일 노드)와 Axon Server EE(엔터프라이즈, 클러스터링 지원) 두 가지 에디션 제공 [62]
Axon Server 외에도 오픈소스 컴포넌트 활용 가능
예: Spring Cloud로 분산 Command Bus, PostgrES 등 관계형 DB를 이벤트 저장소로 사용 [64]
7. 결론 및 통합 가능성 [66]
Axon과 Akka 모두 엔터프라이즈급 CQRS/ES 플랫폼으로, 애플리케이션 구축에 적합함 [67]
두 플랫폼의 통합 예시로, Akka의 이벤트 저널로 Axon Server를 사용하는 방안도 제안됨 [67]
8. 참고 및 저자 정보 [68]
저자: Vijay Nair, DDD, CQRS, 이벤트 소싱 전문가, "Practical Domain-Driven DESign with Enterprise Java" 저자 [68]
Axon Framework는 복잡한 도메인을 가진 시스템에서 **도메인 주도 설계(DDD)**와 함께 CQRS(Command Query Responsibility Segregation) 및 Event Sourcing을 실용적으로 구현하도록 설계된 프레임워크입니다.
주로 Spring Boot 기반으로 많이 사용됩니다.
AxonIQ라는 회사에서 관리하며, 클라우드 및 On-Premise용 플랫폼인 Axon Server도 제공합니다.
시스템에서 "무언가를 해달라"는 요청
예: CreateOrderCommand, ChangeEmailCommand
처리자는 @CommandHandler로 구현
@CommandHandler
fun handle(cmd: CreateOrderCommand) {
AggregateLifecycle.apply(OrderCreatedEvent(...))
}
|
이미 발생한 사실
예: OrderCreatedEvent, EmailChangedEvent
Aggregate는 이벤트 기반으로 상태를 변화
@EventSourcingHandler
fun on(event: OrderCreatedEvent) {
this.orderId = event.orderId
} |
Command와 분리된 읽기 모델
@QueryHandler로 처리
읽기 성능과 확장성을 높이기 위한 CQRS 패턴 적용
@QueryHandler
fun handle(query: FindOrderQuery): OrderView {
return repository.findById(query.orderId)
} |
DDD에서 말하는 트랜잭션 단위의 루트 객체
@Aggregate 어노테이션으로 정의
@Aggregate
class OrderAggregate {
@AggregateIdentifier
lateinit var orderId: String
} |
이벤트를 DB 또는 Axon Server에 저장
이벤트 소싱 방식이면, 상태 대신 이벤트 이력으로 복원
장기간 걸리는 프로세스(예: 결제 완료 후 배송)를 관리
여러 Aggregate 간의 흐름을 이벤트 기반으로 조정
@Saga
class OrderProcessingSaga {
@SagaEventHandler(...)
fun on(OrderCreatedEvent e) {
// 다음 행동 트리거
}
} |
| 장점 | 설명 |
|---|---|
| DDD 구조를 잘 반영 | 복잡한 도메인을 명확히 모듈화 가능 |
| CQRS 구현 지원 | 읽기/쓰기 분리로 확장성과 성능 확보 |
| Event Sourcing 지원 | 과거 상태 추적과 시간여행 가능 |
| Spring Boot 통합 | 손쉬운 설정과 Bean 관리 |
| Axon Server 제공 | 이벤트 저장소와 라우팅을 중앙 관리 가능 |
| Saga로 프로세스 오케스트레이션 | 여러 도메인의 장기 트랜잭션 처리 가능 |
Axon 전용 이벤트 저장소 및 라우팅 서버
Kafka, RabbitMQ 없이도 분산 처리 가능
클러스터링, 메시지 라우팅, 모니터링 포함
| 적합한 경우 | 설명 |
|---|---|
| 복잡한 비즈니스 도메인 | 예: 금융, 쇼핑몰, 물류, 상담 시스템 등 |
| 강한 일관성이 필요한 경우 | 트랜잭션 단위 명확화 |
| 감사/이력 기능이 중요한 경우 | 모든 상태 변경을 이벤트로 보존 |
| 마이크로서비스 간 프로세스 연결 | Saga 및 이벤트 기반 통합 |
| 역할 | 어노테이션 / 구성 |
|---|---|
| Aggregate | @Aggregate, @AggregateIdentifier |
| Command | 클래스 + @CommandHandler |
| Event | 클래스 + @EventSourcingHandler |
| Query | 클래스 + @QueryHandler |
| Saga | @Saga, @SagaEventHandler |
| 이벤트 저장소 | Axon Server / RDB / Kafka 등 |
| 항목 | Axon Framework | Akka / Pekko (Typed) |
|---|---|---|
| 주요 언어 | Java, Kotlin | Scala, Java, Kotlin (Pekko로 JVM 범위 확대 중) |
| DDD 지원 방식 | CQRS + Event Sourcing + Aggregate 직접 구현 | Actor = Aggregate 역할, 메시지 핸들링 기반 상태 전이 |
| Command/Query 분리 | 내장 CQRS 구조 제공 (@CommandHandler, @QueryHandler) | 별도 설계 필요 (Actor 메시지 타입을 직접 설계) |
| Event Sourcing | 자동 관리 (Axon Event Store or Axon Server 사용) | 수동 구현 (EventPersistence + Snapshot 관리 필요) |
| Aggregate 관리 | @Aggregate 어노테이션 기반 | FSM 또는 PersistentActor로 수동 구성 |
| Saga 프로세스 | @Saga 기반 장기 트랜잭션 처리 | Actor 간 메시지 기반 오케스트레이션 (FSM 또는 Supervisor Pattern) |
| 복구/재처리 | 이벤트 재생으로 Aggregate 상태 복구 | 이벤트 소싱 재생 및 Snapshot 활용 |
| 분산 메시징 | Axon Server or Kafka 연동 | Akka Cluster & Sharding + Akka |
아래는 DDD 패턴을 구현할 때의 Axon vs Akka(Pekko) 기본 예제 비교입니다. 공통 시나리오는 간단한 주문(Order) 생성 및 상태 변경이며, 모두 Kotlin 기준입니다.
명령(Command): CreateOrderCommand(orderId, product)
이벤트(Event): OrderCreatedEvent(orderId, product)
Aggregate는 OrderAggregate로 구현
🟦 1. Axon Framework - Kotlin 예제
@Aggregate
class OrderAggregate() {
@AggregateIdentifier
lateinit var orderId: String
constructor(cmd: CreateOrderCommand) : this() {
AggregateLifecycle.apply(OrderCreatedEvent(cmd.orderId, cmd.product))
}
@EventSourcingHandler
fun on(event: OrderCreatedEvent) {
this.orderId = event.orderId
println("Order created: ${event.product}")
}
}
data class CreateOrderCommand(val orderId: String, val product: String)
data class OrderCreatedEvent(val orderId: String, val product: String)
|
@Aggregate, @CommandHandler, @EventSourcingHandler 어노테이션만 붙이면 핵심 구조 완료
이벤트 저장, 복원, CQRS QueryModel 자동 처리 가능
🟧 2. Akka (Pekko) Persistence - Kotlin 예제
sealed interface Command
data class CreateOrder(val orderId: String, val product: String) : Command
sealed interface Event
data class OrderCreated(val orderId: String, val product: String) : Event
data class OrderState(val orderId: String? = null, val product: String? = null) {
fun apply(event: OrderCreated) = this.copy(orderId = event.orderId, product = event.product)
}
class OrderAggregate private constructor(
private val context: akka.actor.typed.javadsl.ActorContext<Command>
) : EventSourcedBehavior<Command, Event, OrderState>(
PersistenceId.ofUniqueId("order-${context.self.path().name}"),
OrderState()
) {
companion object {
fun create(): Behavior<Command> = Behaviors.setup { ctx -> OrderAggregate(ctx) }
}
override fun commandHandler(): CommandHandler<Command, Event, OrderState> =
newCommandHandlerBuilder()
.forAnyState()
.onCommand(CreateOrder::class.java) { state, cmd ->
Effect().persist(OrderCreated(cmd.orderId, cmd.product))
}.build()
override fun eventHandler(): EventHandler<OrderState, Event> =
newEventHandlerBuilder()
.forAnyState()
.onEvent(OrderCreated::class.java) { state, event ->
state.apply(event)
}.build()
} |
명시적인 FSM 스타일 구조
이벤트 핸들링과 상태 관리 수동 구현
Behavior, CommandHandler, EventHandler 분리
복잡하지만 분산 시스템/Actor 모델에 적합
| 항목 | Axon | Akka / Pekko |
|---|---|---|
| 코드 양 | 매우 간결 (어노테이션 기반) | 많음 (핸들러와 상태 분리 명시 필요) |
| 구조적 통합성 | CQRS/EventSourcing 통합 관리 | 구성요소 직접 설계 필요 |
| 학습 난이도 | 중급 (Spring 기반 경험 있으면 쉬움) | 고급 (Actor, FSM, 메시지 처리 이해 필요) |
| 확장성 | Axon Server or Kafka | Akka Cluster, Sharding, Projection |
| 유즈케이스 | 도메인 중심 시스템, 전통 시스템 CQRS 전환 | 실시간 처리, 고부하 분산 시스템, IoT, 게임 서버 등 |
Axon: DDD + CQRS + Event Sourcing을 간단하게 구조화하고 싶은 경우 → 선언형으로 빠르게 구현 가능
Akka: 고성능, 실시간성, 분산 처리 중심 시스템에서 더 유연하게 동작 → 자유도는 높지만 복잡도도 높음
서로다른진영 가령 자바와 닷넷이 상호 경쟁하면서도 서로영향을 주며발전했듯이 - 플스vs엑박처럼
한가지 진영만 우월하다고 타진영을 무시하는것보다. 다른 진영도 참고하면서 학습하는 방식은 자신이 숙련도가 높다라고 믿는 진영에서도 더 발전하는 오픈마인드식 접근이라 생각이들어 AKKA에 대응하는 또다른 컨셉을 프레임워크를 조사해보았습니다.