Skip to content

UseCases

Tuucho allows you to execute various use cases to interact with the engine. These use cases must be executed using the UseCaseExecutorProtocol instance injected with Koin.

The executor exposes two execution modes:

interface UseCaseExecutorProtocol {
    fun async(
        useCase: UseCaseProtocol<INPUT, OUTPUT>,
        input: INPUT,
        onException: ((DomainException) -> Unit)? = null,
        onResult: OUTPUT?.() -> Unit = {},
    )

    suspend fun await(
        useCase: UseCaseProtocol<INPUT, OUTPUT>,
        input: INPUT,
    ): OUTPUT?
}
  • async → fire & forget
  • await → suspends execution until the result is produced

Example: Retrieve a Validator From a Prototype

useCaseExecutor
    .await(
        useCase = fieldValidatorFactory,
        input = FormValidatorFactoryUseCase.Input(
            prototypeObject = validatorPrototype
        ),
    )
    ?.validator as? FormValidatorProtocol<String>

Example: Trigger an Action

useCaseExecutor.async(
    useCase = actionHandler,
    input = ProcessActionUseCase.Input.ActionObject(
        route = route,
        actionObject = actionObject,
        lockable = null
    )
)

ProcessActionUseCase

This is the most important use case in Tuucho. It executes actions, which are the core mechanism behind:

  • navigating
  • storing data
  • sending data
  • contextual UI updates
  • and any custom action you add
class ProcessActionUseCase(
    private val coroutineScopes: CoroutineScopesProtocol,
    private val actionExecutor: ActionExecutorProtocol,
) : UseCaseProtocol.Async<Input, Output> {

    sealed class Input {
        abstract val route: NavigationRoute.Url?
        abstract val lockable: InteractionLockable?

        data class JsonElement(
            override val route: NavigationRoute.Url?,
            val action: ActionModelDomain,
            override val lockable: InteractionLockable? = null,
            val jsonElement: kotlinx.serialization.json.JsonElement? = null,
        ) : Input()

        data class ActionObject(
            override val route: NavigationRoute.Url?,
            val actionObject: JsonObject,
            override val lockable: InteractionLockable? = null,
        ) : Input()
    }

    sealed class Output {
        class Element(
            val type: KClass<out Any>,
            val rawValue: Any,
        ) : Output() {
            @Suppress("UNCHECKED_CAST")
            inline fun <reified T> value(): T = rawValue as T

            @Suppress("UNCHECKED_CAST")
            inline fun <reified T> valueOrNull(): T? = rawValue as? T
        }

        class ElementArray(
            val values: List<Output>,
        ) : Output()
    }

    override suspend fun invoke(
        input: Input
    ) = coroutineScopes.useCase.await {
        actionExecutor.process(input = input)
    }
}

Example: Navigate Using an Action

useCaseExecutor.async(
    useCase = actionHandler,
    input = ProcessActionUseCase.Input.JsonElement(
        route = null,
        action = ActionModelDomain.from(
            command = NavigateAction.command,
            authority = NavigateAction.Url.authority,
            target = "target url",
        )
    ),
)

Existing actions are available here: object-definition/action/

You can also implement your own actions as needed. Documentation not done yet.


RefreshMaterialCacheUseCase

This use case refreshes the material JSON cache. Typically executed during startup.

class RefreshMaterialCacheUseCase(
    private val refreshMaterialCacheRepository: MaterialRepositoryProtocol.RefreshCache,
) : UseCaseProtocol.Async<RefreshMaterialCacheUseCase.Input, Unit> {

    data class Input(
        val url: String,
    )

    override suspend fun invoke(
        input: Input
    ) {
        with(input) {
            refreshMaterialCacheRepository.process(url)
        }
    }
}

See:


Key-Value Store UseCases

Tuucho provides several use cases to interact with the datastore.

The following use cases provide low-level direct access.


GetValueOrNullFromStoreUseCase

class GetValueOrNullFromStoreUseCase(
    private val coroutineScopes: CoroutineScopesProtocol,
    private val keyValueRepository: KeyValueStoreRepositoryProtocol,
) : UseCaseProtocol.Async<Input, Output> {

    data class Input(
        val key: KeyValueStoreRepositoryProtocol.Key,
    )

    data class Output(
        val value: KeyValueStoreRepositoryProtocol.Value?,
    )

    override suspend fun invoke(
        input: Input
    ) = with(input) {
        coroutineScopes.io.await {
            Output(
                value = keyValueRepository.getOrNull(key)
            )
        }
    }
}

RemoveKeyValueFromStoreUseCase

class RemoveKeyValueFromStoreUseCase(
    private val coroutineScopes: CoroutineScopesProtocol,
    private val keyValueRepository: KeyValueStoreRepositoryProtocol,
) : UseCaseProtocol.Async<Input, Unit> {

    data class Input(
        val key: KeyValueStoreRepositoryProtocol.Key
    )

    override suspend fun invoke(
        input: Input
    ) = with(input) {
        coroutineScopes.io.await {
            keyValueRepository.save(key, null)
        }
    }
}

HasKeyInStoreUseCase

class HasKeyInStoreUseCase(
    private val coroutineScopes: CoroutineScopesProtocol,
    private val keyValueRepository: KeyValueStoreRepositoryProtocol,
) : UseCaseProtocol.Async<Input, Output> {

    data class Input(
        val key: KeyValueStoreRepositoryProtocol.Key
    )

    data class Output(
        val result: Boolean,
    )

    override suspend fun invoke(
        input: Input
    ) = with(input) {
        coroutineScopes.io.await {
            Output(
                result = keyValueRepository.hasKey(key)
            )
        }
    }
}

SaveKeyValueToStoreUseCase

class SaveKeyValueToStoreUseCase(
    private val coroutineScopes: CoroutineScopesProtocol,
    private val keyValueRepository: KeyValueStoreRepositoryProtocol,
) : UseCaseProtocol.Async<Input, Unit> {

    data class Input(
        val key: KeyValueStoreRepositoryProtocol.Key,
        val value: KeyValueStoreRepositoryProtocol.Value?,
    )

    override suspend fun invoke(
        input: Input
    ) = with(input) {
        coroutineScopes.io.await {
            keyValueRepository.save(key, value)
        }
    }
}

Advanced UseCases

Tuucho exposes additional use cases for advanced workflows, including:

  • Creating custom view component features
  • Retrieving the current screen
  • Registering listeners to navigation transition events
  • Retrieving validators from prototypes for form validation
  • Observing or extending internal rendering/update pipelines

These will be fully documented in future updates.