Krizaka
Documentation
Architecture

Business Layer

For developers

Where a request becomes business: the Intention, the use-cases that serve it, the dispatcher that checks who may run what, and where each piece lives (ports & adapters). Generated from orazaka-business.

🤖 Generated from code by scripts/generate-docs.mjs — do not hand-edit. Run orazaka docs build to refresh.

orazaka-business (repository orazaka-ai-engine) is the App Factory of the engine: the layer that decides what a request is for, never how a model answers it. Every request a client sends becomes one immutable Intention; the UseCaseDispatcher resolves it to the UseCase that serves its capability, checks the use-case's RBAC policy against the actor's authorities and runs it. A use-case coordinates the core's inbound ports (AiClient, the Studio run API…) and holds no model logic — that stays in orazaka-core and the interceptor pipeline.

Participants
  • Client · CLI · agent
  • orazaka-conversation-service
  • orazaka-business
  • orazaka-core
  1. 1
    Client · CLI · agent→orazaka-conversation-service

    POST /api/v1/intent (IntentController)

  2. 2
    orazaka-conversation-service→orazaka-business

    an immutable Intention

  3. 3
    orazaka-businessinside orazaka-business

    UseCaseDispatcher resolves the UseCase by capability (UseCaseRegistry)

  4. 4
    orazaka-businessinside orazaka-business

    the use-case's RBAC policy, checked against the actor's authorities

  5. 5
    orazaka-business→orazaka-core

    the use-case runs through an inbound port (AiClient, the Studio run API…)

Use-cases are not packs. A use-case is a technical entry point of the engine, written in Java. The business offers customers install — Studios, grouped in packs — are data: see Packs & Studios.

Where things live

Ports & adapters, the same layout in every Orazaka module: api is the contract other modules may import, application implements it, domain holds the model and the ports, and the use-cases are plain classes discovered by Spring — adding one changes neither the core nor the router.

PackageTypeKindRole
.BusinessAutoConfigurationclassSpring Boot auto-configuration for the orazaka-business module — the App Factory wiring.
apiAgentPayloadrecordPayload for a Capability#AGENT intention — a goal the core's agent loop will plan and execute (plan→act→observe).
apiCapabilityenumThe high-level capability an Intention targets.
apiChatPayloadrecordPayload for a Capability#CHAT intention.
apiExecutionModeenumExecution mode of an Intention: SYNC for low-latency interactive responses (including token streaming), ASYNC for heavy/deferred work dispatched to a worker.
apiImagePayloadrecordPayload for a Capability#IMAGE intention.
apiIntentionrecordThe immutable entry unit of the platform — the single thing the router translates transport into and hands to the UseCaseDispatcher.
apiIntentionContextrecordAmbient context of an Intention: the conversation session, the acting principal (opaque actorId — no cross-context identity coupling), the actor's resolved authorities (for RBAC), and resolved preferences / environment signals.
apiIntentionTypeenumCQRS pivot of an Intention: COMMAND mutates state, QUERY only reads.
apiPayloadinterfaceSealed hierarchy of the typed payloads an Intention can carry.
apiPlanningModeenumHow a use-case resolves its execution plan: DETERMINISTIC runs a fixed workflow (DAG), AGENTIC delegates planning to the core's agent loop (plan→act→observe).
apiRbacPolicyrecordRBAC requirement a UseCaseDescriptor declares: the authorities an actor must hold to run the use-case.
apiStudioPayloadrecordPayload for a Capability#STUDIO intention — one run of an installed Studio (ADR-034).
apiUseCaseinterfaceSPI extension point — adding a product capability means implementing a UseCase and declaring its UseCaseDescriptor.
apiUseCaseContextrecordExecution context handed to a UseCase, derived by the dispatcher from the Intention and the resolved security context.
apiUseCaseDescriptorrecordMetadata describing a UseCase — the metadata-driven contract the UseCaseRegistry matches intentions against and the auto-docs surface.
apiUseCaseDispatcherinterfaceInbound port — the single entry the router calls after translating transport into an Intention.
apiUseCasePayloadinterfaceMarker for a typed input accepted by a UseCase.
apiUseCaseRegistryinterfaceInbound port — the auto-discovered catalogue of registered UseCases.
apiUseCaseResolutionExceptionclassThrown by the UseCaseDispatcher when an Intention cannot be served — no registered UseCase matches it (ERR-410) or RBAC denies the actor (ERR-403).
applicationSpringUseCaseRegistryclassAuto-discovering UseCaseRegistry: it is handed every UseCase bean registered in the context and resolves an Intention to the first use-case whose descriptor serves the intention's capability.
applicationUseCaseDispatcherImplclassApp-Factory dispatcher: resolves an Intention to its UseCase via the UseCaseRegistry, enforces the descriptor's RBAC against the actor's authorities, then executes the use-case with a context derived from the intention.
domain/modelWorkflowContextrecordRich, self-validating domain context for Workflow orchestration.
domain/portWorkflowOrchestratorinterfaceBusiness-owned port interface for sovereign workflow orchestration.
promptMarkdownPromptResolverclassThread-safe, cached resolver for Git-tracked Markdown prompt templates.
usecases/chatChatAssistantUseCaseclassReference use-case — a synchronous chat assistant.
usecases/imageImageGenerationUseCaseclassSecond reference use-case — image generation.
usecases/studioStudioRunUseCaseclassRuns an installed Studio as a first-class Intention (ADR-034 §9.2).

