Skip to main content
The Workflow base class provides several methods for creating stubs, dispatching tasks, managing channels, and handling timers.

Creating Stubs

newService()

Create a stub for calling a service.
protected fun <T : Any> newService(
  klass: Class<out T>,
  tags: Set<String>? = null,
  meta: Map<String, ByteArray>? = null
): T
klass
Class<T>
required
The service interface class
tags
Set<String>
Optional tags for routing to specific workers
meta
Map<String, ByteArray>
Optional metadata to attach to service calls
class MyWorkflow : Workflow(), MyWorkflowInterface {
  // Basic service stub
  private val emailService = newService(EmailService::class.java)
  
  // Service with tags for routing
  private val premiumService = newService(
    EmailService::class.java,
    tags = setOf("premium", "priority-high")
  )
  
  // Service with metadata
  private val trackedService = newService(
    EmailService::class.java,
    meta = mapOf("traceId" to "abc-123".toByteArray())
  )
}

newWorkflow()

Create a stub for calling another workflow (sub-workflow).
protected fun <T> newWorkflow(
  klass: Class<out T>,
  tags: Set<String>? = null,
  meta: Map<String, ByteArray>? = null
): T
klass
Class<T>
required
The workflow interface class
tags
Set<String>
Optional tags for the workflow instance
meta
Map<String, ByteArray>
Optional metadata for the workflow
class ParentWorkflow : Workflow(), ParentWorkflowInterface {
  private val childWorkflow = newWorkflow(
    ChildWorkflow::class.java,
    tags = setOf("child-task"),
    meta = mapOf("parentId" to workflowId.toByteArray())
  )
  
  override fun execute() {
    val result = childWorkflow.process()
  }
}

getWorkflowById()

Get a stub for an existing workflow instance by its ID.
protected fun <T : Any> getWorkflowById(
  klass: Class<out T>,
  id: String
): T
klass
Class<T>
required
The workflow interface class
id
String
required
The workflow instance ID
class MyWorkflow : Workflow(), MyWorkflowInterface {
  override fun sendSignal(workflowId: String, message: String) {
    val target = getWorkflowById(OtherWorkflow::class.java, workflowId)
    target.signalChannel.send(message)
  }
}

getWorkflowByTag()

Get a stub for an existing workflow instance by its tag.
protected fun <T : Any> getWorkflowByTag(
  klass: Class<out T>,
  tag: String
): T
klass
Class<T>
required
The workflow interface class
tag
String
required
The workflow tag
class MyWorkflow : Workflow(), MyWorkflowInterface {
  override fun notifyAdmin(message: String) {
    val adminWorkflow = getWorkflowByTag(
      NotificationWorkflow::class.java,
      "admin-notifier"
    )
    adminWorkflow.notify(message)
  }
}

Asynchronous Dispatch

dispatch()

Dispatch a service or workflow method asynchronously, returning a Deferred<R> object.
// No parameters
protected fun <R : Any?> dispatch(method: () -> R): Deferred<R>

// With 1 parameter
protected fun <P1, R : Any?> dispatch(
  method: (p1: P1) -> R,
  p1: P1
): Deferred<R>

// With 2 parameters
protected fun <P1, P2, R : Any?> dispatch(
  method: (p1: P1, p2: P2) -> R,
  p1: P1,
  p2: P2
): Deferred<R>

// ... up to 9 parameters
The dispatch() method has overloads for 0-9 parameters.
class MyWorkflow : Workflow(), MyWorkflowInterface {
  private val service = newService(MyService::class.java)
  private val childWorkflow = newWorkflow(ChildWorkflow::class.java)
  
  override fun executeAsync() {
    // Dispatch service call
    val deferred1 = dispatch(service::processData, "input")
    
    // Dispatch workflow call
    val deferred2 = dispatch(childWorkflow::calculate, 42, "param2")
    
    // Wait for results
    val result1 = deferred1.await()
    val result2 = deferred2.await()
  }
}

dispatchVoid()

Dispatch a service or workflow method that returns void asynchronously.
// No parameters
protected fun dispatchVoid(method: Consumer0): Deferred<Void>

// With 1 parameter
protected fun <P1> dispatchVoid(
  method: Consumer1<P1>,
  p1: P1
): Deferred<Void>

// ... up to 9 parameters
class MyWorkflow : Workflow(), MyWorkflowInterface {
  private val notificationService = newService(NotificationService::class.java)
  
  override fun sendNotifications() {
    // Dispatch void methods
    val d1 = dispatchVoid(notificationService::sendEmail, "[email protected]")
    val d2 = dispatchVoid(notificationService::sendSMS, "+1234567890")
    
    // Wait for completion
    d1.await()
    d2.await()
  }
}

Channels

channel()

