The Kotlin SDK uses standard Kotlin patterns wherever possible instead of custom APIs.
| Java SDK | Kotlin SDK |
|---|---|
void method() |
suspend fun method() |
Async.function(() -> ...) |
async { ... } |
Promise.allOf(...).get() |
awaitAll(d1, d2) |
Workflow.sleep(duration) |
delay(duration) |
Workflow.newDetachedCancellationScope() |
withContext(NonCancellable) |
Duration.ofSeconds(30) |
30.seconds |
Optional<T> |
T? |
Workflows and activities use suspend fun for natural coroutine integration:
// Method annotations are optional - use only when customizing names
@WorkflowInterface
interface OrderWorkflow {
suspend fun processOrder(order: Order): OrderResult
suspend fun updatePriority(priority: Priority) // Signal handler
suspend fun addItem(item: OrderItem): Boolean // Update handler
val status: OrderStatus // Query handler - queries are NOT suspend (synchronous)
}
// Use annotations only when customizing names
@WorkflowInterface
interface CustomNameWorkflow {
@WorkflowMethod(name = "ProcessOrder")
suspend fun processOrder(order: Order): OrderResult
@SignalMethod(name = "update-priority")
suspend fun updatePriority(priority: Priority)
@UpdateMethod(name = "add-item")
suspend fun addItem(item: OrderItem): Boolean
@QueryMethod(name = "get-status")
val status: OrderStatus
}
@ActivityInterface
interface OrderActivities {
suspend fun validateOrder(order: Order): Boolean
suspend fun chargePayment(order: Order): PaymentResult
}Note: Query methods are never
suspendbecause queries must return immediately without blocking.
Use standard kotlinx.coroutines patterns for parallel execution:
override suspend fun processOrder(order: Order): OrderResult = coroutineScope {
// Parallel execution using standard async
val validation = async { KWorkflow.executeActivity(OrderActivities::validateOrder, options, order) }
val inventory = async { KWorkflow.executeActivity(OrderActivities::checkInventory, options, order) }
// Wait for all - standard awaitAll
val (isValid, hasInventory) = awaitAll(validation, inventory)
if (!isValid || !hasInventory) {
return@coroutineScope OrderResult(success = false)
}
// Sequential execution
val charged = KWorkflow.executeActivity(OrderActivities::chargePayment, options, order)
val shipped = KWorkflow.executeActivity(OrderActivities::shipOrder, options, order)
OrderResult(success = true, trackingNumber = shipped)
}| Kotlin Pattern | Purpose |
|---|---|
coroutineScope { } |
Structured concurrency - if one fails, all cancel |
async { } |
Start parallel operation, returns Deferred<T> |
awaitAll(d1, d2) |
Wait for multiple deferreds |
delay(duration) |
Temporal timer (deterministic) |
Use standard Kotlin cancellation patterns:
override suspend fun processOrder(order: Order): OrderResult {
return try {
doProcessOrder(order)
} catch (e: CancellationException) {
// Workflow was cancelled - run cleanup in non-cancellable context
withContext(NonCancellable) {
KWorkflow.executeActivity(OrderActivities::releaseInventory, options, order)
KWorkflow.executeActivity(OrderActivities::refundPayment, options, order)
}
throw e // Re-throw to propagate cancellation
}
}| Kotlin Pattern | Java SDK Equivalent |
|---|---|
catch (e: CancellationException) |
CancellationScope failure callback |
withContext(NonCancellable) { } |
Workflow.newDetachedCancellationScope() |
coroutineScope { } |
Workflow.newCancellationScope() |
isActive / ensureActive() |
CancellationScope.isCancelRequested() |
Use withTimeout for deadline-based cancellation:
override suspend fun processWithDeadline(order: Order): OrderResult {
return withTimeout(1.hours) {
// Everything here cancels if it takes > 1 hour
KWorkflow.executeActivity(OrderActivities::validateOrder, options, order)
KWorkflow.executeActivity(OrderActivities::chargePayment, options, order)
OrderResult(success = true)
}
}
// Or get null instead of exception
val result = withTimeoutOrNull(30.minutes) {
KWorkflow.executeActivity(OrderActivities::slowOperation, options, data)
}Use kotlin.time.Duration for readable time expressions:
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.hours
val options = KActivityOptions(
startToCloseTimeout = 30.seconds,
scheduleToCloseTimeout = 5.minutes,
heartbeatTimeout = 10.seconds
)
delay(1.hours)
withTimeout(30.minutes) { ... }Nullable types replace Optional<T>:
// KWorkflowInfo uses nullable instead of Optional
val info = KWorkflow.info
val parentId: String? = info.parentWorkflowId // null if no parent
// Activity heartbeat details
val progress = KActivity.executionContext.heartbeatDetails<Int>()
val startIndex = progress ?: 0 // Elvis operator for defaultThis eliminates .orElse(null), .isPresent, and other Optional ceremony.
Use Kotlin data classes with @Serializable for workflow inputs/outputs:
@Serializable
data class Order(
val id: String,
val customerId: String,
val items: List<OrderItem>,
val priority: Priority = Priority.NORMAL
)
@Serializable
data class OrderResult(
val success: Boolean,
val trackingNumber: String? = null,
val errorMessage: String? = null
)
@Serializable
enum class Priority { LOW, NORMAL, HIGH }Data classes provide:
- Automatic
equals(),hashCode(),toString() copy()for creating modified instances- Destructuring:
val (id, customerId) = order
- KOptions Classes - Kotlin-native configuration
- Workflow Definition - Using these idioms in workflows
- Activity Definition - Using these idioms in activities
Next: Configuration