The contract

Intention

The immutable entry unit of the platform — the single thing the router translates transport into and hands to the UseCaseDispatcher.

FieldTypeMeaning
idStringCorrelation id (auto-generated when absent) — used for observability.
typeIntentionTypeCQRS pivot (COMMAND mutates, QUERY reads).
modeExecutionModeSYNC (interactive) or ASYNC (deferred to a worker).
capabilityCapabilityThe targeted capability.
goalStringShort human/business statement of intent.
payloadPayloadThe typed payload (required).
contextIntentionContextAmbient session/actor/preferences context (required).

IntentionContext

Ambient context of an Intention: the conversation session, the acting principal (opaque actorId — no cross-context identity coupling), the actor's resolved authorities (for RBAC), and resolved preferences / environment signals.

FieldTypeMeaning
sessionIdStringConversation/session id (may be null for one-shot intentions).
actorIdStringOpaque actor reference resolved by the router from the security context.
authoritiesSet<String>The actor's granted authorities; never null.
preferencesMap<String, Object>Resolved user preferences and environment signals; never null.

UseCaseDescriptor

Metadata describing a UseCase — the metadata-driven contract the UseCaseRegistry matches intentions against and the auto-docs surface.

FieldTypeMeaning
idStringStable use-case id (required, non-blank).
capabilityCapabilityThe capability this use-case serves (required).
personasSet<String>Persona keys whose prompt fragments apply; never null.
planningPlanningModeDeterministic workflow vs delegated agentic planning (required).
requiredToolsSet<String>Tool ids the use-case needs available; never null.
rbacRbacPolicyRBAC policy gating execution (required).

UseCaseContext

Execution context handed to a UseCase, derived by the dispatcher from the Intention and the resolved security context.

FieldTypeMeaning
intentionIdStringCorrelation id of the originating intention.
actorIdStringOpaque acting principal reference.
sessionIdStringConversation/session id (may be null).
authoritiesSet<String>The actor's granted authorities (for RBAC); never null.
preferencesMap<String, Object>Resolved preferences / environment signals; never null.

RbacPolicy

RBAC requirement a UseCaseDescriptor declares: the authorities an actor must hold to run the use-case.

FieldTypeMeaning
requiredAuthoritiesSet<String>Authorities the actor must hold (empty = permit all); never null.
EnumValuesMeaning
CapabilityCHAT · IMAGE · AUDIO · VIDEO · AGENT · ADMIN · STUDIOThe high-level capability an Intention targets.
ExecutionModeSYNC · ASYNCExecution mode of an Intention: SYNC for low-latency interactive responses (including token streaming), ASYNC for heavy/deferred work dispatched to a worker.
IntentionTypeCOMMAND · QUERYCQRS pivot of an Intention: COMMAND mutates state, QUERY only reads.
PlanningModeDETERMINISTIC · AGENTICHow a use-case resolves its execution plan: DETERMINISTIC runs a fixed workflow (DAG), AGENTIC delegates planning to the core's agent loop (plan→act→observe).

