---
id: DOM-MBR-STR-001
title: Association Hierarchy
realises: CON-MBR-STR-001
product_area: membership
module: structure
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-MBR-STR-structure
readable_by:
  - SVC-MBR-STR-configuration
  - SVC-PFM-DJE-membership-data-jobs
  - SVC-PFM-TNL-membership-tenant-lifecycle
lifecycle: STATE-MBR-STR-002
custom_fields: none
identity: >-
  Unit kinds have permanent readable identifiers within each workspace. Parent periods use generated record
  identifiers and describe a unit's parent over inclusive effective dates. Current ancestry uses one key for
  each ancestor–descendant pair.
attributes:
  - name: unit kind name and plural name
    kind: text
    required: true
    computed: false
    notes: A name contains 1–200 characters; a plural label and description can be supplied separately.
  - name: may be a root
    kind: enum
    required: true
    computed: false
    notes: This switch permits parentless units and is false unless supplied.
  - name: permitted parent kinds
    kind: collection
    required: false
    computed: false
    notes: >-
      A kind can name up to 100 permitted parent kinds, including itself; a kind that forbids roots must name
      at least one.
  - name: accepts member affiliations
    kind: enum
    required: true
    computed: false
    notes: Disabling affiliations prevents new member affiliations while preserving those already recorded.
  - name: committee and geography switches
    kind: enum
    required: true
    computed: false
    notes: Committee and geography choices are retained but have no implemented consumer in the examined sources.
  - name: display order and availability
    kind: number
    required: true
    computed: false
    notes: >-
      Kinds sort by display order and then identifier; retirement prevents new placement and movement of units
      of that kind.
  - name: current parent of each unit
    kind: reference
    required: false
    computed: false
    notes: >-
      The unit stores its current parent; ordinary placement operations permit no parent only for a
      root-enabled kind.
  - name: depth of each unit
    kind: computed
    required: true
    computed: true
    notes: The rebuilt distance from the root must remain below 32.
  - name: present ancestry
    kind: computed
    required: true
    computed: true
    notes: >-
      The derived ancestry includes each unit paired with itself at distance zero and every ancestor paired
      with its descendants.
  - name: dated parent periods
    kind: collection
    required: true
    computed: false
    notes: >-
      Each retained placement interval records its first day, optional last day, explanation and actor; the
      latest interval stays open.
  - name: parent period versions
    kind: collection
    required: true
    computed: true
    notes: >-
      Versions retain both the recorded and superseded times so historical parentage can be reconstructed
      using earlier knowledge.
relationships:
  - to: DOM-MBR-STR-002
    cardinality: A kind may classify many units; Each unit has zero or one current parent and many parent periods
    ownership: Structure owns the kind catalogue and placement history; each unit retains its own descriptive record.
    cross_service: false
  - to: DOM-MBR-STR-003
    cardinality: many programs to many unit kinds
    ownership: >-
      Configuration owns the program's references and requirements; structure owns the referenced kinds and
      protects them from deletion while referenced.
    cross_service: true
  - to: DOM-MBR-STR-004
    cardinality: many package unit requirements to one kind each
    ownership: 'Configuration owns package requirements, whose references prevent removal of the named kind.'
    cross_service: true
