> ## Documentation Index
> Fetch the complete documentation index at: https://learn.nextedy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Model-Driven Design

> Nextedy POWERSHEET follows a **model-driven design** pattern: the structure and behavior of every sheet is determined by a declarative YAML data model rather than imperative code.

export const LastReviewed = ({date}) => {
  if (!date) return null;
  const formatted = new Date(`${date}T00:00:00Z`).toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric",
    timeZone: "UTC"
  });
  return <p className="mt-10 text-sm text-gray-400 dark:text-zinc-500 not-prose">
      Last reviewed on {formatted}
    </p>;
};

## The design principle

In a model-driven system, configuration files describe **what** should happen, not **how** it should happen. The data model declares entity types, their properties, and their relationships. The Powersheet runtime reads this declaration and automatically generates the metadata, queries, pickers, and validation rules needed to render and edit the data.

This is the opposite of a code-driven approach where each new entity type or relationship would require custom development. With Powersheet, adding a new entity type is a YAML change, not a code change.

Think of it like a building blueprint. The blueprint (data model) specifies which rooms exist, how they connect, and what each room contains. The construction crew (Powersheet runtime) reads the blueprint and builds everything accordingly. If you want to add a new room, you update the blueprint rather than writing new construction instructions from scratch.

<Frame>
  <img src="https://mintcdn.com/none-17b4493f/3Zik2OH750CE3kB4/powersheet/diagrams/concepts/model-driven-design/diagram-1.svg?fit=max&auto=format&n=3Zik2OH750CE3kB4&q=85&s=be046ea0107cc8219a58f00a982a22d5" alt="diagram" style={{ width: "560px", maxWidth: "100%" }} width="560" height="160" data-path="powersheet/diagrams/concepts/model-driven-design/diagram-1.svg" />
</Frame>

## The three configuration layers

Powersheet's model-driven architecture rests on three distinct configuration layers that work together. Understanding how they connect is essential for building effective sheets.

| Layer                   | File                        | Purpose                                             |
| ----------------------- | --------------------------- | --------------------------------------------------- |
| **Data model**          | `model.yaml`                | Defines entity types, properties, and relationships |
| **Sheet configuration** | `powersheet.yaml`           | Defines columns, views, formatters, and sources     |
| **Polarion project**    | Link roles, work item types | Provides the underlying data infrastructure         |

The data model sits between Polarion and the sheet configuration. It maps Polarion's native concepts (work item types, link roles, custom fields) into a consistent vocabulary of entity types and navigation properties. The sheet configuration then references that vocabulary without needing to know the Polarion-level details.

<Frame>
  <img src="https://mintcdn.com/none-17b4493f/3Zik2OH750CE3kB4/powersheet/diagrams/concepts/model-driven-design/diagram-2.svg?fit=max&auto=format&n=3Zik2OH750CE3kB4&q=85&s=43c623a8320bd2a28cb84dde55388913" alt="diagram" style={{ width: "600px", maxWidth: "100%" }} width="600" height="260" data-path="powersheet/diagrams/concepts/model-driven-design/diagram-2.svg" />
</Frame>

The navigation property names are the thread that connects these layers. A relationship defined in the data model creates a named navigation property. The sheet sources use that same name to expand into related data. The sheet columns use it to bind and display values. If any layer uses a different name, the connection breaks.

## From YAML to metadata

When a powersheet document is opened, the data model YAML is processed through a metadata generation pipeline. Each `domainModelTypes` entry becomes an entity type with:

* A primary key property (`objectId`)
* A `polarionType` property linking to the Polarion work item type
* Built-in properties: `title`, `icon`, and automatically added document and project navigation properties
* Custom properties from the `properties` section, each mapped to Polarion work item fields

Here is a minimal data model showing how entity types are declared:

```yaml theme={null}
domainModelTypes:
  UserNeed:
    polarionType: user_need
    properties:
      description:
      severity:

  SystemRequirement:
    polarionType: sys_req
    properties:
      description:
      severity:
```