Use-cases

The registry matches an intention to the first use-case whose descriptor serves its capability.

Use-caseIdCapabilityPlanningPersonasRequired toolsRBACSummary
ChatAssistantUseCasechat.assistantCHATDETERMINISTICpersonas/default-assistant—anyone authenticatedReference use-case — a synchronous chat assistant.
ImageGenerationUseCaseimage.generateIMAGEDETERMINISTIC——anyone authenticatedSecond reference use-case — image generation.
StudioRunUseCasestudio.runSTUDIODETERMINISTIC——anyone authenticatedRuns an installed Studio as a first-class Intention (ADR-034 §9.2).

Dispatch refuses an intention with:

CodeWhen
ERR-410no use-case registered for capability
ERR-403actor not authorized for use-case

Entry points

The adapters that hand work to the business layer:

AdapterRepositoryRole
IntentControllerorazaka-conversation-serviceREST controller for the intent resource.
WorkflowAdapterorazaka-conversation-serviceRouter adapter translating the Business layer's WorkflowContext into the Core infrastructure's Context and ChatRequest.

Call it over HTTP through the edge with a session token (see the API reference):

curl -X POST http://localhost:8088/api/v1/intent \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"capability":"CHAT","prompt":"Summarise our refund policy in three bullet points."}'

Adding a use-case

A new product capability is one class: implement UseCase<I, R>, declare its UseCaseDescriptor (id, capability, personas, planning mode, required tools, RBAC) and register it as a bean — the registry discovers it. Personas are Markdown prompts read from classpath:prompts/<name>.md by MarkdownPromptResolver. The reference implementation, verbatim:

package com.krizaka.orazaka.business.usecases.chat;

/**
 * Reference use-case — a synchronous chat assistant. Demonstrates the App Factory contract: it
 * declares a {@link UseCaseDescriptor} (with a markdown {@code persona}), resolves the persona via
 * {@link MarkdownPromptResolver}, and orchestrates the core's {@link AiClient} inbound port. A new
 * product capability is added the same way — a new {@code UseCase} class — without touching the
 * core or the router.
 */
public final class ChatAssistantUseCase implements UseCase<ChatPayload, ChatResponse> {

  private static final UseCaseDescriptor DESCRIPTOR =
      new UseCaseDescriptor(
          "chat.assistant",
          Capability.CHAT,
          Set.of("personas/default-assistant"),
          PlanningMode.DETERMINISTIC,
          Set.of(),
          RbacPolicy.PERMIT_ALL);

  private final AiClient aiClient;
  private final MarkdownPromptResolver prompts;

  public ChatAssistantUseCase(AiClient aiClient, MarkdownPromptResolver prompts) {
    this.aiClient = Objects.requireNonNull(aiClient, "aiClient must not be null");
    this.prompts = Objects.requireNonNull(prompts, "prompts must not be null");
  }

  @Override
  public UseCaseDescriptor descriptor() {
    return DESCRIPTOR;
  }

  @Override
  public ChatResponse execute(UseCaseContext ctx, ChatPayload input) {
    Context coreContext =
        new Context(
            ctx.actorId() != null ? ctx.actorId() : "anonymous",
            ctx.sessionId() != null ? ctx.sessionId() : ctx.intentionId(),
            ctx.preferences(),
            Set.of());
    return aiClient.chat(new ChatRequest(input.prompt(), personaMessages(), Map.of(), coreContext));
  }

  /** Resolves each declared persona's markdown into a leading system message. */
  private List<ChatMessage> personaMessages() {
    return descriptor().personas().stream()
        .map(prompts::resolve)
        .flatMap(java.util.Optional::stream)
        .filter(content -> !content.isBlank())
        .map(content -> new ChatMessage("system", content))
        .toList();
  }
}

Product overview →

On this page