Skip to content

scenario

A scenario is a nested entry inside a feature.scenarios[] list. It is not a top-level document on its own; it lives inside its feature and inherits the feature's variant. See feature for the surrounding document and the variant choice it fixes.

Schema

The shape of a scenario depends on its surrounding feature's variant. The four variants resolve to the following schemas.

Aggregate variant

type: object
properties:
    name:
        type: string
        pattern: "^[a-z][a-z0-9-]*$"
    description:
        type: string
    given:
        type: array
        items:
            type: object
            properties:
                event:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
                data:
                    type: object
            required: [event, data]
            additionalProperties: false
    when:
        type: object
        properties:
            command:
                type: string
                pattern: "^[a-z][a-z0-9-]*$"
            data:
                type: object
            actor:
                type: string
                pattern: "^[a-z][a-z0-9-]*$"
        required: [command, data]
        additionalProperties: false
    then:
        oneOf:
            - type: object
              properties:
                events:
                    type: array
                    items:
                        type: object
                        properties:
                            event:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                            data:
                                type: object
                        required: [event, data]
                        additionalProperties: false
              required: [events]
              additionalProperties: false
            - type: object
              properties:
                rejection:
                    oneOf:
                        - type: object
                          properties:
                            invariant:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                          required: [invariant]
                          additionalProperties: false
                        - type: object
                          properties:
                            reason:
                                type: string
                                minLength: 1
                          required: [reason]
                          additionalProperties: false
              required: [rejection]
              additionalProperties: false
required: [name, when, then]
additionalProperties: false

DCB variant

type: object
properties:
    name:
        type: string
        pattern: "^[a-z][a-z0-9-]*$"
    description:
        type: string
    given:
        type: array
        items:
            oneOf:
                - type: object
                  properties:
                    boundedContext:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    aggregate:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    event:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    data:
                        type: object
                  required: [boundedContext, aggregate, event, data]
                  additionalProperties: false
                - type: object
                  properties:
                    boundedContext:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    event:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    data:
                        type: object
                  required: [boundedContext, event, data]
                  additionalProperties: false
    when:
        type: object
        properties:
            command:
                type: string
                pattern: "^[a-z][a-z0-9-]*$"
            data:
                type: object
            actor:
                type: string
                pattern: "^[a-z][a-z0-9-]*$"
        required: [command, data]
        additionalProperties: false
    then:
        oneOf:
            - type: object
              properties:
                events:
                    type: array
                    items:
                        type: object
                        properties:
                            event:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                            data:
                                type: object
                        required: [event, data]
                        additionalProperties: false
              required: [events]
              additionalProperties: false
            - type: object
              properties:
                rejection:
                    oneOf:
                        - type: object
                          properties:
                            invariant:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                          required: [invariant]
                          additionalProperties: false
                        - type: object
                          properties:
                            reason:
                                type: string
                                minLength: 1
                          required: [reason]
                          additionalProperties: false
              required: [rejection]
              additionalProperties: false
required: [name, when, then]
additionalProperties: false

Process Manager variant

type: object
properties:
    name:
        type: string
        pattern: "^[a-z][a-z0-9-]*$"
    description:
        type: string
    given:
        type: array
        items:
            oneOf:
                - type: object
                  properties:
                    boundedContext:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    aggregate:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    event:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    data:
                        type: object
                  required: [boundedContext, aggregate, event, data]
                  additionalProperties: false
                - type: object
                  properties:
                    boundedContext:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    event:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    data:
                        type: object
                  required: [boundedContext, event, data]
                  additionalProperties: false
    when:
        oneOf:
            - type: object
              properties:
                boundedContext:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
                aggregate:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
                event:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
                data:
                    type: object
              required: [boundedContext, aggregate, event, data]
              additionalProperties: false
            - type: object
              properties:
                boundedContext:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
                event:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
                data:
                    type: object
              required: [boundedContext, event, data]
              additionalProperties: false
            - type: object
              properties:
                timer:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
              required: [timer]
              additionalProperties: false
    then:
        type: object
        properties:
            emits:
                type: array
                items:
                    oneOf:
                        - type: object
                          properties:
                            boundedContext:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                            aggregate:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                            command:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                            data:
                                type: object
                          required: [boundedContext, aggregate, command]
                          additionalProperties: false
                        - type: object
                          properties:
                            boundedContext:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                            dynamicConsistencyBoundary:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                            command:
                                type: string
                                pattern: "^[a-z][a-z0-9-]*$"
                            data:
                                type: object
                          required: [boundedContext, dynamicConsistencyBoundary, command]
                          additionalProperties: false
            setTimers:
                type: array
                items:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
            cancelTimers:
                type: array
                items:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
            state:
                type: object
            ended:
                const: true
        minProperties: 1
        additionalProperties: false