invariants:
  - id: INV-DOM-MBR-STR-001-01
    statement: >-
      Placement requires an allowed parent kind, or a root-enabled unit kind when no parent is supplied;
      service validation is backed by the declared database trigger.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-membership:tests/e2e/structure-api.e2e.spec.ts#the structure API, end to end in process ›
      refuses a parent of a type not allowed, 409 parent_type_not_allowed
  - id: INV-DOM-MBR-STR-001-02
    statement: Ordinary placement validation rejects ancestry cycles and branches that reach depth 32.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-membership:modules/structure/tests/integration/services/persistence/organizational-units.integration.spec.ts#organizational
      units in the database › the refusals › refuses a move under the unit’s own descendant as a cycle
  - id: INV-DOM-MBR-STR-001-03
    statement: >-
      Creation, moves and merges commit ancestry and depths consistent with parent references under the tree
      lock; loads defer rebuilding and deletion removes only the deleted unit's pairs.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-membership:modules/structure/tests/unit/services/tree-edits.spec.ts#the tree edits › move ›
      locks, validates against the current closure, changes the parent, records history, rebuilds, updates
      depths and checks the cap, in that order
  - id: INV-DOM-MBR-STR-001-04
    statement: >-
      Parent periods recorded through the placement service join without gaps or overlap from first placement,
      with one latest open period.
    enforced_at:
      - service
    provenance: observed
    source: 'services-membership:modules/structure/services/persistence/organizational-units.ts:recordParentChange'
  - id: INV-DOM-MBR-STR-001-05
    statement: >-
      Kind replacement preserves root and parent permissions used by current units; deletion fails while
      units, other kinds, programs or package requirements reference the kind.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-membership:tests/e2e/structure-api.e2e.spec.ts#the structure API, end to end in process ›
      refuses to delete a unit type in use, 409 unit_type_in_use
  - id: INV-DOM-MBR-STR-001-06
    statement: >-
      Published ancestry functions and workspace-scoped structure reads cannot return another workspace's
      records.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-membership:modules/structure/tests/integration/services/persistence/organizational-units.integration.spec.ts#organizational
      units in the database › the published functions › answers nothing of a tenant other than the reader’s
      own
  - id: INV-DOM-MBR-STR-001-07
    statement: >-
      Ordinary application access cannot rewrite parent-period versions; workspace removal uses the owner's
      designated function.
    enforced_at:
      - database
    provenance: observed
    source: 'services-membership:modules/structure/services/lifecycle/structure-participant.ts:structureParticipant'
physical:
  - organizational_unit_types
  - organizational_unit_type_parents
  - organizational_unit_closure
  - organizational_unit_parent_history
  - organizational_unit_parent_history_versions
evidence:
  - >-
    services-membership at 5994c270: modules/structure/db/organizational-unit-types.table.ts,
    organizational-unit-type-parents.table.ts, organizational-unit-closure.table.ts,
    organizational-unit-parent-history.table.ts, organizational-unit-parent-history-versions.table.ts;
    services/tree/tree-edits.ts; services/persistence/organizational-units.ts, organizational-unit-types.ts;
    services/lifecycle/structure-participant.ts; docs/migrations/structure/README.md (move sequence, published
    functions, what could not be done).
  - >-
    rhythm-v2-docs: specs/membership/structure/planning (CAP-MBR-STR-001, 002, 005; FND-MBR-STR-005, 006,
    024).
---

# Association Hierarchy

## What It Models

The hierarchy describes how the association arranges national bodies, regions, chapters and other units. Staff define the permitted kinds of placement and can inspect both today's arrangement and earlier parentage.

## Identity and Attributes

Unit kinds supply placement permissions, affiliation choices and availability. Units hold current parent references; derived ancestry and depth support navigation and validation. Dated parent periods retain the explanation and actor, while versions preserve what an earlier reader could have known.

## Relationships

Units supply the nodes being arranged. Programs and packages reference kinds when defining which units a membership requires. Those references belong to configuration and do not transfer ownership of the kind catalogue.

## Invariants

The structured invariants identify placement checks and their enforcement points. Their scope distinguishes ordinary placement transactions from imports, which deliberately defer rebuilding until the producer finishes.

## How It Is Stored and Shared

The structure service stores kinds, current ancestry and dated parentage. Configuration reads kinds for validation, and other applications can read ancestry through published database functions. Unit and kind query registrations describe the datasets Platform can expose.

## Open Design Questions

Product must settle the current-tree treatment of a merged source and whether scheduled restructuring is needed. Historical answers use current unit names and codes, and a same-day placement correction retains its predecessor only in versions.
