Skip to main content

Context in Production: Real-World Examples

Complete, production-ready examples demonstrating Context API usage in real-world applications.

Overview​

This guide provides production-grade implementations showing how to use Spice Framework's Context API in real business scenarios:

  • E-Commerce Platform - Multi-tenant order processing with inventory management
  • Customer Support System - Ticket routing with user context and SLA tracking
  • Financial Services - Transaction processing with audit trails and compliance
  • Healthcare Platform - Patient data access with HIPAA compliance

Each example includes:

  • βœ… Complete working code
  • βœ… Multi-tenancy support
  • βœ… Security and compliance
  • βœ… Error handling
  • βœ… Audit logging
  • βœ… Tests

Example 1: E-Commerce Platform​

Scenario​

Multi-tenant e-commerce platform with:

  • Multiple brands (tenants) on shared infrastructure
  • Inventory management per tenant
  • Order processing with payment validation
  • Audit trail for all operations

Implementation​

1. Domain Models​

data class Product(
val id: String,
val tenantId: String,
val name: String,
val price: Double,
val stockQuantity: Int
)

data class Order(
val id: String,
val tenantId: String,
val userId: String,
val items: List<OrderItem>,
val totalAmount: Double,
val status: OrderStatus,
val createdAt: Instant,
val createdBy: String
)

data class OrderItem(
val productId: String,
val quantity: Int,
val priceAtPurchase: Double
)

enum class OrderStatus {
PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
}

data class PaymentMethod(
val id: String,
val userId: String,
val tenantId: String,
val type: PaymentType,
val last4: String
)

enum class PaymentType {
CREDIT_CARD, DEBIT_CARD, PAYPAL, BANK_TRANSFER
}

2. Repository Layer​

class ProductRepository : BaseContextAwareService() {

suspend fun findById(productId: String): Product? = withTenant { tenantId ->
database.query(
"""
SELECT * FROM products
WHERE id = ? AND tenant_id = ?
""",
productId, tenantId
).firstOrNull()
}

suspend fun findByIds(productIds: List<String>): List<Product> = withTenant { tenantId ->
database.query(
"""
SELECT * FROM products
WHERE id IN (${productIds.joinToString { "?" }})
AND tenant_id = ?
""",
*(productIds + tenantId).toTypedArray()
)
}

suspend fun updateStock(productId: String, quantity: Int) = withTenant { tenantId ->
database.execute(
"""
UPDATE products
SET stock_quantity = stock_quantity + ?
WHERE id = ? AND tenant_id = ?
""",
quantity, productId, tenantId
)
}

suspend fun reserveStock(productId: String, quantity: Int): Boolean = withTenant { tenantId ->
val updated = database.execute(
"""
UPDATE products
SET stock_quantity = stock_quantity - ?
WHERE id = ?
AND tenant_id = ?
AND stock_quantity >= ?
""",
quantity, productId, tenantId, quantity
)

updated > 0
}
}

class OrderRepository : BaseContextAwareService() {

suspend fun create(order: Order): Order = withTenantAndUser { tenantId, userId ->
require(order.tenantId == tenantId) { "Tenant ID mismatch" }
require(order.userId == userId) { "User ID mismatch" }

database.insert("orders", order)
order
}

suspend fun findById(orderId: String): Order? = withTenant { tenantId ->
database.query(
"SELECT * FROM orders WHERE id = ? AND tenant_id = ?",
orderId, tenantId
).firstOrNull()
}

suspend fun findUserOrders(status: OrderStatus? = null): List<Order> =
withTenantAndUser { tenantId, userId ->
val query = if (status != null) {
database.query(
"""
SELECT * FROM orders
WHERE tenant_id = ? AND user_id = ? AND status = ?
ORDER BY created_at DESC
""",
tenantId, userId, status.name
)
} else {
database.query(
"""
SELECT * FROM orders
WHERE tenant_id = ? AND user_id = ?
ORDER BY created_at DESC
""",
tenantId, userId
)
}
query
}

suspend fun updateStatus(orderId: String, status: OrderStatus) = withTenant { tenantId ->
database.execute(
"UPDATE orders SET status = ? WHERE id = ? AND tenant_id = ?",
status.name, orderId, tenantId
)
}
}

class PaymentRepository : BaseContextAwareService() {

suspend fun findUserPaymentMethods(): List<PaymentMethod> =
withTenantAndUser { tenantId, userId ->
database.query(
"""
SELECT * FROM payment_methods
WHERE tenant_id = ? AND user_id = ?
""",
tenantId, userId
)
}

suspend fun findById(paymentMethodId: String): PaymentMethod? =
withTenantAndUser { tenantId, userId ->
database.query(
"""
SELECT * FROM payment_methods
WHERE id = ? AND tenant_id = ? AND user_id = ?
""",
paymentMethodId, tenantId, userId
).firstOrNull()
}
}

3. Service Layer​

class InventoryService : BaseContextAwareService() {
private val productRepo = ProductRepository()
private val auditService = AuditService()

suspend fun checkAvailability(
items: List<OrderItem>
): InventoryCheckResult = withTenant { tenantId ->

val productIds = items.map { it.productId }
val products = productRepo.findByIds(productIds)

val unavailableItems = mutableListOf<String>()

items.forEach { item ->
val product = products.find { it.id == item.productId }

if (product == null) {
unavailableItems.add("Product ${item.productId} not found")
} else if (product.stockQuantity < item.quantity) {
unavailableItems.add(
"Product ${product.name}: requested ${item.quantity}, " +
"available ${product.stockQuantity}"
)
}
}

auditService.log(
"INVENTORY_CHECK",
mapOf(
"items" to items.size,
"available" to (unavailableItems.isEmpty())
)
)

InventoryCheckResult(
available = unavailableItems.isEmpty(),
unavailableItems = unavailableItems
)
}

suspend fun reserveStock(items: List<OrderItem>): Boolean = withTenant { tenantId ->
val reservations = mutableListOf<Pair<String, Int>>()

try {
items.forEach { item ->
val reserved = productRepo.reserveStock(item.productId, item.quantity)

if (!reserved) {
// Rollback all reservations
reservations.forEach { (productId, quantity) ->
productRepo.updateStock(productId, quantity) // Return stock
}

auditService.log(
"STOCK_RESERVATION_FAILED",
mapOf("productId" to item.productId, "quantity" to item.quantity)
)

return@withTenant false
}

reservations.add(item.productId to item.quantity)
}

auditService.log(
"STOCK_RESERVED",
mapOf("items" to items.size, "reservations" to reservations.size)
)

true

} catch (e: Exception) {
// Rollback on exception
reservations.forEach { (productId, quantity) ->
productRepo.updateStock(productId, quantity)
}
throw e
}
}

suspend fun releaseStock(items: List<OrderItem>) = withTenant { tenantId ->
items.forEach { item ->
productRepo.updateStock(item.productId, item.quantity)
}

auditService.log(
"STOCK_RELEASED",
mapOf("items" to items.size)
)
}
}

data class InventoryCheckResult(
val available: Boolean,
val unavailableItems: List<String>
)

3.1. Production Error Handling with SpiceResult​

Real-world services should return SpiceResult<T> for type-safe error handling:

import io.github.noailabs.spice.error.SpiceResult
import io.github.noailabs.spice.error.SpiceError

