Schema Definitions in AaC

AaC employs a set of specialized definitions, all of which are defined with the schema, to establish data structures within the AaC Domain-Specific Language (DSL). These schema-defined data structures encompass various forms, such as internal and external message formats, inputs and outputs for modeled behaviors, custom structures for plugin developers or user libraries, and other essential data constructs needed for your team’s modeling activities.

In the AaC DSL, every definition has its structure defined by a schema definition, which permits users to tailor the language to their team’s requirements and even craft new DSLs or extensions within AaC.

Users can define these extensions locally in their user-defined AaC files, or they can leverage plugins and user libraries for common structures and functionalities shared across teams and repositories.

Required Fields

Fields defined in a schema definition can be marked as required, meaning that any implementation of that schema MUST contain that field. This is accomplished by setting the is_required boolean field to true.

In the definition below, Enum, the field values is marked as required. An Enum MUST include at least one value in the values field to be a valid definition.

schema:
  name: Enum
  package: aac.lang
  extends:
    - package: aac.lang
      name: AacType
  root: enum
  description: |
    A definition that represents an enumerated type of value.

    An example of when to use an 'enum' is when you want to provide several
    different options for a single value.
  fields:
    - name: values
      type: string[]
      description: |
        A list of strings that encompass the enumeration's possible values.
      is_required: true

Utilizing Schema Definitions

Schema definitions serve multiple roles in the AaC DSL. Some examples are provided below for common use cases: crafting message interfaces, custom data structures, and defining new definition instance types.

Crafting Message Interfaces

Interface message structures provide structures for communication within your system or between system components. Using schemas one can define payloads exchanged with external systems, software applications, external actors, input for plugins, and intricate data inputs for constraint purposes. Leveraging a schema definition as part of a model definition’s behavior.input or behavior.output, the schema acts as an interface message, documenting the data structure flowing into or out of a component within your modeled system. A useful way of documenting software application behavior inputs or outputs.

In our Alarm Clock model example, the behavior entry defines the input and behavior of our Alarm Clock model. The behavior contains an input named targetTime, which is a schema-defined data structure named Timestamp representing information needed to set the alarm.

model:
  name: AlarmClock
  description: A simple alarm clock
  components:
    - name: clock
      model: Clock
    - name: timer
      model: ClockTimer
    - name: alarm
      model: ClockAlarm
  behavior:
    - name: setAlarm
      description: Set the alarm timer
      input:
        - name: targetTime
          type: Timestamp
      acceptance:
        - name: Modify Timer State
          scenarios:
            - name: Set timer
              given:
                - The alarm timer is not set
              when:
                - The user sets the alarm timer
              then:
                - The alarm is triggered when the clock reaches the specified time
            - name: Update timer
              given:
                - The alarm timer is set
              when:
                - The user sets the alarm timer
              then:
                - The alarm is triggered when the clock reaches the specified time

The Timestamp schema definition below outlines the fields required to define an exact alarm time.

---
schema:
  name: Timestamp
  package: alarm_clock
  fields:
    - name: hour
      type: int
    - name: minute
      type: int
    - name: second
      type: int
    - name: year
      type: int
    - name: month
      type: int
    - name: day
      type: int
    - name: timezone
      type: TimezoneOffset

While this example is simplified and not representative of a software system, the same approach applies to defining user requests, events from message queues, Swift XML messages in financial applications, or any other input/output data structure within your modeled system.

Crafting Custom Data Structures

Given AaC’s self-defining nature, users can employ the schema definition to craft their own data structures by using the schema root key. Timestamp serves as an example of a defined data structure with a schema root key that can be referenced by name, as demonstrated in the Alarm Clock model’s behavior, or used as a component in more intricate data structures.

schema:
  name: Timestamp
  package: alarm_clock
  fields:
    - name: hour
      type: int
    - name: minute
      type: int
    - name: second
      type: int
    - name: year
      type: int
    - name: month
      type: int
    - name: day
      type: int
    - name: timezone
      type: TimezoneOffset
model:
  name: AlarmClock
  description: A simple alarm clock
  components:
    - name: clock
      model: Clock
    - name: timer
      model: ClockTimer
    - name: alarm
      model: ClockAlarm
  behavior:
    - name: setAlarm
      description: Set the alarm timer
      input:
        - name: targetTime
          type: Timestamp
      acceptance:
        - name: Modify Timer State
          scenarios:
            - name: Set timer
              given:
                - The alarm timer is not set
              when:
                - The user sets the alarm timer
              then:
                - The alarm is triggered when the clock reaches the specified time
            - name: Update timer
              given:
                - The alarm timer is set
              when:
                - The user sets the alarm timer
              then:
                - The alarm is triggered when the clock reaches the specified time

Constructing Complex Data Structures via Composition

Each schema definition in AaC outlines a set of fields for the definition. These fields can be primitive types as per the AaC DSL, custom enum types, or references to other schema definitions. When a schema definition references other schema definitions within its field types, it assembles more intricate data structures through composition. In our Alarm Clock model example, the definition TimerAlert references Timestamp as a field type.

schema:
  name: TimerAlert
  package: alarm_clock
  root: timer_alert
  description: Data structure for timer alerts.
  fields:
    - name: name
      type: string
    - name: triggeredTime
      type: Timestamp
    - name: alarmNoise
      type: AlarmNoise

Introducing New Definition Types

AaC permits users to define new instances of custom data structures. In our Alarm Clock example, you might want to model individual instances of TimerAlert with varying dates and alarm sounds. To do this, you specify the root field on the schema to be the YAML root tag to use when declaring instances of your new type. The following example demonstrates how to set the root field to allow timer_alert to be used as a YAML root for declaring TimerAlert instances.

schema:
  name: TimerAlert
  package: alarm_clock
  root: timer_alert
  description: Data structure for timer alerts.
  fields:
    - name: name
      type: string
    - name: triggeredTime
      type: Timestamp
    - name: alarmNoise
      type: AlarmNoise