Skip to content

aggregate

Container-based consistency unit inside a Bounded Context. See Concepts: Aggregate.

Schema

properties:
    scope:
        type: object
        properties:
            domain:
                type: string
                pattern: "^[a-z][a-z0-9-]*$"
            boundedContext:
                type: string
                pattern: "^[a-z][a-z0-9-]*$"
        required: [domain, boundedContext]
        additionalProperties: false
    identifiedBy:
        oneOf:
            - type: object
              properties:
                source:
                    const: state
                field:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
              required: [source, field]
              additionalProperties: false
            - type: object
              properties:
                source:
                    const: static
                value:
                    type: string
                    minLength: 1
              required: [source, value]
              additionalProperties: false
            - type: object
              properties:
                source:
                    const: generated
                generator:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
              required: [source, generator]
              additionalProperties: false
    state:
        type: object
    invariants:
        type: array
        items:
            type: object
            properties:
                name:
                    type: string
                    pattern: "^[a-z][a-z0-9-]*$"
                rule:
                    type: string
            required: [name, rule]
            additionalProperties: false
required: [scope, identifiedBy, state]

Anatomy

An Aggregate scopes to a Bounded Context. The scope object carries domain and boundedContext, both required, and no other fields are allowed. That positioning is what gives the Aggregate its consistency boundary: every Command, Event, and invariant attached to it lives inside that single Bounded Context.

The identifiedBy field is a discriminated oneOf on source, and the chosen value selects the sibling field that completes the strategy. When source is state, the entry pairs with a required field, naming a property of state that holds the identifier. When source is static, the entry pairs with value, a non-empty string that fixes the identifier for all instances – useful for singleton Aggregates. When source is generated, the entry pairs with generator, the kebab-case name of a generation strategy such as uuid, ulid, snowflake, cuid, or nanoid. The schema does not validate that the named generator exists; that's a downstream concern.

The state field is a JSON Schema object that describes the Aggregate's per-instance state. Its precise shape is up to the model; the schema only requires that it is an object, leaving the structure entirely to the team.

The optional invariants array holds named rules that must always hold over state. Each entry is { name, rule }, where name follows the kebab-case name pattern and rule is the prose statement of the constraint. Invariants are descriptive, not executable: they tell the reader what the Aggregate guarantees, not how it enforces it.

description and metadata carry the usual free-form prose and non-semantic attachments. apiVersion is schema.esdm.io/core/v1, kind is aggregate, and name is the Aggregate's kebab-case identifier.