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