A Afsm Docs 0.1.0

    Getting started with Afsm Afsm 시작하기

    Install the internal snapshot, define a small Draft machine, and host it in an ordinary Android ViewModel. 내부 스냅샷을 설치하고 작은 Draft 머신을 만든 뒤 일반적인 Android ViewModel에서 실행합니다.

    Kotlin 2.0.21 JDK 17 minSdk 23 AGP 8.10.1
    Public pre-1.0 beta 아직 공개 배포 전 Afsm is available from Maven Central as 0.1.0. APIs may change in a later pre-1.0 release when usability or safety evidence supports a better design. Afsm은 Maven Central에서 0.1.0으로 사용할 수 있습니다. 사용성이나 안전성 근거가 더 나은 설계를 지지하면 이후 pre-1.0 릴리스에서 API가 변경될 수 있습니다.

    Installation설치

    Maven Central is the only repository required. Afsm is distributed under Apache-2.0. 별도 저장소는 필요하지 않습니다. Afsm은 Apache-2.0으로 배포됩니다.

    1. Verify Maven Central repositoryMaven Central 저장소 확인

      Ensure mavenCentral() is present in plugin and dependency repositories.프로젝트의 plugin 및 dependency repository에 mavenCentral()이 포함되어 있는지 확인합니다.

      settings.gradle.kts
      pluginManagement {
          repositories {
              google()
              mavenCentral()
              gradlePluginPortal()
          }
      }
      
      dependencyResolutionManagement {
          repositories {
              google()
              mavenCentral()
          }
      }
    2. Add the modules모듈 추가

      Start with core, runtime, ViewModel integration, and the test helpers.core, runtime, ViewModel 연동, 테스트 helper부터 추가합니다.

      build.gradle.kts
      val afsmVersion = "0.1.0"
      
      dependencies {
          implementation("io.github.afsm:afsm-core:$afsmVersion")
          implementation("io.github.afsm:afsm-runtime:$afsmVersion")
          implementation("io.github.afsm:afsm-viewmodel:$afsmVersion")
          testImplementation("io.github.afsm:afsm-test:$afsmVersion")
      }

    5-minute quickstart5분 Quickstart

    This Draft flow is the smallest useful example: edit a title, start repository work, and finish in a durable Saved phase. 이 Draft 흐름은 가장 작은 실용 예제입니다. 제목을 편집하고 repository 작업을 시작한 뒤 지속되는 Saved phase로 완료합니다.

    Editing— SaveClicked → Saving— DraftSaveCompleted → Saved

    1. Define the flow types1. 흐름 타입 정의

    DraftFlow.kt
    sealed interface DraftPhase {
        data object Editing : DraftPhase
        data object Saving : DraftPhase
        data object Saved : DraftPhase
    }
    
    data class DraftData(
        val title: String = "",
        val errorMessage: String? = null,
    )
    
    typealias DraftState = AfsmState<DraftPhase, DraftData>
    
    sealed interface DraftEvent {
        data class TitleChanged(val value: String) : DraftEvent
        data object SaveClicked : DraftEvent
        data object DraftSaveCompleted : DraftEvent
        data class DraftSaveFailed(val message: String) : DraftEvent
    }
    
    sealed interface DraftCommand {
        data class SaveDraft(val title: String) : DraftCommand
    }

    2. Write the executable machine2. 실행 가능한 머신 작성

    DraftStateMachine.kt
    val draftMachine: AfsmDefaultMachine<
        DraftState,
        DraftEvent,
        DraftCommand,
    > = afsmMachine {
        initial(DraftPhase.Editing, DraftData())
    
        phase(DraftPhase.Editing) {
            on<DraftEvent.TitleChanged> {
                updateData { data, event ->
                    data.copy(title = event.value, errorMessage = null)
                }
            }
    
            on<DraftEvent.SaveClicked> {
                case("valid title", condition = { data.title.isNotBlank() }) {
                    transitionTo(DraftPhase.Saving)
                }
                case("missing title", condition = { data.title.isBlank() }) {
                    updateData { copy(errorMessage = "Title is required.") }
                }
            }
        }
    
        phase(DraftPhase.Saving) {
            onEnter {
                command("SaveDraft") { DraftCommand.SaveDraft(data.title) }
            }
            on<DraftEvent.DraftSaveCompleted> {
                transitionTo(DraftPhase.Saved)
            }
            on<DraftEvent.DraftSaveFailed> {
                updateData { data, event ->
                    data.copy(errorMessage = event.message)
                }
                transitionTo(DraftPhase.Editing)
            }
        }
    
        phase(DraftPhase.Saved)
    }
    Why Command is a valueCommand가 값인 이유 The pure machine does not call a repository, database, timer, or SDK. It emits work for the Android host to execute, which keeps transitions deterministic and JVM-testable. 순수 머신은 repository, database, timer, SDK를 직접 호출하지 않습니다. Android host가 실행할 작업을 값으로 내보내므로 전이는 결정적이고 JVM에서 테스트할 수 있습니다.

    Continue with the complete Draft tutorial →전체 Draft 튜토리얼 계속 읽기 →

    Core concepts핵심 개념

    Afsm keeps the public flow vocabulary deliberately small. Afsm은 공개 흐름 어휘를 의도적으로 작게 유지합니다.

    State
    Current Phase plus durable business Data. This is the output rendered by UI.현재 Phase와 지속되는 비즈니스 Data. UI가 렌더링하는 출력입니다.
    Event
    User intent or an external-work result entering the machine.사용자 의도 또는 외부 작업 결과가 머신으로 들어오는 입력입니다.
    Command
    Typed host-work request emitted by an accepted transition.허용된 전이가 내보내는 타입 안전한 host 작업 요청입니다.

    Decision meaningsDecision 의미

    DecisionMeaning의미
    TransitionedAn accepted rule changed the phase.허용된 규칙이 phase를 변경했습니다.
    HandledThe rule was accepted without changing phase.phase 변경 없이 규칙이 처리됐습니다.
    IgnoredAn expected duplicate or stale result intentionally did nothing.예상된 중복이나 오래된 결과를 의도적으로 무시했습니다.
    InvalidNo valid rule exists in the current phase, or the event was explicitly rejected.현재 phase에 유효한 규칙이 없거나 event를 명시적으로 거부했습니다.

    Android integrationAndroid 연동

    Afsm does not replace ViewModel. The machine owns flow rules; ViewModel owns Android lifecycle and external work. Afsm은 ViewModel을 대체하지 않습니다. 머신은 흐름 규칙을, ViewModel은 Android lifecycle과 외부 작업을 맡습니다.

    Compose ViewModel verb Event Machine State + Command
    DraftViewModel.kt
    class DraftViewModel(
        private val repository: DraftRepository,
    ) : ViewModel() {
        private val host = afsmHost(
            machine = draftMachine,
            commandHandler = { command: DraftCommand, send ->
                when (command) {
                    is DraftCommand.SaveDraft -> repository.save(command.title).fold(
                        onSuccess = { send(DraftEvent.DraftSaveCompleted) },
                        onFailure = { error ->
                            send(DraftEvent.DraftSaveFailed(error.message.orEmpty()))
                        },
                    )
                }
            },
        )
    
        val state: StateFlow<DraftState> = host.state
    
        fun updateTitle(value: String) = host.send(DraftEvent.TitleChanged(value))
        fun save() = host.send(DraftEvent.SaveClicked)
    }
    Layer계층Owns책임
    MachineTransition validity, Phase/Data changes, Commands, duplicate and stale-result policy.전이 유효성, Phase/Data 변경, Command, 중복·오래된 결과 정책.
    ViewModelStateFlow, viewModelScope, repositories, SavedStateHandle, command execution.StateFlow, viewModelScope, repository, SavedStateHandle, command 실행.
    Compose / ViewRendering, lifecycle-aware collection, navigation, focus, scroll, animation, and UI-only state.렌더링, lifecycle-aware 수집, navigation, focus, scroll, animation, UI 전용 state.

    API quick referenceAPI 빠른 참조

    The table below covers the main public surface. Use the full API page for signatures and overloads. 아래 표는 주요 공개 surface를 다룹니다. signature와 overload는 전체 API 문서를 확인하세요.

    APIModule모듈Purpose역할
    AfsmState<P, D>afsm-coreStandard Phase + Data state value.표준 Phase + Data state 값.
    AfsmReducer<S, E, C>afsm-corePure synchronous transition contract.순수 동기 전이 contract.
    AfsmMachine<S, E, C>afsm-coreGraphable machine with host-supplied initial state.host가 초기 state를 제공하는 graphable machine.
    AfsmDefaultMachine<S, E, C>afsm-coreMachine with a genuine reusable default state.재사용 가능한 실제 default state를 가진 machine.
    afsmMachine { ... }afsm-coreExecutable Phase/Data DSL and topology source.실행 가능한 Phase/Data DSL 및 topology source.
    AfsmTransition<S, C>afsm-coreNext state, command work, invocations, and decision.다음 state, command 작업, invocation, decision.
    AfsmHost<S, E, C>afsm-runtimeSerialized events, StateFlow publication, and command execution.event 직렬화, StateFlow 게시, command 실행.
    ViewModel.afsmHost(...)afsm-viewmodelViewModel-owned host attached to viewModelScope.viewModelScope에 연결된 ViewModel 소유 host.
    @AfsmGraphafsm-coreRegisters a machine for generated Mermaid output.Mermaid 출력을 생성할 machine 등록.

    DSL operationsDSL 연산

    OperationUse용도
    initialDeclare a static initial Phase and Data.정적 초기 Phase와 Data 선언.
    phaseRegister phase-local rules.phase-local 규칙 등록.
    on<Event>Handle a typed Event in the current Phase.현재 Phase에서 typed Event 처리.
    updateDataUpdate durable extended state.지속되는 extended state 업데이트.
    transitionToChange Phase.Phase 변경.
    commandEmit host-executed work.host가 실행할 작업 방출.
    caseName a real conditional branch.실제 조건 분기에 이름 부여.
    ignore / invalidAccept an expected no-op or reject an Event.예상된 no-op 허용 또는 Event 거부.
    onEnter / onExitAttach phase lifecycle actions.phase lifecycle action 연결.
    invokeStart phase-owned cancellable command work.phase 소유의 취소 가능한 command 작업 시작.

    Open the full public API reference →전체 공개 API 레퍼런스 열기 →

    Guides가이드

    Example path예제 학습 순서

    Start small, then add Android and runtime complexity only when the previous flow is clear.작게 시작하고 이전 흐름이 명확해진 뒤에만 Android와 runtime 복잡성을 추가합니다.

    1. DraftMinimal machine and ViewModel host최소 machine과 ViewModel host
    2. AuthValidation and durable completion검증과 지속되는 완료 상태
    3. CheckoutDynamic input, retry, stale results, restoration동적 입력, 재시도, 오래된 결과, 복원
    4. ProductEditorAfter Checkout: long flow and phase-owned cancellationCheckout 이후: 긴 흐름과 phase 소유 취소

    Main-path trace lab주요 경로 추적 실습

    Draft

    Read full walkthrough →전체 가이드 읽기 →
    Try the feature기능 직접 조작
    Current phase현재 phase Editing Data
    {}
    Execution trace실행 기록 0 / 0 Events

      Mirrors the maintained Kotlin machine; this page does not execute the Kotlin/JVM runtime.현재 Kotlin machine을 반영한 추적기이며 Kotlin/JVM runtime 자체를 실행하지는 않습니다.

      Why Afsm existsAfsm을 만든 이유

      Complex Android screens can reach a point where each handler looks reasonable but the complete flow exists across ViewModel updates, coroutines, callbacks, repositories, and tests. Afsm makes those phase-dependent rules local and executable without replacing ViewModel or imposing an app-wide MVI framework. 복잡한 Android 화면은 각 handler가 합리적으로 보여도 전체 흐름이 ViewModel 업데이트, 코루틴, 콜백, repository, 테스트에 흩어질 수 있습니다. Afsm은 ViewModel을 대체하거나 앱 전체 MVI를 강제하지 않고 단계별 규칙을 한곳에서 실행 가능하게 만듭니다.

      Use Afsm selectivelyAfsm은 선택적으로 사용하세요 Prefer it for multi-step, high-branching flows with phase-dependent validity, retries, or stale async results. Keep ordinary ViewModel + StateFlow when that is clearer. phase에 따른 유효성, 재시도, 오래된 비동기 결과가 있는 다단계 고분기 흐름에 사용하세요. 일반 ViewModel + StateFlow가 더 명확하면 그대로 유지하세요.
      Afsm 0.1.0 · Official documentation hub 공식 문서 허브