Workshop 5: Adding a Simple Activity

Building Your First Workflow-Activity Pattern

Create a basic calculator workflow that demonstrates the fundamental workflow-activity pattern


What we want to build

Create a basic calculator workflow that demonstrates the fundamental workflow-activity pattern.

The workflow will take two numbers as input and call an activity to perform the addition, showing how workflows orchestrate activities.


Expecting Result

By the end of this lesson, you'll have:

  • A CalculatorWorkflow that accepts two integers
  • A MathActivity that performs the actual addition
  • Proper logging showing the flow: workflow → activity → result
  • Understanding of how workflows delegate work to activities

Code Steps

Step 1: Create the Workflow Interface

Open class/workshop/lesson_5/workflow/CalculatorWorkflow.kt:

package com.temporal.bootcamp.lesson5.workflow

import io.temporal.workflow.WorkflowInterface
import io.temporal.workflow.WorkflowMethod

@WorkflowInterface
interface CalculatorWorkflow {

    @WorkflowMethod
    fun add(a: Int, b: Int): Int
}

Key points:

  • Simple interface with one method
  • Takes two integers, returns one integer
  • This defines the contract for our workflow

Step 2: Create the Activity Interface

Open class/workshop/lesson_5/activity/MathActivity.kt:

package com.temporal.bootcamp.lesson5.activity

import io.temporal.activity.ActivityInterface
import io.temporal.activity.ActivityMethod

@ActivityInterface
interface MathActivity {

    @ActivityMethod
    fun performAddition(a: Int, b: Int): Int
}

Key points:

  • Activities contain the actual business logic
  • This activity will do the real work of adding numbers
  • Method name is descriptive of what it does

Step 3: Implement the Activity

Open class/workshop/lesson_5/activity/MathActivityImpl.kt:

package com.temporal.bootcamp.lesson5.activity

import mu.KotlinLogging
import org.springframework.stereotype.Component

@Component
class MathActivityImpl : MathActivity {

    private val logger = KotlinLogging.logger {}

    override fun performAddition(a: Int, b: Int): Int {
        logger.info { "MathActivity: Adding $a + $b" }

        // Simulate some work
        Thread.sleep(100)

        val result = a + b

        logger.info { "MathActivity: Result = $result" }

        return result
    }
}

Key Implementation Points

Activity Implementation:

  • @Component makes this a Spring-managed bean
  • Add logging to see when the activity runs
  • Thread.sleep(100) simulates work (database calls, API calls, etc.)
  • Actual business logic happens in activities, not workflows

Why Activities:

  • External calls (database, API, file system)
  • Non-deterministic operations (random numbers, current time)
  • Business logic that can be tested independently

Step 4: Implement the Workflow

package com.temporal.bootcamp.lesson5.workflow

import com.temporal.bootcamp.lesson5.activity.MathActivity
import io.temporal.activity.ActivityOptions
import io.temporal.workflow.Workflow
import java.time.Duration

class CalculatorWorkflowImpl : CalculatorWorkflow {

    private val mathActivity = Workflow.newActivityStub(
        MathActivity::class.java,
        ActivityOptions.newBuilder()
            .setStartToCloseTimeout(Duration.ofSeconds(30))
            .build()
    )

    override fun add(a: Int, b: Int): Int {
        val logger = Workflow.getLogger(this::class.java)

        logger.info("CalculatorWorkflow: Starting addition of $a + $b")

        val result = mathActivity.performAddition(a, b)

        logger.info("CalculatorWorkflow: Final result = $result")

        return result
    }
}

Workflow Pattern Explained

Key Components:

  • Activity Stub → Connection to the activity
  • Activity Options → Timeout and retry configuration
  • Workflow Logger → Deterministic logging within workflows
  • Orchestration Logic → Workflow controls the flow, activity does the work

This Pattern:

  • Workflow = Coordination and decision making
  • Activity = Actual work execution
  • Clear separation of concerns

How to Run

Register Components

worker.registerWorkflowImplementationTypes(CalculatorWorkflowImpl::class.java)
worker.registerActivitiesImplementations(MathActivityImpl())

Execute the Workflow

val workflow = workflowClient.newWorkflowStub(
    CalculatorWorkflow::class.java,
    WorkflowOptions.newBuilder()
        .setTaskQueue("calculator-queue")
        .setWorkflowId("calc-${System.currentTimeMillis()}")
        .build()
)

val result = workflow.add(5, 3)
println("Result: $result") // Output: Result: 8

Expected Output

CalculatorWorkflow: Starting addition of 5 + 3
MathActivity: Adding 5 + 3
MathActivity: Result = 8
CalculatorWorkflow: Final result = 8
Result: 8

Clear execution flow showing workflow orchestration and activity execution


💡 Key Takeaways

What You've Learned:

  • Workflow-Activity Pattern - fundamental Temporal pattern
  • Separation of Concerns - workflows orchestrate, activities execute
  • Activity Stubs - how workflows call activities
  • Timeout Configuration - basic resilience patterns
  • Deterministic Logging - proper logging in workflows

🚀 Next Steps

You now understand the fundamental Temporal pattern!

Lesson 6 will cover:

  • Workflow and activity separation
  • Clean architecture patterns
  • Multiple activities coordination
  • Advanced error handling

Ready to build more complex workflows? Let's continue! 🎉

results matching ""

    No results matching ""