class InventoryServiceWithErrors : BaseContextAwareService() {
private val productRepo = ProductRepository()
private val auditService = AuditService()

/**
* Check product availability with proper error handling
*/
suspend fun checkAvailability(
items: List<OrderItem>
): SpiceResult<InventoryCheckResult> = withTenant { tenantId ->

try {
val productIds = items.map { it.productId }
val products = productRepo.findByIds(productIds)

val unavailableItems = mutableListOf<String>()

items.forEach { item ->
val product = products.find { it.id == item.productId }

if (product == null) {
// Product not found - validation error
return@withTenant SpiceResult.failure(
SpiceError.validationError(
message = "Product not found",
field = "productId",
actualValue = item.productId
).withContext(
"tenantId" to tenantId,
"requestedProducts" to productIds.size
)
)
} else if (product.stockQuantity < item.quantity) {
unavailableItems.add(
"Product ${product.name}: requested ${item.quantity}, " +
"available ${product.stockQuantity}"
)
}
}

auditService.log(
"INVENTORY_CHECK",
mapOf(
"items" to items.size,
"available" to (unavailableItems.isEmpty())
)
)

SpiceResult.success(
InventoryCheckResult(
available = unavailableItems.isEmpty(),
unavailableItems = unavailableItems
)
)

} catch (e: Exception) {
// Database or unexpected errors
SpiceResult.failure(
SpiceError.agentError(
message = "Inventory check failed: ${e.message}",
cause = e
).withContext(
"tenantId" to tenantId,
"itemCount" to items.size
)
)
}
}

/**
* Reserve stock with atomic rollback on failure
*/
suspend fun reserveStock(
items: List<OrderItem>
): SpiceResult<ReservationResult> = withTenant { tenantId ->

val reservations = mutableListOf<Pair<String, Int>>()

try {
items.forEach { item ->
val reserved = productRepo.reserveStock(item.productId, item.quantity)

if (!reserved) {
// Rollback all reservations
reservations.forEach { (productId, quantity) ->
productRepo.updateStock(productId, quantity)
}

auditService.log(
"STOCK_RESERVATION_FAILED",
mapOf("productId" to item.productId)
)

return@withTenant SpiceResult.failure(
SpiceError.validationError(
message = "Insufficient stock for product ${item.productId}",
field = "stockQuantity",
actualValue = item.quantity
).withContext(
"tenantId" to tenantId,
"productId" to item.productId,
"requestedQuantity" to item.quantity
)
)
}

reservations.add(item.productId to item.quantity)
}

auditService.log(
"STOCK_RESERVED",
mapOf("items" to items.size)
)

SpiceResult.success(
ReservationResult(
reservedItems = reservations.size,
reservations = reservations
)
)

} catch (e: Exception) {
// Rollback on exception
reservations.forEach { (productId, quantity) ->
productRepo.updateStock(productId, quantity)
}

SpiceResult.failure(
SpiceError.agentError(
message = "Stock reservation failed",
cause = e
).withContext(
"tenantId" to tenantId,
"reservedBeforeFailure" to reservations.size
)
)
}
}
}

data class ReservationResult(
val reservedItems: Int,
val reservations: List<Pair<String, Int>>
)

3.2. Payment Service with Error Handling​

class PaymentServiceWithErrors : BaseContextAwareService() {
private val paymentRepo = PaymentRepository()
private val auditService = AuditService()

suspend fun processPayment(
paymentMethodId: String,
amount: Double,
orderId: String
): SpiceResult<PaymentResult> = withTenantAndUser { tenantId, userId ->

try {
// Validate payment method
val paymentMethod = paymentRepo.findById(paymentMethodId)
if (paymentMethod == null) {
return@withTenantAndUser SpiceResult.failure(
SpiceError.validationError(
message = "Payment method not found",
field = "paymentMethodId",
actualValue = paymentMethodId
).withContext(
"tenantId" to tenantId,
"userId" to userId
)
)
}

// Validate ownership
if (paymentMethod.userId != userId) {
auditService.log(
"PAYMENT_UNAUTHORIZED",
mapOf(
"paymentMethodId" to paymentMethodId,
"attemptedUserId" to userId,
"actualUserId" to paymentMethod.userId
)
)

return@withTenantAndUser SpiceResult.failure(
SpiceError.authError(
message = "Payment method belongs to different user",
provider = "internal"
).withContext(
"tenantId" to tenantId,
"userId" to userId,
"paymentMethodId" to paymentMethodId
)
)
}

// Process payment via external gateway
val gatewayResult = processWithPaymentGateway(
paymentMethod = paymentMethod,
amount = amount,
orderId = orderId
)

if (!gatewayResult.success) {
auditService.log(
"PAYMENT_FAILED",
mapOf(
"orderId" to orderId,
"amount" to amount,
"reason" to gatewayResult.errorMessage
)
)

return@withTenantAndUser SpiceResult.failure(
SpiceError.networkError(
message = "Payment gateway error: ${gatewayResult.errorMessage}",
statusCode = gatewayResult.statusCode,
endpoint = "payment-gateway"
).withContext(
"tenantId" to tenantId,
"userId" to userId,
"orderId" to orderId,
"amount" to amount
)
)
}

auditService.log(
"PAYMENT_PROCESSED",
mapOf(
"orderId" to orderId,
"amount" to amount,
"transactionId" to gatewayResult.transactionId
)
)

SpiceResult.success(
PaymentResult(
success = true,
transactionId = gatewayResult.transactionId!!,
amount = amount
)
)

} catch (e: Exception) {
SpiceResult.failure(
SpiceError.agentError(
message = "Payment processing failed: ${e.message}",
cause = e
).withContext(
"tenantId" to tenantId,
"userId" to userId,
"orderId" to orderId
)
)
}
}

private suspend fun processWithPaymentGateway(
paymentMethod: PaymentMethod,
amount: Double,
orderId: String
): GatewayResult {
// Simulate payment gateway call
return GatewayResult(
success = true,
transactionId = "txn_${System.currentTimeMillis()}",
statusCode = 200
)
}
}

data class PaymentResult(
val success: Boolean,
val transactionId: String,
val amount: Double
)

data class GatewayResult(
val success: Boolean,
val transactionId: String? = null,
val errorMessage: String? = null,
val statusCode: Int? = null
)

3.3. Order Service with Complete Error Handling​

class OrderServiceWithErrors : BaseContextAwareService() {
private val orderRepo = OrderRepository()
private val inventoryService = InventoryServiceWithErrors()
private val paymentService = PaymentServiceWithErrors()
private val auditService = AuditService()

/**
* Create order with complete error handling and context propagation
*/
suspend fun createOrder(
items: List<OrderItem>,
paymentMethodId: String
): SpiceResult<Order> = withTenantAndUser { tenantId, userId ->

auditService.log(
"ORDER_CREATION_STARTED",
mapOf("items" to items.size, "userId" to userId)
)

try {
// Step 1: Check inventory availability
val inventoryResult = inventoryService.checkAvailability(items)
if (inventoryResult.isFailure) {
auditService.log(
"ORDER_CREATION_FAILED",
mapOf("step" to "inventory_check", "reason" to "INVENTORY_ERROR")
)
return@withTenantAndUser inventoryResult.mapSuccess { Order() } // Convert type
}

val inventoryCheck = inventoryResult.getOrThrow()
if (!inventoryCheck.available) {
return@withTenantAndUser SpiceResult.failure(
SpiceError.validationError(
message = "Items unavailable: ${inventoryCheck.unavailableItems.joinToString()}",
field = "items"
).withContext(
"tenantId" to tenantId,
"unavailableItems" to inventoryCheck.unavailableItems.size
)
)
}

// Step 2: Reserve stock
val reservationResult = inventoryService.reserveStock(items)
if (reservationResult.isFailure) {
auditService.log(
"ORDER_CREATION_FAILED",
mapOf("step" to "stock_reservation", "reason" to "RESERVATION_FAILED")
)
return@withTenantAndUser reservationResult.mapSuccess { Order() }
}

// Step 3: Calculate total
val totalAmount = items.sumOf { it.quantity * it.priceAtPurchase }

// Step 4: Process payment
val orderId = "ord_${System.currentTimeMillis()}"
val paymentResult = paymentService.processPayment(
paymentMethodId = paymentMethodId,
amount = totalAmount,
orderId = orderId
)

if (paymentResult.isFailure) {
// Rollback stock reservation
items.forEach { item ->
// Release reserved stock (implementation omitted)
}

auditService.log(
"ORDER_CREATION_FAILED",
mapOf("step" to "payment", "reason" to "PAYMENT_FAILED")
)

return@withTenantAndUser paymentResult.mapSuccess { Order() }
}

// Step 5: Create order
val order = Order(
id = orderId,
tenantId = tenantId,
userId = userId,
items = items,
totalAmount = totalAmount,
status = OrderStatus.CONFIRMED,
createdAt = Instant.now(),
createdBy = userId
)

val createdOrder = orderRepo.create(order)

auditService.log(
"ORDER_CREATED",
mapOf(
"orderId" to orderId,
"totalAmount" to totalAmount,
"items" to items.size
)
)

SpiceResult.success(createdOrder)

} catch (e: Exception) {
auditService.log(
"ORDER_CREATION_FAILED",
mapOf("reason" to "UNEXPECTED_ERROR", "error" to e.message)
)

SpiceResult.failure(
SpiceError.agentError(
message = "Order creation failed: ${e.message}",
cause = e
).withContext(
"tenantId" to tenantId,
"userId" to userId,
"itemCount" to items.size
)
)
}
}
}

