Child Workflows & continueAsNew
Lesson 12: Advanced Workflow Orchestration
Master advanced workflow orchestration patterns using child workflows for hierarchical decomposition and continueAsNew
for managing long-running processes.
Objective
By the end of this lesson, you will understand:
- ✅ Child Workflows vs Activities - when to use each pattern
- ✅ Child workflow patterns - sequential, parallel, and dynamic
- ✅
continueAsNew
mechanism for long-running processes - ✅ Workflow hierarchies and orchestration strategies
- ✅ Scaling workflows through composition
- ✅ Best practices for complex workflow systems
1. Child Workflows vs Activities
Decision Matrix
Aspect | Child Workflows | Activities |
---|---|---|
Purpose | Complex business logic | External operations |
Durability | Full workflow semantics | Simple execution model |
Visibility | Own workflow execution | Part of parent execution |
Scalability | Independent scaling | Scales with parent |
Complexity | Can have own signals/queries | Single operation |
Testing | Can be tested independently | Tested with parent |
Monitoring | Separate workflow metrics | Part of parent metrics |
When to Use Child Workflows
// Good use cases for child workflows:
// 1. Complex multi-step processes
@WorkflowInterface
interface PaymentProcessingWorkflow {
@WorkflowMethod
fun processPayment(payment: PaymentRequest): PaymentResult
@SignalMethod
fun updatePaymentMethod(newMethod: PaymentMethod)
@QueryMethod
fun getPaymentStatus(): PaymentStatus
}
// 2. Independent business domains
@WorkflowInterface
interface InventoryManagementWorkflow {
@WorkflowMethod
fun manageInventory(request: InventoryRequest): InventoryResult
}
// 3. Long-running sub-processes
@WorkflowInterface
interface ShippingTrackingWorkflow {
@WorkflowMethod
fun trackShipment(shipment: ShipmentInfo): TrackingResult
}
When to Use Activities Instead
// Good use cases for activities:
// 1. Simple external calls
@ActivityInterface
interface EmailService {
@ActivityMethod
fun sendEmail(email: EmailMessage): Boolean
}
// 2. Database operations
@ActivityInterface
interface OrderRepository {
@ActivityMethod
fun saveOrder(order: Order): String
}
// 3. API calls
@ActivityInterface
interface PaymentGateway {
@ActivityMethod
fun chargeCard(cardInfo: CardInfo, amount: BigDecimal): ChargeResult
}
Use child workflows for complex business logic, activities for simple operations
2. Child Workflow Patterns
Sequential Child Workflows
class OrderProcessingWorkflowImpl : OrderProcessingWorkflow {
override fun processOrder(request: OrderRequest): OrderResult {
// Execute child workflows in sequence
val paymentResult = paymentWorkflow.processPayment(request.payment)
if (!paymentResult.success) {
return OrderResult.failed("Payment failed")
}
val inventoryResult = inventoryWorkflow.reserveItems(request.items)
if (!inventoryResult.success) {
// Compensate payment
compensationWorkflow.refundPayment(paymentResult.transactionId)
return OrderResult.failed("Inventory unavailable")
}
val shippingResult = shippingWorkflow.arrangeShipping(request.address)
return OrderResult.success(paymentResult, inventoryResult, shippingResult)
}
}
Parallel Child Workflows
class ParallelProcessingWorkflowImpl : ParallelProcessingWorkflow {
override fun processOrderInParallel(request: OrderRequest): OrderResult {
// Start child workflows in parallel using Async
val paymentFuture = Async.function { paymentWorkflow.processPayment(request.payment) }
val inventoryFuture = Async.function { inventoryWorkflow.checkAvailability(request.items) }
val shippingFuture = Async.function { shippingWorkflow.calculateShipping(request.address) }
// Wait for all to complete
val paymentResult = paymentFuture.get()
val inventoryResult = inventoryFuture.get()
val shippingResult = shippingFuture.get()
// Proceed with sequential steps based on parallel results
if (paymentResult.success && inventoryResult.available) {
val finalShipping = shippingWorkflow.arrangeShipping(request.address)
return OrderResult.success(paymentResult, inventoryResult, finalShipping)
}
return OrderResult.failed("Parallel validation failed")
}
}
Dynamic Child Workflow Creation
class DynamicOrderWorkflowImpl : DynamicOrderWorkflow {
override fun processOrderDynamically(request: OrderRequest): OrderResult {
val results = mutableListOf<ProcessingResult>()
// Create child workflows dynamically based on order characteristics
request.items.forEach { item ->
when (item.category) {
ItemCategory.DIGITAL -> {
val digitalWorkflow = createDigitalFulfillmentWorkflow(item)
results.add(digitalWorkflow.processDigitalItem(item))
}
ItemCategory.PHYSICAL -> {
val physicalWorkflow = createPhysicalFulfillmentWorkflow(item)
results.add(physicalWorkflow.processPhysicalItem(item))
}
ItemCategory.SUBSCRIPTION -> {
val subscriptionWorkflow = createSubscriptionWorkflow(item)
results.add(subscriptionWorkflow.setupSubscription(item))
}
}
}
return OrderResult.fromResults(results)
}
}
💡 Key Takeaways
What You've Learned:
- ✅ Child workflows enable complex hierarchical business logic
- ✅ Sequential patterns for dependent operations
- ✅ Parallel patterns for independent operations
- ✅ Dynamic creation for variable complexity scenarios
- ✅ Clear decision criteria for child workflows vs activities