Create a channel for receiving external signals.
protected fun <T : Any> channel(): Channel<T>
interface MyWorkflow {
  val statusChannel: SendChannel<String>
  fun waitForApproval(): String
}

class MyWorkflowImpl : Workflow(), MyWorkflow {
  override val statusChannel = channel<String>()
  
  override fun waitForApproval(): String {
    val deferred = statusChannel.receive()
    return deferred.await()
  }
}
See Channels for detailed usage.

Timers

timer(Duration)

Create a timer that completes after a specified duration.
protected fun timer(duration: Duration): Deferred<Instant>
duration
Duration
required
The duration to wait (e.g., Duration.ofSeconds(30))
import java.time.Duration

class MyWorkflow : Workflow(), MyWorkflowInterface {
  override fun processWithDelay() {
    // Wait for 30 seconds
    timer(Duration.ofSeconds(30)).await()
    
    // Continue processing
    service.doWork()
  }
}

timer(Instant)

Create a timer that completes at a specific instant in time.
protected fun timer(instant: Instant): Deferred<Instant>
instant
Instant
required
The specific time to wait until
import java.time.Instant
import java.time.Duration

class MyWorkflow : Workflow(), MyWorkflowInterface {
  override fun scheduleForLater(scheduledTime: Instant) {
    // Wait until specific time
    timer(scheduledTime).await()
    
    // Execute at scheduled time
    service.executeScheduledTask()
  }
}
See Timers for detailed usage including racing timers with other operations.

Inline Tasks

inline()

Execute a synchronous computation within the workflow.
protected fun <S> inline(task: () -> S): S
task
() -> S
required
A lambda containing the computation to run
class MyWorkflow : Workflow(), MyWorkflowInterface {
  override fun calculateTotal(items: List<Item>): Double {
    // Run calculation inline
    return inline {
      items.sumOf { it.price * it.quantity }
    }
  }
  
  override fun processData(data: List<String>): List<String> {
    // Transform data inline
    return inline {
      data.filter { it.isNotEmpty() }
          .map { it.uppercase() }
          .sorted()
    }
  }
}

inlineVoid()

Execute a synchronous computation that returns void.
protected fun inlineVoid(task: Consumer0): Unit
class MyWorkflow : Workflow(), MyWorkflowInterface {
  private var counter = 0
  
  override fun incrementCounter() {
    inlineVoid {
      counter++
    }
  }
}
When to use inline()Use inline() for:
  • Pure computations (no side effects)
  • Data transformations
  • Simple business logic
Do NOT use for:
  • API calls (use services instead)
  • Database operations (use services instead)
  • File I/O (use services instead)

Context Properties

These static properties provide information about the current workflow execution:

workflowName

val name: String = Workflow.workflowName
The name of the workflow class.

workflowId

val id: String = Workflow.workflowId
The unique ID of the current workflow instance.

methodName

val method: String = Workflow.methodName
The name of the currently executing workflow method.

methodId

val methodId: String = Workflow.methodId
The unique ID of the current method execution.

tags

val tags: Set<String> = Workflow.tags
The tags associated with the workflow instance.

meta

val meta: Map<String, ByteArray> = Workflow.meta
The metadata associated with the workflow instance.

Example: Using Multiple Methods

import io.infinitic.workflows.Workflow
import java.time.Duration

class OrderWorkflow : Workflow(), OrderWorkflowInterface {
  private val paymentService = newService(PaymentService::class.java)
  private val inventoryService = newService(InventoryService::class.java)
  private val shippingService = newService(ShippingService::class.java)
  
  override val approvalChannel = channel<Boolean>()
  
  override fun processOrder(orderId: String): OrderResult {
    // Dispatch inventory check asynchronously
    val inventoryCheck = dispatch(inventoryService::checkStock, orderId)
    
    // Calculate total inline
    val items = inventoryCheck.await()
    val total = inline {
      items.sumOf { it.price * it.quantity }
    }
    
    // Wait for approval with timeout
    val approvalDeferred = approvalChannel.receive()
    val timeout = timer(Duration.ofMinutes(5))
    
    val approved = when ((approvalDeferred or timeout).await()) {
      is Boolean -> approvalDeferred.await()
      else -> false
    }
    
    if (!approved) {
      throw OrderNotApprovedException(orderId)
    }
    
    // Process payment and shipping in parallel
    val paymentDeferred = dispatch(paymentService::charge, orderId, total)
    val shippingDeferred = dispatch(shippingService::schedulePickup, orderId)
    
    val payment = paymentDeferred.await()
    val tracking = shippingDeferred.await()
    
    return OrderResult(orderId, payment, tracking)
  }
}

Next Steps

Calling Services

Learn how to call services from workflows

Sub-workflows

Use workflows within workflows

Channels

Receive external signals

Timers

Work with timers and delays

Build docs developers (and LLMs) love