Key Patterns:

  1. Return SpiceResult - All service methods return SpiceResult<T>
  2. Use Specific Errors - validationError, authError, networkError, etc.
  3. Add Context - Always use .withContext() to add debugging info
  4. Audit Trail - Log all important operations with tenant/user context
  5. Rollback on Failure - Clean up partial state when operations fail
class OrderService : BaseContextAwareService() {
private val orderRepo = OrderRepository()
private val productRepo = ProductRepository()
private val paymentRepo = PaymentRepository()
private val inventoryService = InventoryService()
private val paymentService = PaymentService()
private val auditService = AuditService()

suspend fun createOrder(
items: List<OrderItem>,
paymentMethodId: String
): OrderResult = withTenantAndUser { tenantId, userId ->

auditService.log(
"ORDER_CREATION_STARTED",
mapOf("items" to items.size, "userId" to userId)
)

// 1. Validate payment method
val paymentMethod = paymentRepo.findById(paymentMethodId)
?: return@withTenantAndUser OrderResult.Error("Payment method not found")

// 2. Check inventory
val inventoryCheck = inventoryService.checkAvailability(items)
if (!inventoryCheck.available) {
auditService.log(
"ORDER_CREATION_FAILED",
mapOf("reason" to "INVENTORY_UNAVAILABLE")
)
return@withTenantAndUser OrderResult.Error(
"Items unavailable: ${inventoryCheck.unavailableItems.joinToString()}"
)
}

// 3. Calculate total
val productIds = items.map { it.productId }
val products = productRepo.findByIds(productIds)
val totalAmount = items.sumOf { item ->
val product = products.find { it.id == item.productId }!!
product.price * item.quantity
}

// 4. Reserve stock
val stockReserved = inventoryService.reserveStock(items)
if (!stockReserved) {
auditService.log(
"ORDER_CREATION_FAILED",
mapOf("reason" to "STOCK_RESERVATION_FAILED")
)
return@withTenantAndUser OrderResult.Error("Failed to reserve stock")
}

try {
// 5. Process payment
val paymentResult = paymentService.processPayment(
amount = totalAmount,
paymentMethodId = paymentMethodId
)

if (!paymentResult.success) {
// Release stock on payment failure
inventoryService.releaseStock(items)

auditService.log(
"ORDER_CREATION_FAILED",
mapOf("reason" to "PAYMENT_FAILED", "error" to paymentResult.error)
)

return@withTenantAndUser OrderResult.Error(
"Payment failed: ${paymentResult.error}"
)
}

// 6. Create order
val order = Order(
id = generateOrderId(),
tenantId = tenantId,
userId = userId,
items = items,
totalAmount = totalAmount,
status = OrderStatus.CONFIRMED,
createdAt = Instant.now(),
createdBy = userId
)

val createdOrder = orderRepo.create(order)

auditService.log(
"ORDER_CREATED",
mapOf(
"orderId" to createdOrder.id,
"amount" to totalAmount,
"items" to items.size,
"paymentId" to paymentResult.transactionId
)
)

OrderResult.Success(createdOrder)

} catch (e: Exception) {
// Release stock on any exception
inventoryService.releaseStock(items)

auditService.log(
"ORDER_CREATION_FAILED",
mapOf("reason" to "EXCEPTION", "error" to e.message)
)

throw e
}
}

suspend fun getUserOrders(status: OrderStatus? = null): List<Order> =
withTenantAndUser { tenantId, userId ->
orderRepo.findUserOrders(status)
}

suspend fun cancelOrder(orderId: String): CancellationResult =
withTenantAndUser { tenantId, userId ->

val order = orderRepo.findById(orderId)
?: return@withTenantAndUser CancellationResult.Error("Order not found")

if (order.userId != userId) {
auditService.log(
"ORDER_CANCELLATION_DENIED",
mapOf("orderId" to orderId, "reason" to "UNAUTHORIZED")
)
return@withTenantAndUser CancellationResult.Error("Unauthorized")
}

if (order.status != OrderStatus.PENDING && order.status != OrderStatus.CONFIRMED) {
return@withTenantAndUser CancellationResult.Error(
"Cannot cancel order in status ${order.status}"
)
}

// Update status
orderRepo.updateStatus(orderId, OrderStatus.CANCELLED)

// Release stock
inventoryService.releaseStock(order.items)

// Refund payment (if applicable)
paymentService.refundPayment(orderId)

auditService.log(
"ORDER_CANCELLED",
mapOf("orderId" to orderId, "amount" to order.totalAmount)
)

CancellationResult.Success
}
}

sealed class OrderResult {
data class Success(val order: Order) : OrderResult()
data class Error(val message: String) : OrderResult()
}

sealed class CancellationResult {
object Success : CancellationResult()
data class Error(val message: String) : CancellationResult()
}

class PaymentService : BaseContextAwareService() {
private val auditService = AuditService()

suspend fun processPayment(
amount: Double,
paymentMethodId: String
): PaymentResult = withTenantAndUser { tenantId, userId ->

auditService.log(
"PAYMENT_PROCESSING",
mapOf("amount" to amount, "paymentMethodId" to paymentMethodId)
)

try {
// Call external payment gateway
val transactionId = paymentGateway.charge(
amount = amount,
paymentMethodId = paymentMethodId,
metadata = mapOf(
"tenantId" to tenantId,
"userId" to userId
)
)

auditService.log(
"PAYMENT_SUCCESS",
mapOf("amount" to amount, "transactionId" to transactionId)
)

PaymentResult(
success = true,
transactionId = transactionId,
error = null
)

} catch (e: PaymentException) {
auditService.log(
"PAYMENT_FAILED",
mapOf("amount" to amount, "error" to e.message)
)

PaymentResult(
success = false,
transactionId = null,
error = e.message
)
}
}

suspend fun refundPayment(orderId: String) = withTenant { tenantId ->
// Refund logic
auditService.log("PAYMENT_REFUNDED", mapOf("orderId" to orderId))
}
}

data class PaymentResult(
val success: Boolean,
val transactionId: String?,
val error: String?
)

class AuditService : BaseContextAwareService() {
private val auditLog = mutableListOf<AuditEntry>()

suspend fun log(operation: String, data: Map<String, Any>) =
withTenantAndUser { tenantId, userId ->
val context = getContext()

val entry = AuditEntry(
timestamp = Instant.now(),
tenantId = tenantId,
userId = userId,
correlationId = context.correlationId ?: "unknown",
operation = operation,
data = data
)

auditLog.add(entry)
database.insert("audit_log", entry)
}
}

data class AuditEntry(
val timestamp: Instant,
val tenantId: String,
val userId: String,
val correlationId: String,
val operation: String,
val data: Map<String, Any>
)

4. Agent Layer​

val ecommerceAgent = buildAgent {
id = "ecommerce-agent"
name = "E-Commerce Assistant"
description = "Handles order processing and product inquiries"

val orderService = OrderService()
val productRepo = ProductRepository()

llm = anthropic(apiKey = env["ANTHROPIC_API_KEY"]!!) {
model = "claude-3-5-sonnet-20241022"
temperature = 0.7
}

contextAwareTool("create_order") {
description = "Create a new order"
param("items", "array", "Order items with productId and quantity")
param("paymentMethodId", "string", "Payment method ID")

execute { params, context ->
val items = (params["items"] as List<Map<String, Any>>).map { item ->
OrderItem(
productId = item["productId"] as String,
quantity = (item["quantity"] as Number).toInt(),
priceAtPurchase = 0.0 // Will be set by service
)
}
val paymentMethodId = params["paymentMethodId"] as String

when (val result = orderService.createOrder(items, paymentMethodId)) {
is OrderResult.Success -> {
val order = result.order
"""
Order created successfully!
Order ID: ${order.id}
Total: $${order.totalAmount}
Status: ${order.status}
Items: ${order.items.size}
""".trimIndent()
}
is OrderResult.Error -> {
"Failed to create order: ${result.message}"
}
}
}
}

contextAwareTool("get_orders") {
description = "Get user orders"
param("status", "string", "Order status filter (optional)", required = false)

execute { params, context ->
val status = params["status"]?.let {
OrderStatus.valueOf(it as String)
}

val orders = orderService.getUserOrders(status)

if (orders.isEmpty()) {
"No orders found"
} else {
orders.joinToString("\n\n") { order ->
"""
Order #${order.id}
Status: ${order.status}
Total: $${order.totalAmount}
Items: ${order.items.size}
Created: ${order.createdAt}
""".trimIndent()
}
}
}
}

contextAwareTool("cancel_order") {
description = "Cancel an order"
param("orderId", "string", "Order ID to cancel")

execute { params, context ->
val orderId = params["orderId"] as String

when (val result = orderService.cancelOrder(orderId)) {
is CancellationResult.Success -> {
"Order $orderId cancelled successfully. Refund will be processed."
}
is CancellationResult.Error -> {
"Failed to cancel order: ${result.message}"
}
}
}
}

contextAwareTool("search_products") {
description = "Search for products"
param("query", "string", "Search query")

execute { params, context ->
val query = params["query"] as String
// Search implementation
"Search results for: $query"
}
}

instructions = """
You are an e-commerce assistant helping customers with orders and product inquiries.

Capabilities:
- Create orders using create_order tool
- View order history using get_orders tool
- Cancel orders using cancel_order tool
- Search products using search_products tool

Guidelines:
- Always confirm order details before creation
- Verify payment method is valid
- Check stock availability
- Provide clear order status updates
- Handle cancellations gracefully

All operations are automatically scoped to the current user and tenant.
""".trimIndent()
}

5. HTTP Integration​

// Ktor application setup
fun Application.ecommerceRoutes() {
routing {
authenticate("jwt") {
post("/orders") {
val request = call.receive<CreateOrderRequest>()
val jwt = call.principal<JWTPrincipal>()!!

// Extract tenant and user from JWT
val tenantId = jwt.payload.getClaim("tenantId").asString()
val userId = jwt.payload.getClaim("sub").asString()

// Set context for entire request
val result = withAgentContext(
"tenantId" to tenantId,
"userId" to userId,
"correlationId" to UUID.randomUUID().toString()
) {
val comm = Comm(
content = "Create order: ${request.items.size} items",
from = userId,
data = mapOf(
"items" to request.items,
"paymentMethodId" to request.paymentMethodId
)
)

ecommerceAgent.processComm(comm)
}

result.fold(
onSuccess = { response ->
call.respond(HttpStatusCode.Created, response.content)
},
onFailure = { error ->
call.respond(HttpStatusCode.BadRequest, error.message)
}
)
}

get("/orders") {
val jwt = call.principal<JWTPrincipal>()!!
val tenantId = jwt.payload.getClaim("tenantId").asString()
val userId = jwt.payload.getClaim("sub").asString()

val result = withAgentContext(
"tenantId" to tenantId,
"userId" to userId
) {
val comm = Comm(
content = "Get my orders",
from = userId
)

ecommerceAgent.processComm(comm)
}

result.fold(
onSuccess = { response ->
call.respond(HttpStatusCode.OK, response.content)
},
onFailure = { error ->
call.respond(HttpStatusCode.InternalServerError, error.message)
}
)
}
}
}
}

6. Tests​

class OrderServiceTest {
private lateinit var orderService: OrderService
private lateinit var inventoryService: InventoryService

@BeforeTest
fun setup() {
orderService = OrderService()
inventoryService = InventoryService()
setupTestDatabase()
}

@Test
fun `should create order for tenant A without affecting tenant B`() = runTest {
// Tenant A: Create order
val tenantAOrder = withAgentContext(
"tenantId" to "TENANT_A",
"userId" to "user-a1"
) {
orderService.createOrder(
items = listOf(
OrderItem("product-1", 2, 0.0)
),
paymentMethodId = "payment-a1"
)
}

assertTrue(tenantAOrder is OrderResult.Success)
assertEquals("TENANT_A", (tenantAOrder as OrderResult.Success).order.tenantId)

// Tenant B: Create order (isolated!)
val tenantBOrder = withAgentContext(
"tenantId" to "TENANT_B",
"userId" to "user-b1"
) {
orderService.createOrder(
items = listOf(
OrderItem("product-1", 1, 0.0)
),
paymentMethodId = "payment-b1"
)
}

assertTrue(tenantBOrder is OrderResult.Success)
assertEquals("TENANT_B", (tenantBOrder as OrderResult.Success).order.tenantId)

// Verify orders are isolated
val tenantAOrders = withAgentContext(
"tenantId" to "TENANT_A",
"userId" to "user-a1"
) {
orderService.getUserOrders()
}

val tenantBOrders = withAgentContext(
"tenantId" to "TENANT_B",
"userId" to "user-b1"
) {
orderService.getUserOrders()
}

assertEquals(1, tenantAOrders.size)
assertEquals(1, tenantBOrders.size)
assertNotEquals(tenantAOrders[0].id, tenantBOrders[0].id)
}

@Test
fun `should fail when stock unavailable`() = runTest {
withAgentContext(
"tenantId" to "TENANT_A",
"userId" to "user-a1"
) {
// Product has 5 in stock
setupProduct("product-1", stockQuantity = 5)

// Try to order 10
val result = orderService.createOrder(
items = listOf(OrderItem("product-1", 10, 0.0)),
paymentMethodId = "payment-a1"
)

assertTrue(result is OrderResult.Error)
assertTrue((result as OrderResult.Error).message.contains("unavailable"))
}
}

@Test
fun `should release stock when payment fails`() = runTest {
withAgentContext(
"tenantId" to "TENANT_A",
"userId" to "user-a1"
) {
setupProduct("product-1", stockQuantity = 10)

// Mock payment failure
setupPaymentFailure()

val result = orderService.createOrder(
items = listOf(OrderItem("product-1", 5, 0.0)),
paymentMethodId = "payment-fail"
)

assertTrue(result is OrderResult.Error)

// Stock should be released back
val product = productRepo.findById("product-1")
assertEquals(10, product?.stockQuantity)
}
}
}

Key Takeaways​

βœ… Perfect Tenant Isolation - Each tenant's data completely isolated through automatic context scoping βœ… Zero Manual Passing - No need to pass tenantId/userId through layers βœ… Audit Trail - Full correlation tracking across all operations βœ… Transaction Safety - Stock reservations with automatic rollback βœ… Type Safety - Compile-time guarantees for context access


Example 2: Customer Support System​

Scenario​

Multi-tenant customer support platform with:

  • Ticket routing based on user permissions and tenant config
  • SLA tracking and escalation
  • Agent assignment with workload balancing
  • Full audit trail with correlation IDs

Implementation​

1. Domain Models​

data class Ticket(
val id: String,
val tenantId: String,
val customerId: String,
val assignedAgentId: String?,
val priority: Priority,
val status: TicketStatus,
val subject: String,
val description: String,
val category: TicketCategory,
val sla: SLA,
val createdAt: Instant,
val updatedAt: Instant,
val resolvedAt: Instant? = null
)

enum class Priority {
LOW, MEDIUM, HIGH, CRITICAL
}

enum class TicketStatus {
OPEN, ASSIGNED, IN_PROGRESS, WAITING_CUSTOMER, RESOLVED, CLOSED
}

enum class TicketCategory {
TECHNICAL, BILLING, GENERAL, FEATURE_REQUEST
}

data class SLA(
val responseTime: Duration,
val resolutionTime: Duration,
val responseDeadline: Instant,
val resolutionDeadline: Instant
)

data class SupportAgent(
val id: String,
val tenantId: String,
val name: String,
val email: String,
val specializations: List<TicketCategory>,
val currentWorkload: Int,
val maxWorkload: Int,
val status: AgentStatus
)

enum class AgentStatus {
AVAILABLE, BUSY, OFFLINE
}

data class TicketMessage(
val id: String,
val ticketId: String,
val tenantId: String,
val fromUserId: String,
val fromUserType: UserType,
val message: String,
val createdAt: Instant
)

enum class UserType {
CUSTOMER, SUPPORT_AGENT, SYSTEM
}

2. Repository Layer​

class TicketRepository : BaseContextAwareService() {

suspend fun create(ticket: Ticket): Ticket = withTenant { tenantId ->
require(ticket.tenantId == tenantId) { "Tenant ID mismatch" }

database.insert("tickets", ticket)
ticket
}

suspend fun findById(ticketId: String): Ticket? = withTenant { tenantId ->
database.query(
"SELECT * FROM tickets WHERE id = ? AND tenant_id = ?",
ticketId, tenantId
).firstOrNull()
}

suspend fun findByCustomer(customerId: String): List<Ticket> = withTenant { tenantId ->
database.query(
"""
SELECT * FROM tickets
WHERE tenant_id = ? AND customer_id = ?
ORDER BY created_at DESC
""",
tenantId, customerId
)
}

suspend fun findByAgent(agentId: String, status: TicketStatus? = null): List<Ticket> =
withTenant { tenantId ->
if (status != null) {
database.query(
"""
SELECT * FROM tickets
WHERE tenant_id = ? AND assigned_agent_id = ? AND status = ?
ORDER BY priority DESC, created_at ASC
""",
tenantId, agentId, status.name
)
} else {
database.query(
"""
SELECT * FROM tickets
WHERE tenant_id = ? AND assigned_agent_id = ?
ORDER BY priority DESC, created_at ASC
""",
tenantId, agentId
)
}
}

suspend fun findUnassigned(priority: Priority? = null): List<Ticket> = withTenant { tenantId ->
if (priority != null) {
database.query(
"""
SELECT * FROM tickets
WHERE tenant_id = ?
AND status = 'OPEN'
AND assigned_agent_id IS NULL
AND priority = ?
ORDER BY created_at ASC
""",
tenantId, priority.name
)
} else {
database.query(
"""
SELECT * FROM tickets
WHERE tenant_id = ?
AND status = 'OPEN'
AND assigned_agent_id IS NULL
ORDER BY priority DESC, created_at ASC
""",
tenantId
)
}
}

suspend fun updateStatus(ticketId: String, status: TicketStatus) = withTenant { tenantId ->
val now = Instant.now()

database.execute(
"""
UPDATE tickets
SET status = ?,
updated_at = ?,
resolved_at = CASE WHEN ? = 'RESOLVED' THEN ? ELSE resolved_at END
WHERE id = ? AND tenant_id = ?
""",
status.name, now, status.name, now, ticketId, tenantId
)
}

suspend fun assign(ticketId: String, agentId: String) = withTenant { tenantId ->
database.execute(
"""
UPDATE tickets
SET assigned_agent_id = ?,
status = 'ASSIGNED',
updated_at = ?
WHERE id = ? AND tenant_id = ?
""",
agentId, Instant.now(), ticketId, tenantId
)
}

suspend fun findOverdueSLA(): List<Ticket> = withTenant { tenantId ->
val now = Instant.now()

database.query(
"""
SELECT * FROM tickets
WHERE tenant_id = ?
AND status NOT IN ('RESOLVED', 'CLOSED')
AND (
sla_response_deadline < ?
OR sla_resolution_deadline < ?
)
ORDER BY priority DESC
""",
tenantId, now, now
)
}
}

class SupportAgentRepository : BaseContextAwareService() {

suspend fun findAvailable(category: TicketCategory? = null): List<SupportAgent> =
withTenant { tenantId ->
if (category != null) {
database.query(
"""
SELECT * FROM support_agents
WHERE tenant_id = ?
AND status = 'AVAILABLE'
AND current_workload < max_workload
AND ? = ANY(specializations)
ORDER BY current_workload ASC
""",
tenantId, category.name
)
} else {
database.query(
"""
SELECT * FROM support_agents
WHERE tenant_id = ?
AND status = 'AVAILABLE'
AND current_workload < max_workload
ORDER BY current_workload ASC
""",
tenantId
)
}
}

suspend fun findById(agentId: String): SupportAgent? = withTenant { tenantId ->
database.query(
"SELECT * FROM support_agents WHERE id = ? AND tenant_id = ?",
agentId, tenantId
).firstOrNull()
}

suspend fun incrementWorkload(agentId: String) = withTenant { tenantId ->
database.execute(
"""
UPDATE support_agents
SET current_workload = current_workload + 1
WHERE id = ? AND tenant_id = ?
""",
agentId, tenantId
)
}

suspend fun decrementWorkload(agentId: String) = withTenant { tenantId ->
database.execute(
"""
UPDATE support_agents
SET current_workload = GREATEST(current_workload - 1, 0)
WHERE id = ? AND tenant_id = ?
""",
agentId, tenantId
)
}
}

class TicketMessageRepository : BaseContextAwareService() {

suspend fun create(message: TicketMessage): TicketMessage = withTenant { tenantId ->
require(message.tenantId == tenantId) { "Tenant ID mismatch" }

database.insert("ticket_messages", message)
message
}

suspend fun findByTicket(ticketId: String): List<TicketMessage> = withTenant { tenantId ->
database.query(
"""
SELECT * FROM ticket_messages
WHERE ticket_id = ? AND tenant_id = ?
ORDER BY created_at ASC
""",
ticketId, tenantId
)
}
}

3. Service Layer​

class TicketService : BaseContextAwareService() {
private val ticketRepo = TicketRepository()
private val agentRepo = SupportAgentRepository()
private val messageRepo = TicketMessageRepository()
private val slaService = SLAService()
private val auditService = AuditService()

suspend fun createTicket(
subject: String,
description: String,
category: TicketCategory,
priority: Priority = Priority.MEDIUM
): Ticket = withTenantAndUser { tenantId, userId ->

// Calculate SLA based on priority
val sla = slaService.calculateSLA(priority)

val ticket = Ticket(
id = generateTicketId(),
tenantId = tenantId,
customerId = userId,
assignedAgentId = null,
priority = priority,
status = TicketStatus.OPEN,
subject = subject,
description = description,
category = category,
sla = sla,
createdAt = Instant.now(),
updatedAt = Instant.now()
)

val created = ticketRepo.create(ticket)

auditService.log(
"TICKET_CREATED",
mapOf(
"ticketId" to created.id,
"priority" to priority.name,
"category" to category.name
)
)

// Auto-assign if possible
tryAutoAssign(created.id, category)

created
}

private suspend fun tryAutoAssign(ticketId: String, category: TicketCategory) =
withTenant { tenantId ->
// Find available agents with matching specialization
val availableAgents = agentRepo.findAvailable(category)

if (availableAgents.isNotEmpty()) {
// Assign to agent with lowest workload
val agent = availableAgents.first()

ticketRepo.assign(ticketId, agent.id)
agentRepo.incrementWorkload(agent.id)

auditService.log(
"TICKET_AUTO_ASSIGNED",
mapOf(
"ticketId" to ticketId,
"agentId" to agent.id,
"agentWorkload" to agent.currentWorkload
)
)
}
}

suspend fun assignTicket(ticketId: String, agentId: String): AssignmentResult =
withTenant { tenantId ->

val ticket = ticketRepo.findById(ticketId)
?: return@withTenant AssignmentResult.Error("Ticket not found")

if (ticket.assignedAgentId != null) {
return@withTenant AssignmentResult.Error("Ticket already assigned")
}

val agent = agentRepo.findById(agentId)
?: return@withTenant AssignmentResult.Error("Agent not found")

if (agent.currentWorkload >= agent.maxWorkload) {
return@withTenant AssignmentResult.Error("Agent at max capacity")
}

ticketRepo.assign(ticketId, agentId)
agentRepo.incrementWorkload(agentId)

auditService.log(
"TICKET_ASSIGNED",
mapOf(
"ticketId" to ticketId,
"agentId" to agentId
)
)

AssignmentResult.Success
}

suspend fun addMessage(
ticketId: String,
message: String,
fromUserType: UserType
): TicketMessage = withTenantAndUser { tenantId, userId ->

val ticket = ticketRepo.findById(ticketId)
?: throw IllegalArgumentException("Ticket not found")

val ticketMessage = TicketMessage(
id = generateMessageId(),
ticketId = ticketId,
tenantId = tenantId,
fromUserId = userId,
fromUserType = fromUserType,
message = message,
createdAt = Instant.now()
)

val created = messageRepo.create(ticketMessage)

// Update ticket status if customer responds
if (fromUserType == UserType.CUSTOMER && ticket.status == TicketStatus.WAITING_CUSTOMER) {
ticketRepo.updateStatus(ticketId, TicketStatus.IN_PROGRESS)
}

auditService.log(
"TICKET_MESSAGE_ADDED",
mapOf(
"ticketId" to ticketId,
"messageId" to created.id,
"userType" to fromUserType.name
)
)

created
}

suspend fun resolveTicket(ticketId: String): ResolutionResult =
withTenantAndUser { tenantId, userId ->

val ticket = ticketRepo.findById(ticketId)
?: return@withTenantAndUser ResolutionResult.Error("Ticket not found")

if (ticket.status == TicketStatus.RESOLVED || ticket.status == TicketStatus.CLOSED) {
return@withTenantAndUser ResolutionResult.Error("Ticket already resolved")
}

ticketRepo.updateStatus(ticketId, TicketStatus.RESOLVED)

// Decrement agent workload
ticket.assignedAgentId?.let { agentId ->
agentRepo.decrementWorkload(agentId)
}

auditService.log(
"TICKET_RESOLVED",
mapOf(
"ticketId" to ticketId,
"resolvedBy" to userId,
"resolutionTime" to Duration.between(ticket.createdAt, Instant.now()).toMinutes()
)
)

ResolutionResult.Success
}

suspend fun getMyTickets(status: TicketStatus? = null): List<Ticket> =
withTenantAndUser { tenantId, userId ->
// Check if user is customer or agent
val context = getContext()
val userRole = context.getAs<String>("userRole")

when (userRole) {
"customer" -> ticketRepo.findByCustomer(userId)
"agent" -> ticketRepo.findByAgent(userId, status)
else -> emptyList()
}
}

suspend fun escalateOverdueTickets() = withTenant { tenantId ->
val overdueTickets = ticketRepo.findOverdueSLA()

overdueTickets.forEach { ticket ->
auditService.log(
"TICKET_SLA_BREACH",
mapOf(
"ticketId" to ticket.id,
"priority" to ticket.priority.name,
"responseDeadline" to ticket.sla.responseDeadline,
"resolutionDeadline" to ticket.sla.resolutionDeadline
)
)

// Escalate priority if not already critical
if (ticket.priority != Priority.CRITICAL) {
// Escalation logic
notifyManagement(ticket)
}
}
}
}

sealed class AssignmentResult {
object Success : AssignmentResult()
data class Error(val message: String) : AssignmentResult()
}

sealed class ResolutionResult {
object Success : ResolutionResult()
data class Error(val message: String) : ResolutionResult()
}

class SLAService : BaseContextAwareService() {

fun calculateSLA(priority: Priority): SLA {
val now = Instant.now()

val (responseTime, resolutionTime) = when (priority) {
Priority.CRITICAL -> Duration.ofMinutes(15) to Duration.ofHours(4)
Priority.HIGH -> Duration.ofHours(1) to Duration.ofHours(24)
Priority.MEDIUM -> Duration.ofHours(4) to Duration.ofDays(3)
Priority.LOW -> Duration.ofHours(24) to Duration.ofDays(7)
}

return SLA(
responseTime = responseTime,
resolutionTime = resolutionTime,
responseDeadline = now.plus(responseTime),
resolutionDeadline = now.plus(resolutionTime)
)
}
}

Key Takeaways​

βœ… Role-Based Context - Different views for customers vs. agents βœ… SLA Tracking - Automatic deadline calculation and monitoring βœ… Workload Balancing - Smart agent assignment based on capacity βœ… Complete Audit Trail - Full correlation tracking across ticket lifecycle


Example 3: Financial Transaction Processing​

Scenario​

Multi-tenant financial platform with:

  • Transaction processing with double-entry bookkeeping
  • Compliance and audit requirements
  • Real-time fraud detection
  • Immutable audit trail

Implementation​

1. Domain Models​

data class Account(
val id: String,
val tenantId: String,
val userId: String,
val accountNumber: String,
val accountType: AccountType,
val currency: Currency,
val balance: BigDecimal,
val status: AccountStatus,
val createdAt: Instant
)

enum class AccountType {
CHECKING, SAVINGS, CREDIT, INVESTMENT
}

enum class AccountStatus {
ACTIVE, SUSPENDED, CLOSED
}

data class Transaction(
val id: String,
val tenantId: String,
val fromAccountId: String,
val toAccountId: String,
val amount: BigDecimal,
val currency: Currency,
val type: TransactionType,
val status: TransactionStatus,
val description: String,
val metadata: Map<String, Any>,
val createdAt: Instant,
val processedAt: Instant?,
val processedBy: String?
)

enum class TransactionType {
TRANSFER, PAYMENT, WITHDRAWAL, DEPOSIT, FEE
}

enum class TransactionStatus {
PENDING, PROCESSING, COMPLETED, FAILED, REVERSED
}

data class LedgerEntry(
val id: String,
val tenantId: String,
val transactionId: String,
val accountId: String,
val entryType: EntryType,
val amount: BigDecimal,
val currency: Currency,
val balanceBefore: BigDecimal,
val balanceAfter: BigDecimal,
val createdAt: Instant
)

enum class EntryType {
DEBIT, CREDIT
}

2. Repository Layer​

class AccountRepository : BaseContextAwareService() {

suspend fun findByUser(): List<Account> = withTenantAndUser { tenantId, userId ->
database.query(
"""
SELECT * FROM accounts
WHERE tenant_id = ? AND user_id = ?
ORDER BY created_at DESC
""",
tenantId, userId
)
}

suspend fun findById(accountId: String): Account? = withTenantAndUser { tenantId, userId ->
database.query(
"""
SELECT * FROM accounts
WHERE id = ? AND tenant_id = ? AND user_id = ?
""",
accountId, tenantId, userId
).firstOrNull()
}

suspend fun updateBalance(
accountId: String,
newBalance: BigDecimal
) = withTenant { tenantId ->
database.execute(
"""
UPDATE accounts
SET balance = ?
WHERE id = ? AND tenant_id = ?
""",
newBalance, accountId, tenantId
)
}

suspend fun lockForUpdate(accountId: String): Account? = withTenant { tenantId ->
database.query(
"""
SELECT * FROM accounts
WHERE id = ? AND tenant_id = ?
FOR UPDATE
""",
accountId, tenantId
).firstOrNull()
}
}

class TransactionRepository : BaseContextAwareService() {

suspend fun create(transaction: Transaction): Transaction = withTenant { tenantId ->
require(transaction.tenantId == tenantId) { "Tenant ID mismatch" }

database.insert("transactions", transaction)
transaction
}

suspend fun findById(transactionId: String): Transaction? = withTenant { tenantId ->
database.query(
"SELECT * FROM transactions WHERE id = ? AND tenant_id = ?",
transactionId, tenantId
).firstOrNull()
}

suspend fun findByAccount(accountId: String, limit: Int = 50): List<Transaction> =
withTenant { tenantId ->
database.query(
"""
SELECT * FROM transactions
WHERE tenant_id = ?
AND (from_account_id = ? OR to_account_id = ?)
ORDER BY created_at DESC
LIMIT ?
""",
tenantId, accountId, accountId, limit
)
}

suspend fun updateStatus(
transactionId: String,
status: TransactionStatus
) = withTenantAndUser { tenantId, userId ->
database.execute(
"""
UPDATE transactions
SET status = ?,
processed_at = ?,
processed_by = ?
WHERE id = ? AND tenant_id = ?
""",
status.name, Instant.now(), userId, transactionId, tenantId
)
}
}

class LedgerRepository : BaseContextAwareService() {

suspend fun createEntry(entry: LedgerEntry): LedgerEntry = withTenant { tenantId ->
require(entry.tenantId == tenantId) { "Tenant ID mismatch" }

database.insert("ledger_entries", entry)
entry
}

suspend fun findByTransaction(transactionId: String): List<LedgerEntry> =
withTenant { tenantId ->
database.query(
"""
SELECT * FROM ledger_entries
WHERE transaction_id = ? AND tenant_id = ?
ORDER BY created_at ASC
""",
transactionId, tenantId
)
}

suspend fun findByAccount(accountId: String, limit: Int = 100): List<LedgerEntry> =
withTenant { tenantId ->
database.query(
"""
SELECT * FROM ledger_entries
WHERE account_id = ? AND tenant_id = ?
ORDER BY created_at DESC
LIMIT ?
""",
accountId, tenantId, limit
)
}
}

3. Service Layer​

class TransactionService : BaseContextAwareService() {
private val accountRepo = AccountRepository()
private val transactionRepo = TransactionRepository()
private val ledgerRepo = LedgerRepository()
private val fraudService = FraudDetectionService()
private val auditService = AuditService()

suspend fun transfer(
fromAccountId: String,
toAccountId: String,
amount: BigDecimal,
description: String
): TransferResult = withTenantAndUser { tenantId, userId ->

auditService.log(
"TRANSFER_INITIATED",
mapOf(
"fromAccount" to fromAccountId,
"toAccount" to toAccountId,
"amount" to amount.toString()
)
)

// Run fraud detection
val fraudCheck = fraudService.checkTransfer(fromAccountId, toAccountId, amount)
if (fraudCheck.isFraudulent) {
auditService.log(
"TRANSFER_BLOCKED_FRAUD",
mapOf(
"reason" to fraudCheck.reason,
"riskScore" to fraudCheck.riskScore
)
)
return@withTenantAndUser TransferResult.FraudDetected(fraudCheck.reason)
}

// Begin transaction
database.transaction {
// Lock accounts for update (prevents concurrent modifications)
val fromAccount = accountRepo.lockForUpdate(fromAccountId)
?: return@transaction TransferResult.Error("From account not found")

val toAccount = accountRepo.lockForUpdate(toAccountId)
?: return@transaction TransferResult.Error("To account not found")

// Validate
if (fromAccount.status != AccountStatus.ACTIVE) {
return@transaction TransferResult.Error("From account not active")
}

if (toAccount.status != AccountStatus.ACTIVE) {
return@transaction TransferResult.Error("To account not active")
}

if (fromAccount.balance < amount) {
auditService.log(
"TRANSFER_FAILED_INSUFFICIENT_FUNDS",
mapOf(
"required" to amount.toString(),
"available" to fromAccount.balance.toString()
)
)
return@transaction TransferResult.InsufficientFunds
}

// Create transaction record
val transaction = Transaction(
id = generateTransactionId(),
tenantId = tenantId,
fromAccountId = fromAccountId,
toAccountId = toAccountId,
amount = amount,
currency = fromAccount.currency,
type = TransactionType.TRANSFER,
status = TransactionStatus.PROCESSING,
description = description,
metadata = mapOf(
"initiatedBy" to userId,
"fraudCheck" to fraudCheck.riskScore
),
createdAt = Instant.now(),
processedAt = null,
processedBy = null
)

transactionRepo.create(transaction)

// Calculate new balances
val newFromBalance = fromAccount.balance - amount
val newToBalance = toAccount.balance + amount

// Create ledger entries (double-entry bookkeeping)
ledgerRepo.createEntry(
LedgerEntry(
id = generateEntryId(),
tenantId = tenantId,
transactionId = transaction.id,
accountId = fromAccountId,
entryType = EntryType.DEBIT,
amount = amount,
currency = fromAccount.currency,
balanceBefore = fromAccount.balance,
balanceAfter = newFromBalance,
createdAt = Instant.now()
)
)

ledgerRepo.createEntry(
LedgerEntry(
id = generateEntryId(),
tenantId = tenantId,
transactionId = transaction.id,
accountId = toAccountId,
entryType = EntryType.CREDIT,
amount = amount,
currency = toAccount.currency,
balanceBefore = toAccount.balance,
balanceAfter = newToBalance,
createdAt = Instant.now()
)
)

// Update balances
accountRepo.updateBalance(fromAccountId, newFromBalance)
accountRepo.updateBalance(toAccountId, newToBalance)

// Mark transaction as completed
transactionRepo.updateStatus(transaction.id, TransactionStatus.COMPLETED)

auditService.log(
"TRANSFER_COMPLETED",
mapOf(
"transactionId" to transaction.id,
"amount" to amount.toString(),
"fromBalance" to newFromBalance.toString(),
"toBalance" to newToBalance.toString()
)
)

TransferResult.Success(transaction.id)
}
}

suspend fun getAccountTransactions(
accountId: String,
limit: Int = 50
): List<Transaction> = withTenantAndUser { tenantId, userId ->

// Verify user owns account
val account = accountRepo.findById(accountId)
?: throw IllegalArgumentException("Account not found")

transactionRepo.findByAccount(accountId, limit)
}

suspend fun getTransactionDetails(transactionId: String): TransactionDetails? =
withTenant { tenantId ->

val transaction = transactionRepo.findById(transactionId)
?: return@withTenant null

val ledgerEntries = ledgerRepo.findByTransaction(transactionId)

TransactionDetails(
transaction = transaction,
ledgerEntries = ledgerEntries
)
}
}

sealed class TransferResult {
data class Success(val transactionId: String) : TransferResult()
data class Error(val message: String) : TransferResult()
object InsufficientFunds : TransferResult()
data class FraudDetected(val reason: String) : TransferResult()
}

data class TransactionDetails(
val transaction: Transaction,
val ledgerEntries: List<LedgerEntry>
)

class FraudDetectionService : BaseContextAwareService() {

suspend fun checkTransfer(
fromAccountId: String,
toAccountId: String,
amount: BigDecimal
): FraudCheckResult = withTenantAndUser { tenantId, userId ->

// Simple fraud detection logic (in production, use ML models)
var riskScore = 0.0

// Check transaction amount
if (amount > BigDecimal(10000)) {
riskScore += 0.3
}

// Check transaction frequency
val recentTransactions = transactionRepo.findByAccount(fromAccountId, 10)
val recentCount = recentTransactions.filter {
it.createdAt.isAfter(Instant.now().minus(Duration.ofHours(1)))
}.size

if (recentCount > 5) {
riskScore += 0.4
}

// Check velocity
val recentAmount = recentTransactions
.filter { it.createdAt.isAfter(Instant.now().minus(Duration.ofHours(24))) }
.sumOf { it.amount }

if (recentAmount > BigDecimal(50000)) {
riskScore += 0.5
}

val isFraudulent = riskScore > 0.7

if (isFraudulent) {
auditService.log(
"FRAUD_DETECTED",
mapOf(
"riskScore" to riskScore,
"amount" to amount.toString(),
"recentCount" to recentCount,
"recentAmount" to recentAmount.toString()
)
)
}

FraudCheckResult(
isFraudulent = isFraudulent,
riskScore = riskScore,
reason = if (isFraudulent) "High risk transaction detected" else ""
)
}
}

data class FraudCheckResult(
val isFraudulent: Boolean,
val riskScore: Double,
val reason: String
)

Key Takeaways​

βœ… Immutable Audit Trail - Complete ledger with correlation IDs βœ… Double-Entry Bookkeeping - Financial integrity guarantees βœ… Fraud Detection - Real-time risk analysis with context βœ… ACID Transactions - Consistent state with row-level locking


Common Patterns​

1. HTTP Request Handler Pattern​

// Set context once at HTTP boundary
suspend fun handleRequest(request: HttpRequest) {
val jwt = extractJWT(request)

withAgentContext(
"tenantId" to jwt.tenantId,
"userId" to jwt.userId,
"correlationId" to UUID.randomUUID().toString(),
"sessionId" to request.sessionId
) {
// All operations have full context
processRequest(request)
}
}

2. Context Enrichment Pattern​

// Progressive enrichment as more data becomes available
suspend fun processWithEnrichment(request: Request) {
withAgentContext("tenantId" to request.tenantId) {

withEnrichedContext("userId" to authenticateUser(request)) {

withEnrichedContext("permissions" to loadPermissions()) {

// Full enriched context available
processRequest(request)
}
}
}
}

3. Multi-Tenant Test Pattern​

@Test
fun `should isolate tenants completely`() = runTest {
// Tenant A operations
val resultA = withAgentContext("tenantId" to "A", "userId" to "user-a") {
service.createResource()
}

// Tenant B operations (isolated!)
val resultB = withAgentContext("tenantId" to "B", "userId" to "user-b") {
service.createResource()
}

// Verify isolation
assertNotEquals(resultA.tenantId, resultB.tenantId)
}

4. Audit Trail Pattern​

class AuditService : BaseContextAwareService() {
suspend fun log(operation: String, data: Map<String, Any>) =
withTenantAndUser { tenantId, userId ->
val context = getContext()

auditLog.insert(AuditEntry(
correlationId = context.correlationId ?: "unknown",
tenantId = tenantId,
userId = userId,
operation = operation,
data = data,
timestamp = Instant.now()
))
}
}

API Quick Reference​

Common Mistakes vs Correct Usage​

SpiceError Factory Functions​

// ❌ WRONG - These functions don't exist!
SpiceError.configurationError("message", "field") // ❌ NO!
SpiceError.toolError("message", cause = e) // ❌ Wrong signature!

// βœ… CORRECT - Use these instead
SpiceError.configError("message", field = "apiKey") // βœ… YES
SpiceError.toolError("message", toolName = "calculator") // βœ… YES
SpiceError.agentError("message", agentId = "my-agent") // βœ… YES
SpiceError.validationError("message", field = "username") // βœ… YES
SpiceError.networkError("message", statusCode = 500) // βœ… YES
SpiceError.timeoutError("message", timeoutMs = 5000L) // βœ… YES
SpiceError.authError("message", provider = "oauth") // βœ… YES
SpiceError.rateLimitError("message", retryAfterMs = 60000L) // βœ… YES

Logger with SpiceError​

val error = SpiceError.configError("Config failed")

// ❌ WRONG - SpiceError is not Throwable
logger.error(error) { "Config failed" } // ❌ Compile error!

// βœ… CORRECT - Use these instead
logger.error { "Config failed: ${error.message}" } // βœ… Option 1
logger.error { "Error: $error" } // βœ… Option 2
logger.error(error.toException()) { "Config failed" } // βœ… Option 3

// If error has cause
if (error.cause != null) {
logger.error(error.cause) { error.message } // βœ… Option 4
}

ContextExtension Signature​

// ❌ WRONG - Incorrect parameter/return type
class MyExtension : ContextExtension {
override suspend fun enrich(context: Map<String, Any>): Map<String, Any> { // ❌ NO!
return context
}
}

// βœ… CORRECT - Use AgentContext
class MyExtension : ContextExtension {
override val key = "my-extension"

override suspend fun enrich(context: AgentContext): AgentContext { // βœ… YES!
return context.with("extra", "value")
}
}

currentAgentContext() Usage​

// ❌ WRONG - Non-existent functions
val context = getCurrentContext() // ❌ NO!
val context = AgentContext.current() // ❌ NO!

// βœ… CORRECT - Use these instead
val context = currentAgentContext() // βœ… Nullable
val context = requireAgentContext() // βœ… Throws if missing
val tenantId = currentTenantId() // βœ… Convenience function
val userId = currentUserId() // βœ… Convenience function

Import Paths​

// ❌ WRONG - These packages don't exist
import io.github.noailabs.spice.flow.* // ❌ NO!
import io.github.noailabs.spice.context.current // ❌ NO!

// βœ… CORRECT - Use these instead
import io.github.noailabs.spice.* // βœ… Core types
import io.github.noailabs.spice.AgentContext
import io.github.noailabs.spice.dsl.withAgentContext
import io.github.noailabs.spice.dsl.currentAgentContext
import io.github.noailabs.spice.context.ContextExtension
import io.github.noailabs.spice.error.SpiceError
import io.github.noailabs.spice.error.SpiceResult

BaseContextAwareService Helpers​

class MyService : BaseContextAwareService() {

// βœ… Require tenant ID (throws if missing)
suspend fun getTenantData() = withTenant { tenantId ->
// tenantId is String (non-null)
database.query("SELECT * FROM data WHERE tenant_id = ?", tenantId)
}

// βœ… Require both tenant and user ID
suspend fun getUserData() = withTenantAndUser { tenantId, userId ->
// Both are String (non-null)
database.query(
"SELECT * FROM data WHERE tenant_id = ? AND user_id = ?",
tenantId, userId
)
}

// βœ… Access full context
suspend fun getFullContext(): AgentContext {
return getContext() // Returns AgentContext
}
}

SpiceResult Handling​

// βœ… Creating results
val success = SpiceResult.success(myValue)
val failure = SpiceResult.failure(SpiceError.configError("Failed"))

// βœ… Checking results
if (result.isSuccess) { /* ... */ }
if (result.isFailure) { /* ... */ }

// βœ… Getting values
result.fold(
onSuccess = { value -> println("Success: $value") },
onFailure = { error -> println("Error: ${error.message}") }
)

val value = result.getOrNull() // Nullable
val value = result.getOrThrow() // Throws if failure
val value = result.getOrElse { defaultValue }

// βœ… Transforming results
val mapped = result.map { it.toString() }
val recovered = result.recover { error -> defaultValue }

Complete Example: All Patterns Together​

import io.github.noailabs.spice.AgentContext
import io.github.noailabs.spice.dsl.withAgentContext
import io.github.noailabs.spice.dsl.currentAgentContext
import io.github.noailabs.spice.error.SpiceError
import io.github.noailabs.spice.error.SpiceResult
import io.github.noailabs.spice.context.BaseContextAwareService

class ProductionService : BaseContextAwareService() {
private val logger = LoggerFactory.getLogger(javaClass)

/**
* βœ… Production-ready method with all best practices
*/
suspend fun processOrder(orderId: String): SpiceResult<Order> =
withTenantAndUser { tenantId, userId ->

logger.info { "Processing order: $orderId for tenant: $tenantId, user: $userId" }

try {
// Business logic here
val order = loadOrder(orderId)

if (order == null) {
return@withTenantAndUser SpiceResult.failure(
SpiceError.validationError(
message = "Order not found",
field = "orderId",
actualValue = orderId
).withContext(
"tenantId" to tenantId,
"userId" to userId
)
)
}

// Success
logger.info { "Order processed successfully: $orderId" }
SpiceResult.success(order)

} catch (e: Exception) {
logger.error(e) { "Failed to process order: $orderId" }

SpiceResult.failure(
SpiceError.agentError(
message = "Order processing failed: ${e.message}",
cause = e
).withContext(
"tenantId" to tenantId,
"userId" to userId,
"orderId" to orderId
)
)
}
}
}

// βœ… Usage at HTTP boundary
@PostMapping("/orders/{orderId}/process")
suspend fun processOrder(
@PathVariable orderId: String,
@RequestHeader("X-Tenant-ID") tenantId: String,
@RequestHeader("X-User-ID") userId: String
): ResponseEntity<OrderResponse> = withAgentContext(
"tenantId" to tenantId,
"userId" to userId,
"requestId" to UUID.randomUUID().toString()
) {
val service = ProductionService()

service.processOrder(orderId).fold(
onSuccess = { order ->
ResponseEntity.ok(OrderResponse.from(order))
},
onFailure = { error ->
ResponseEntity
.status(mapErrorToHttpStatus(error))
.body(ErrorResponse.from(error))
}
)
}

Next Steps​