required: [name, when, then]
additionalProperties: false

Read Model variant

type: object
properties:
    name:
        type: string
        pattern: "^[a-z][a-z0-9-]*$"
    description:
        type: string
    given:
        type: array
        items:
            oneOf:
                - type: object
                  properties:
                    boundedContext:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    aggregate:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    event:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    data:
                        type: object
                  required: [boundedContext, aggregate, event, data]
                  additionalProperties: false
                - type: object
                  properties:
                    boundedContext:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    event:
                        type: string
                        pattern: "^[a-z][a-z0-9-]*$"
                    data:
                        type: object
                  required: [boundedContext, event, data]
                  additionalProperties: false
    when:
        type: object
        properties:
            query:
                type: string
                pattern: "^[a-z][a-z0-9-]*$"
            parameters:
                type: object
        required: [query, parameters]
        additionalProperties: false
    then:
        oneOf:
            - type: object
              properties:
                result: true
              required: [result]
              additionalProperties: false
            - type: object
              properties:
                readModel: true
              required: [readModel]
              additionalProperties: false
required: [name, when, then]
additionalProperties: false

Anatomy

Every scenario carries four fields. The required name is a kebab-case identifier that uniquely names the scenario within its feature. The optional description carries free-form prose. The required given, when, and then describe the scenario itself, and their shape depends on which variant the surrounding feature has chosen.

given is always a list and is always required, but the list may be empty. An empty given means "no preceding history" – the consistency unit is in its initial state when the scenario starts. The shape of each given entry depends on the variant: bare for Aggregate features, scoped for the rest.

The Aggregate variant uses bare-name Events in given. Each entry carries event (the bare Event name) and data (the concrete payload value, an empty object for an Event without a payload). The when of an Aggregate scenario carries command (the bare Command name), data (the concrete payload value), and an optional actor (the Actor name, useful for permission-style scenarios). The then of an Aggregate scenario is one of two shapes: events, listing { event, data } entries with bare Event names – the empty list is legal and expresses idempotency – or rejection, an expected refusal expressed as either { invariant: <name> } or { reason: <prose> }.

The DCB variant uses scoped Event references in given, mirroring the structure of consults on the DCB itself. Each entry carries boundedContext, event, data, and (when the Event is Aggregate-bound) aggregate. The when and then shapes match the Aggregate variant, because the DCB still produces Events from a single triggering Command.

The Process Manager variant uses scoped Event references in given, with the same shape as the DCB variant. The when of a Process Manager scenario is a oneOf over three shapes: an Aggregate-owned Event delivered to the instance (carrying boundedContext, aggregate, event, data), a free-standing Event delivered to the instance (carrying boundedContext, event, data), or a tick of a named timer (carrying timer). The then of a Process Manager scenario is an object that may carry any of emits (Command references with their data, Aggregate-bound or DCB-bound), setTimers (timer names the reaction arms), cancelTimers (timer names the reaction cancels), state (the concrete state value of the instance after the reaction), or ended (true when the reaction is expected to retire the instance; false is not meaningful and should be omitted).

The Read Model variant uses scoped Event references in given so the projection state can be set up. The when of a Read Model scenario carries query (the bare Query name) and parameters (the concrete parameter value). The then is one of two shapes: result, which is the expected query result and is intentionally free-form because result shapes are paradigm-specific, or readModel, which is the expected materialized Read Model content.

description is the only optional top-level field on a scenario. The common document-level fields – apiVersion, kind, name, metadata – are not present on a scenario; they live on the surrounding feature.