Each key under `domainModelTypes` (like `UserNeed`) becomes the entity type name used throughout the rest of the configuration. The `polarionType` maps it to the corresponding Polarion work item type. Properties listed under `properties` become available as columns in the sheet.

### Relationships and navigation properties

Relationships define how entity types connect to each other. Each relationship specifies:

* **`from` / `to`** -- the source and target entity types
* **`cardinality`** -- the multiplicity (many-to-one, one-to-many, many-to-many)
* **`storage`** -- how the link is persisted in Polarion (`linkedWorkItems` is the only supported storage mechanism)
* **`linkRole`** -- the Polarion link role that implements the connection
* **`direct` / `back`** -- the navigation property names for forward and reverse traversal

```yaml theme={null}
relationships:
  - from: SystemRequirement
    to: UserNeed
    cardinality: many-to-many
    storage: linkedWorkItems
    linkRole: decomposes
    direct:
      name: userNeeds
    back:
      name: systemRequirements
```

In this example, the relationship creates two navigation properties:

* `direct.name: userNeeds` -- navigate from a `SystemRequirement` to its linked `UserNeed` items
* `back.name: systemRequirements` -- navigate from a `UserNeed` to its linked `SystemRequirement` items

<Warning title="Use direct/back syntax">
  The `direct` and `back` object notation is the current relationship syntax. Each contains a `name` field specifying the navigation property. This syntax replaced an earlier flat format and is the only supported approach.
</Warning>

### Built-in entity types

Two built-in entity types are always available regardless of the data model configuration:

| Entity type | Purpose                                                                                                                                                      |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Document`  | Represents Polarion modules (LiveDocs). Exposes properties like `moduleName`, `moduleFolder`, `title`, `titleOrName`, `titleWithSpace`, and `allowedWITypes` |
| `Project`   | Represents Polarion projects. Automatically linked to all work item entity types                                                                             |

These built-in types enable features like document-scoped filtering in picker constraints and project-level navigation without any explicit configuration.

## The mapping layer

A key advantage of model-driven design is that the data model acts as a **mapping layer** between Polarion's native data structures and the Powersheet view. This is significant because:

* **Different projects can have different Polarion type names** for the same conceptual entity. The data model normalizes these: one project's `sys_requirement` and another's `systemReq` can both map to a `SystemRequirement` entity type in the data model.
* **Link roles vary between projects.** The data model abstracts them behind navigation property names, so the sheet configuration remains identical.
* **A single sheet configuration can be reused across projects** that share the same data model structure but differ in Polarion-level details.

<Note title="Why two separate files?">
  This mapping layer is why the data model and sheet configuration are separate files. The data model handles project-specific Polarion mappings. The sheet configuration handles presentation and interaction. You can pair one data model with multiple sheet configurations, or update the Polarion mappings without touching the column layout.
</Note>

## How cardinality shapes the sheet

The `cardinality` of a relationship determines how data flows through sources, how columns bind to properties, and what users see in the sheet. This is perhaps the most important concept for understanding how model-driven design translates into a working UI.

| Cardinality | Model definition                                             | Source expand                                                 | Column binding                         | UI behavior                      |
| ----------- | ------------------------------------------------------------ | ------------------------------------------------------------- | -------------------------------------- | -------------------------------- |
| **N:1**     | `cardinality: many-to-one`, `direct.name: chapter`           | `- name: chapter`                                             | `chapter`, `chapter.title`             | Single-value reference picker    |
| **1:N**     | Reverse of N:1, `back.name: userNeeds`                       | `- name: userNeeds`                                           | `userNeeds`                            | Child rows (new hierarchy level) |
| **M:N**     | `cardinality: many-to-many`, `back.name: systemRequirements` | `- name: systemRequirements` then `- name: systemRequirement` | `systemRequirements.systemRequirement` | Multi-item reference picker      |

### Many-to-one: scalar references

When an entity has a many-to-one relationship (each `UserNeed` belongs to exactly one `Chapter`), the navigation property is scalar. The source expands it with a single name, and columns can either display a picker for the reference or read through to display properties of the referenced entity:

```yaml theme={null}
columns:
  chapter:
    title: Chapter
    display: title
    list:
      search:
        - title
  chapter.title:
    title: Chapter Title
    isReadOnly: true
