v3-transition

star 1

V2→V3 엔진 전환 패턴. 현재 V3 API + V2 엔진 구조에서 V3 엔진을 만들기 위한 추상화 설계.

em3s By em3s schedule Updated 2/26/2026

name: v3-transition description: V2→V3 엔진 전환 패턴. 현재 V3 API + V2 엔진 구조에서 V3 엔진을 만들기 위한 추상화 설계.

V3 전환 패턴

현재 상태

V3 API (Controller) → MutationService → MutationEngine (interface)
                                              ↓
                                        V2BackedEngine (V2 Graph 래핑)
                                              ↓
                                        V2BackedTableBinding (HBase)
                                        V2BackedMessageBinding (WAL/CDC)

V3 API는 이미 존재하지만, 엔진은 V2를 래핑해서 쓰고 있다. 목표는 MutationEngine/TableBinding 인터페이스의 V3 네이티브 구현체를 만드는 것.

전환 전략

1단계: 추상화 추출 (완료 — PR #201)

V2 내부를 직접 참조하던 코드에서 인터페이스를 추출.

// 추상화 (engine 모듈) — V2/V3 무관
interface MutationEngine {
    fun getTableBinding(database: String, alias: String): TableBinding
    fun writeWal(ctx: MutationContext, event: MutationEvent): Mono<Void>
    fun writeCdc(ctx: MutationContext, events: List<MutationEvent>, ...)
    val mutationRequestTimeout: Long
}

interface TableBinding {
    val table: String
    val schema: ModelSchema
    val mutationMode: MutationMode
    fun <T> withLock(key: MutationKey, action: () -> Mono<T>): Mono<T>
    fun read(key: MutationKey): Mono<State>
    fun write(key: MutationKey, before: State, after: State): Mono<MutationRecordsSummary>
    fun handleMutationError(error: Throwable)
}

2단계: V2 래핑 구현 (완료 — PR #201)

V2 내부를 V2Backed* 클래스로 캡슐화.

engine/
├── MutationEngine.kt              # 인터페이스
├── MutationContext.kt              # Context
├── binding/TableBinding.kt         # 인터페이스
├── service/MutationService.kt      # 범용 오케스트레이션
└── v2/engine/v3/
    ├── V2BackedEngine.kt           # Graph 래핑
    ├── V2BackedTableBinding.kt     # HBase 래핑
    └── V2BackedMessageBinding.kt   # WAL/CDC 래핑

3단계: V3 네이티브 구현 (목표)

같은 인터페이스에 V3 구현체를 붙인다. MutationService는 변경 없음.

engine/
├── MutationEngine.kt              # 인터페이스 (변경 없음)
├── service/MutationService.kt      # 범용 (변경 없음)
├── v2/engine/v3/V2BackedEngine.kt  # 기존 유지
└── v3/
    ├── V3Engine.kt                 # 새 구현체 (예: SlateDB)
    ├── V3TableBinding.kt
    └── V3MessageBinding.kt

핵심 설계 원칙

인터페이스는 engine 모듈, 구현체는 하위 패키지

engine/                    ← 인터페이스 (V2/V3 무관)
engine/v2/engine/v3/       ← V2 구현체 (V2Backed*)
engine/v3/                 ← V3 구현체 (미래)

V2 변환은 V2Backed* 내부에만

// V2BackedMessageBinding 내부 — V2 타입 변환
private fun MutationEvent.toV2TraceEdge(): TraceEdge = ...
private fun EventType.toV2(): EdgeOperation = ...
private fun Audit.toV2(): V2Audit = ...

V2 변환 로직이 추상화 계층 위로 새면 안 된다.

Sealed Type으로 통합

제네릭 대신 sealed type. Edge/MultiEdge를 하나의 타입 계층으로.

sealed interface MutationKey {
    data class SourceTarget(val source: Any, val target: Any) : MutationKey
    data class Id(val id: Any) : MutationKey
}

// 통합 결과 (EdgeMutationStatus + MultiEdgeMutationStatus → MutationResult)
data class MutationResult(
    val key: MutationKey,
    val count: Int,
    val status: String,
    val before: State = State.initial,
    val after: State = State.initial,
    val acc: Long = 0,
)

Request-scoped Context

data class MutationContext(
    val database: String,
    val alias: String,
    val table: String,
    val mutationMode: MutationModeContext,
    val audit: Audit,
    val requestId: String,
)

UnresolvedEvent → MutationEvent

Request DTO에서 schema 의존성을 분리. resolve는 engine 레이어에서.

interface UnresolvedEvent {
    fun createEvent(schema: ModelSchema): MutationEvent
}

// MutationService에서 resolve
Flux.fromIterable(unresolvedEvents)
    .map { it.createEvent(tb.schema) }

V3 구현 시 체크리스트

V3Engine/V3TableBinding 구현 시:

  • MutationEngine 인터페이스 완전 구현
  • TableBinding 인터페이스 완전 구현 (lock, read, write)
  • MutationService는 수정하지 않음 — 범용 오케스트레이션
  • V2 타입 import 없음 — V3 구현체에서 V2 패키지 참조 금지
  • 기존 E2E 테스트 통과 (V2BackedEngine과 동일 결과)
Install via CLI
npx skills add https://github.com/em3s/actionbase-agents --skill v3-transition
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator