Component Playbook

Component Playbook

Use this when deciding which Akka SDK component to create and how to keep it clean.

Decision Table

NeedPreferClean-Code Rule
Public JSON APIHTTP endpointEndpoint translates; domain decides
Durable state by idKey Value EntityState is immutable and has an explicit empty state
Auditable state historyEvent Sourced EntityEvents are business facts with stable type names
Long-running processWorkflowSteps are named business transitions
Query by attributesViewModel the read access pattern explicitly
External event ingestionConsumerHandler is idempotent
Scheduled actionTimerTime is part of the business model
LLM-backed reasoningAgentNon-determinism is isolated and evaluated

HTTP Endpoints

Clean endpoint checklist:

  • Route path is named after a resource or business action.
  • ACL/auth decision is visible at the class or method boundary.
  • Request DTOs are public contracts, not entity state.
  • Endpoint calls one application-level operation per route.
  • Expected failures are translated into public errors.
  • No durable state is stored in endpoint fields.

Bad endpoint names:

@HttpEndpoint("/api")
class ApiEndpoint {}

Good endpoint names:

@HttpEndpoint("/carts")
class ShoppingCartEndpoint {}

Entities

Clean entity checklist:

  • @Component(id = "...") is stable and business-named.
  • emptyState() exists for key value entities.
  • State records are immutable.
  • Command names are verbs in the business language.
  • Read-only handlers use read-only effects.
  • Domain calculations are delegated to domain objects.
  • External side effects are deliberate and protected from duplicate execution.

Entity smell:

public Effect<Done> update(UpdateCommand command)

Better:

public Effect<Done> reserveInventory(ReserveInventory command)

Event Sourced Entities

Clean event checklist:

  • Events are facts that already happened.
  • Event names are past tense: OrderPaid, InventoryReserved.
  • Event payloads are minimal but sufficient to rebuild state.
  • Type names are stable.
  • Command validation happens before events are persisted.
  • Event handlers are deterministic and side-effect free.

Bad event:

record UpdateOrder(String field, String value) implements OrderEvent {}

Good events:

record ShippingAddressChanged(Address address) implements OrderEvent {}
record OrderPaid(PaymentReference paymentReference) implements OrderEvent {}

Workflows

Clean workflow checklist:

  • Workflow id maps to one business process instance.
  • Steps are named after business transitions.
  • State records what operators need to understand progress.
  • Waiting states are explicit.
  • Timeouts and retries are part of the design.
  • Compensation is modeled, not hidden in catch blocks.
  • Human intervention paths are command handlers with clear guards.

Workflow smell:

private StepEffect step2() { ... }

Better:

@StepName("capture-payment")
private StepEffect capturePayment(CapturePayment input) { ... }

Views

Clean view checklist:

  • View name describes the query: orders-by-customer.
  • Query method names describe the access pattern.
  • Row model is a projection, not a leaked aggregate unless deliberate.
  • The endpoint using the view documents eventual consistency when relevant.
  • Incompatible query or table changes are treated as migrations.

Consumers

Clean consumer checklist:

  • Message source is obvious from the annotation and class name.
  • Handler is idempotent.
  • Poison-message behavior is explicit.
  • External side effects can survive retries.
  • Business correlation ids are logged.

Agents

Clean agent checklist:

  • Agent role is narrow.
  • Prompt construction is named and tested where possible.
  • Tool calls are explicit and bounded.
  • Session memory semantics are documented.
  • Deterministic business rules stay outside the agent.
  • Evaluation cases cover expected model behavior.

Agent smell:

class BusinessRulesAgent extends Agent { ... }

Better:

class SupportIntentClassifierAgent extends Agent { ... }

Timers

Clean timer checklist:

  • Timer name says why time matters.
  • Scheduling command is separate from the action being scheduled.
  • Repeated timers are idempotent.
  • Cancellation path is explicit.
  • Time values are constants or configuration, not magic numbers.