Basic Project Structure

AaC is unopinionated about project structures, so they can be very flexible. However, we do suggest storing models in an organized project directory structure, such as maintaining all AaC files under a top, or near-top level directory src/. An example project might look like:

src
├── system
│   └── system.yaml
├── external
|   ├── external_actors.yaml
│   └── external_interface_messages.yaml
├── service_one
|   ├── service_one.yaml
│   └── service_one_misc.yaml
└── service_two
    └── service_two.yaml

Because AaC does not require your models to be located within the same directory, you can house your model files however is necessary and logical for your project needs. An example of this might look like:

system └── system.yaml external ├── external_actors.yaml └── external_interface_messages.yaml service_one ├── service_one.yaml └── service_one_misc.yaml service_two └── service_two.yaml

Anatomy of an AaC file

Since AaC leverages YAML for its DSL, there are very few restrictions on how to store and organize AaC files and AaC definitions. The main consideration is that every AaC definition must be defined in its own YAML document or YAML file.

YAML provides a keyword/symbol --- to denote a logical document separator for multiple YAML documents within a YAML file. This means that if we store multiple AaC definitions in the same file, they must be separated using the --- symbol. If they aren’t separated correctly then the AaC file will not be recognized as a valid AaC definition.

These two examples are equivalent and both valid:

schemas.yaml

schema:
    name: Schema1
    fields:
        - name: integer
          type: int
---
schema:
    name: Schema2
    fields:
        - name: string
          type: string

schema1.yaml

schema:
    name: Schema1
    fields:
        - name: integer
          type: int

schema2.yaml

schema:
    name: Schema2
    fields:
        - name: string
          type: string

Referencing AaC Structures Across Files

Another consideration when deciding how to organize AaC definitions is which definitions reference each other. If definition A references definition B, then both definitions will need to be either declared in the same AaC file or definition A will need to define an import including the absolute or relative path to the file containing definition B.

For this example, schemaA references schemaB, and both definitions are in separate files, as a result schemaA is importing the file containing schemaB.

Additionally, while it’s possible to have multiple import definitions in a file, good style would suggest using a single import list per file.

An example of a relative path import is: schemaA.yaml

import:
  files:
    - ./schemaB.yaml
---
schema:
  name: schemaA
  fields:
    - name: sub-datastructure
      type: schemaB
    - name: integer
      type: int

schemaB.yaml

schema:
  name: schemaB
  fields:
    - name: string
      type: string

An example of a an absolute path import is: schemaA.yaml

import:
  files:
    - C:/src/external/external_actors/schemaB.yaml
---
schema:
  name: schemaA
  fields:
    - name: sub-datastructure
      type: schemaB
    - name: integer
      type: int

schemaB.yaml

schema:
  name: schemaB
  fields:
    - name: string
      type: string

Embedding AaC files alongside your implementation

Because AaC can be used to generate code, users may find themselves with a project repository consisting of:

  1. AaC files that describe the architecture and drive code-stub generation

  2. Version-controlled implementation files that compliment the stub/interface code files generated by AaC

There is no requirement to maintain these files in any sort of relationship or directory structure as long as the AaC files can reference each other. The best practice would be to have your VCS ignore the generated/stubbed files and leverage automated workflows and user/developer environments to generate the stub/interface files as fresh artifacts every time they’re used – this helps prevent configuration drift between what is defined in your AaC files and the downstream artifacts.

Creating a User Library for your project

Because of the extensibility and reusability goals of AaC, a plugin can also be designed as a library for a project. By making a plugin to function more as a library, it exposes itself as a tool to be utilized in a larger context throughout the AaC space.

For more information on making a User Library, view Creating a User Library.