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에서 실행합니다.
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으로 배포됩니다.
-
Verify Maven Central repositoryMaven Central 저장소 확인
Ensure
mavenCentral()is present in plugin and dependency repositories.프로젝트의 plugin 및 dependency repository에mavenCentral()이 포함되어 있는지 확인합니다.settings.gradle.ktspluginManagement { repositories { google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositories { google() mavenCentral() } } -
Add the modules모듈 추가
Start with core, runtime, ViewModel integration, and the test helpers.core, runtime, ViewModel 연동, 테스트 helper부터 추가합니다.
build.gradle.ktsval 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로 완료합니다.
1. Define the flow types1. 흐름 타입 정의
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. 실행 가능한 머신 작성
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)
}
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
Phaseplus durable businessData. 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 의미
| Decision | Meaning의미 |
|---|---|
| Transitioned | An accepted rule changed the phase.허용된 규칙이 phase를 변경했습니다. |
| Handled | The rule was accepted without changing phase.phase 변경 없이 규칙이 처리됐습니다. |
| Ignored | An expected duplicate or stale result intentionally did nothing.예상된 중복이나 오래된 결과를 의도적으로 무시했습니다. |
| Invalid | No 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과 외부 작업을 맡습니다.
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책임 |
|---|---|
| Machine | Transition validity, Phase/Data changes, Commands, duplicate and stale-result policy.전이 유효성, Phase/Data 변경, Command, 중복·오래된 결과 정책. |
| ViewModel | StateFlow, viewModelScope, repositories, SavedStateHandle, command execution.StateFlow, viewModelScope, repository, SavedStateHandle, command 실행. |
| Compose / View | Rendering, 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 문서를 확인하세요.
| API | Module모듈 | Purpose역할 |
|---|---|---|
| AfsmState<P, D> | afsm-core | Standard Phase + Data state value.표준 Phase + Data state 값. |
| AfsmReducer<S, E, C> | afsm-core | Pure synchronous transition contract.순수 동기 전이 contract. |
| AfsmMachine<S, E, C> | afsm-core | Graphable machine with host-supplied initial state.host가 초기 state를 제공하는 graphable machine. |
| AfsmDefaultMachine<S, E, C> | afsm-core | Machine with a genuine reusable default state.재사용 가능한 실제 default state를 가진 machine. |
| afsmMachine { ... } | afsm-core | Executable Phase/Data DSL and topology source.실행 가능한 Phase/Data DSL 및 topology source. |
| AfsmTransition<S, C> | afsm-core | Next state, command work, invocations, and decision.다음 state, command 작업, invocation, decision. |
| AfsmHost<S, E, C> | afsm-runtime | Serialized events, StateFlow publication, and command execution.event 직렬화, StateFlow 게시, command 실행. |
| ViewModel.afsmHost(...) | afsm-viewmodel | ViewModel-owned host attached to viewModelScope.viewModelScope에 연결된 ViewModel 소유 host. |
| @AfsmGraph | afsm-core | Registers a machine for generated Mermaid output.Mermaid 출력을 생성할 machine 등록. |
DSL operationsDSL 연산
| Operation | Use용도 |
|---|---|
| initial | Declare a static initial Phase and Data.정적 초기 Phase와 Data 선언. |
| phase | Register phase-local rules.phase-local 규칙 등록. |
| on<Event> | Handle a typed Event in the current Phase.현재 Phase에서 typed Event 처리. |
| updateData | Update durable extended state.지속되는 extended state 업데이트. |
| transitionTo | Change Phase.Phase 변경. |
| command | Emit host-executed work.host가 실행할 작업 방출. |
| case | Name a real conditional branch.실제 조건 분기에 이름 부여. |
| ignore / invalid | Accept an expected no-op or reject an Event.예상된 no-op 허용 또는 Event 거부. |
| onEnter / onExit | Attach phase lifecycle actions.phase lifecycle action 연결. |
| invoke | Start phase-owned cancellable command work.phase 소유의 취소 가능한 command 작업 시작. |
Guides가이드
Choose Phase, Data, Event, and Command boundaries without forcing FSM ceremony.불필요한 FSM 의식 없이 Phase, Data, Event, Command 경계를 선택합니다.
→ Testing guide테스트 가이드Test pure transitions, ViewModel wiring, runtime ordering, and graph contracts.순수 전이, ViewModel wiring, runtime 순서, graph contract를 테스트합니다.
→ Graph generation그래프 생성Generate Mermaid topology from the same executable machine definition.같은 실행 가능한 machine 정의에서 Mermaid topology를 생성합니다.
→ Restoration, Command, and UI policy복원, Command, UI 정책Restore minimal safe state and keep UI behavior at the correct boundary.최소한의 안전한 state를 복원하고 UI 동작을 올바른 경계에 둡니다.
→Example path예제 학습 순서
Start small, then add Android and runtime complexity only when the previous flow is clear.작게 시작하고 이전 흐름이 명확해진 뒤에만 Android와 runtime 복잡성을 추가합니다.
- DraftMinimal machine and ViewModel host최소 machine과 ViewModel host
- AuthValidation and durable completion검증과 지속되는 완료 상태
- CheckoutDynamic input, retry, stale results, restoration동적 입력, 재시도, 오래된 결과, 복원
- ProductEditorAfter Checkout: long flow and phase-owned cancellationCheckout 이후: 긴 흐름과 phase 소유 취소
Main-path trace lab주요 경로 추적 실습
Draft
{}
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를 강제하지 않고 단계별 규칙을 한곳에서 실행 가능하게 만듭니다.