VOOZH about

URL: https://dev.to/numq/a-recipe-for-the-perfect-use-case-5201

⇱ A recipe for the Perfect Use Case - DEV Community


Today we’re cooking the perfect Use Case — and CQRS will be our recipe.

Ingredients

  • Action — fire and forget (logout, clear cache)
  • Query — read data (list products)
  • Command — write data (update profile)
  • Exchange — transform data (login)

All four types extend a sealed interface:

sealed interface UseCase<Input, Output> {
 interface Action : UseCase<Unit, Unit>

 interface Query<Output> : UseCase<Unit, Output>

 interface Command<Input> : UseCase<Input, Unit>

 interface Exchange<Input, Output> : UseCase<Input, Output>
}

👁 Usecases

Three Ways to Season It

Arrow (Typed Errors) — the chef’s choice:

class GenerateSeed(
 private val seedService: SeedService
) : UseCase.Action {
 override suspend fun Raise<Throwable>.action() =
 seedService.generateSeed().bind()
}

Result (Standard Wrapper) — zero dependencies:

class GenerateSeed(private val service: SeedService) : UseCase.Action {
 override suspend fun action() = service.generateSeed().getOrThrow()
}

Raw (Direct Execution) — zero overhead:

class GenerateSeed(private val service: SeedService) : UseCase.Action {
 override suspend fun action() = service.generateSeed()
}

Why This Recipe Works

  • No more UseCase<Unit, Unit> noise
  • Every use case follows the same structure
  • Query or Command makes intent obvious

Full implementation in the GitHub repository.