Kotlin + Spring Boot + Temporal Setup
Lesson 2: Building Your Foundation
π§ Objective: Set up Temporal with Spring Boot + Kotlin, understand how it fits together, and get ready to build real workflows.
βοΈ Temporal SDK Architecture
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β Temporal Server βββββΊβ WorkflowClient βββββΊβ Your Applicationβ
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β² β² β²
β β β
β ββββββββββββββββββββ β
βββββββββββββββΊβ Worker βββββββββββββββ
ββββββββββββββββββββ
Quick Architecture Breakdown
π Core Components:
- WorkflowServiceStubs: Connects to Temporal server (think: Ethernet cable)
- WorkflowClient: Interface to start workflows (like your remote control)
- WorkerFactory: Manages the lifecycle of your workers
- Worker: Executes workflows and activities
π Spring Boot Integration
@Configuration
class TemporalConfig {
@Bean
fun workflowServiceStubs() = WorkflowServiceStubs.newLocalServiceStubs()
@Bean
fun workflowClient(stubs: WorkflowServiceStubs) =
WorkflowClient.newInstance(stubs)
@Bean
fun workerFactory(client: WorkflowClient) =
WorkerFactory.newInstance(client)
}
Simple and clean Spring configuration!
πͺ Lifecycle Hooks
@PostConstruct
fun startWorker() = workerFactory.start()
@PreDestroy
fun shutdown() = workerFactory.shutdown()
Why This Matters:
- β Proper startup - Workers start after beans are initialized
- β Graceful shutdown - Clean cleanup when app stops
- β Spring lifecycle - Integrates with Spring Boot lifecycle
π¦ Task Queues = Workflow Channels
val worker = workerFactory.newWorker("my-task-queue")
val stub = client.newWorkflowStub(
MyWorkflow::class.java,
WorkflowOptions.newBuilder()
.setTaskQueue("my-task-queue")
.build()
)
Why Task Queues Matter:
- β Horizontal scaling - Add more workers as needed
- β Clear separation - Different queues for different responsibilities
- β Logical routing - Route workflows to appropriate workers
π§ͺ Local vs Production Setup
Local Development:
WorkflowServiceStubs.newLocalServiceStubs()
Production:
WorkflowServiceStubs.newServiceStubs(
WorkflowServiceStubsOptions.newBuilder()
.setTarget("temporal.mycompany.com:7233")
.build()
)
π§ Spring Configuration Example
# application.properties
temporal.server.host=localhost
temporal.server.port=7233
temporal.namespace=default
@ConfigurationProperties(prefix = "temporal")
data class TemporalProperties(
val server: Server = Server()
) {
data class Server(
val host: String = "localhost",
val port: Int = 7233
)
}
π Dependencies You'll Need
// build.gradle.kts
implementation("io.temporal:temporal-sdk:1.22.3")
implementation("io.temporal:temporal-kotlin:1.22.3")
testImplementation("io.temporal:temporal-testing:1.22.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
implementation("io.github.microutils:kotlin-logging:3.0.5")
π Pro tip:
Lock versions and group Temporal dependencies together for clarity.
π§ Best Practices
π Environment-Specific Beans
@Profile("test")
@Bean
fun testStubs() = WorkflowServiceStubs.newLocalServiceStubs()
@Profile("!test")
@Bean
fun prodStubs() = WorkflowServiceStubs.newServiceStubs(...)
Result: Different configurations for different environments
π§΅ Multiple Workers Example
@PostConstruct
fun startWorkers() {
val userWorker = workerFactory.newWorker("user-queue")
val orderWorker = workerFactory.newWorker("order-queue")
userWorker.registerWorkflowImplementationTypes(UserWorkflowImpl::class.java)
orderWorker.registerActivitiesImplementations(OrderActivitiesImpl())
workerFactory.start()
}
Benefit: Separate concerns with dedicated task queues
π¨ Common Mistakes
β Register After Start
// β Wrong - Workers already started!
workerFactory.start()
worker.registerWorkflowImplementationTypes(...)
// β
Right - Register then start
worker.registerWorkflowImplementationTypes(...)
workerFactory.start()
More Common Mistakes
β Hardcoding Configuration
// β Wrong - No flexibility
WorkflowServiceStubs.newServiceStubs("prod-temporal:7233")
// β
Right - Configurable
@Value("\${temporal.server.url}")
lateinit var serverUrl: String
Always use externalized configuration!
π§° Troubleshooting Cheatsheet
Common Issues:
- β Connection refused β Is Temporal running locally?
- β Bean creation failed β Missing annotations or misconfigured
@Bean
- β Worker not running β Did you call
start()
after registration?
π Quick Fix:
Check logs and use structured logging for quick diagnosis.
π‘ Key Takeaways
What You've Learned:
- β How to integrate Temporal with Spring Boot
- β Configuration patterns for different environments
- β Worker lifecycle management
- β Task queue concepts
- β Common pitfalls to avoid
π What's Next?
You've laid the groundwork!
Next up in Lesson 3:
- Spin up Temporal locally
- Build your first end-to-end workflow
- See everything working together
Ready to run Temporal locally? Let's go! π