```

### One-to-many: hierarchical expansion

The reverse side of a many-to-one relationship is one-to-many. This is where Powersheet's hierarchical nature becomes visible. When a `Chapter` expands into its `userNeeds`, each child `UserNeed` appears as a nested row under the parent. The expand creates a new level in the sheet hierarchy.

### Many-to-many: association entities

Many-to-many relationships use an **association entity** between the two types. The source expand goes through two levels -- first the association collection, then the target entity. The column binding uses dot-notation to reach through:

```yaml theme={null}
sources:
  - id: user_needs
    query:
      from: UserNeed
    expand:
      - name: systemRequirements
        expand:
          - name: systemRequirement

columns:
  systemRequirements.systemRequirement:
    title: System Requirement
    list:
      search:
        - objectId
        - title
      createNew: true
```

The two-level expand (`systemRequirements` then `systemRequirement`) and the dot-notation column key (`systemRequirements.systemRequirement`) are the telltale signs of a many-to-many relationship. When a sheet has two different work item types linked to the same parent entity, the second linked column must declare `multiItem: true` in the sheet configuration.

<Tip title="Reading a column key">
  A dot-separated column key like `systemRequirements.systemRequirement.title` reads as: "Starting from the current entity, follow the `systemRequirements` navigation property (association collection), then follow `systemRequirement` (target entity), then display the `title` property." Each segment maps to a relationship or property defined in the data model.
</Tip>

## Constraints and picker filtering

The data model also drives **picker constraints** -- rules that filter which items appear when a user selects a related entity. Constraints can restrict picker results by:

* **Document location** (`moduleFolder`, `moduleName`) -- limit selections to items in specific spaces
* **Document type** (`type`) -- only show items from documents of a certain type
* **Component** (`component`) -- scope selections to the same component as the source entity using `$context.source.document.component`

```yaml theme={null}
domainModelTypes:
  SystemRequirement:
    polarionType: sys_req
    constraints:
      pick:
        document:
          moduleFolder: Requirements
          type: requirements_specification
```

This constraint means that when a user picks a `SystemRequirement` in the sheet, the picker only shows items from documents in the `Requirements` space that have the `requirements_specification` document type. The runtime applies these filters automatically based on the model declaration.

<Info title="Verify in application">
  Dynamic context references like `$context.source.document.component` resolve at runtime based on the current entity's document context. The exact behavior depends on the project's component configuration in Polarion.
</Info>

## Why model-driven design matters

The model-driven approach delivers several practical benefits that become increasingly valuable as configurations grow in complexity:

**Consistency across the organization.** Because the data model is a single YAML file shared across sheet configurations, all sheets that reference the same model use identical entity definitions, relationship rules, and navigation properties. Changes propagate automatically.

**Separation of concerns.** Polarion administrators manage work item types and link roles. Data model authors map those to a clean entity vocabulary. Sheet designers compose columns and views without worrying about Polarion internals. Each role works at the appropriate level of abstraction.

**Reduced configuration errors.** New users frequently encounter issues when jumping straight to complex multi-entity configurations. The model-driven pattern encourages an incremental approach: define one entity type, verify it works, then add relationships one at a time. Each addition is a self-contained YAML block that can be validated independently.

**Reusability across projects.** A well-designed data model can serve multiple Polarion projects by updating only the `polarionType` and `linkRole` mappings. The sheet configurations, sources, and column bindings remain unchanged because they reference entity type names and navigation properties, not Polarion-specific identifiers.

For a detailed comparison of the data model and sheet configuration files, see [Data Model vs Sheet Configuration](/powersheet/concepts/data-model-vs-sheet-config). To understand how entity types and relationships are structured in practice, see [Entity Types and Relationships](/powersheet/concepts/entity-types-and-relationships). For hands-on configuration guidance, see the [Data Model Guides](/powersheet/guides/data-model/index).

<LastReviewed date="2026-07-02" />
