core PK: id 10 required 2 unique

Description

Represents a single node in a multi-level organizational hierarchy tree. Each node belongs to an organization and may have a parent node, enabling modeling of nested structures such as national associations → regions → local chapters. Used by NHF (12 national associations, 9 regions, 1,400 local chapters) and other tenants with nested org structures. Supports activity distribution across hierarchy levels, Bufdir reporting unit mapping, and role-scoped visibility for coordinators and org admins.

16
Attributes
6
Indexes
6
Validation Rules
14
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Primary key. Stable identifier for this hierarchy node.
PKrequiredunique
organization_id uuid FK to organizations. The tenant this node belongs to. All nodes in a hierarchy tree share the same organization_id.
required
parent_node_id uuid Self-referential FK to organization_hierarchy_nodes. NULL for root nodes (e.g. national association level). Non-null for all child nodes.
-
node_type enum Structural level of this node within the hierarchy. Determines display label and reporting semantics.
required
name string Canonical internal name of this node (e.g. 'NHF Oslo og Akershus'). Used in admin UI and reports.
required
display_name string Optional override for how this node is presented in user-facing UI. Falls back to name if null.
-
external_id string Identifier used in external member registry or accounting system (e.g. Dynamics, Xledger) to cross-reference this org unit. Nullable for nodes not mapped to external systems.
-
bufdir_unit_id string Bufdir reporting unit identifier for this node, if it is a distinct reporting unit in grant compliance reports. Nullable; nodes without a Bufdir ID are not independently reportable.
-
path string Materialized path string representing the full ancestor chain, e.g. '/root-id/mid-id/this-id/'. Maintained by hierarchy-service on create/move. Enables efficient subtree queries without recursive CTEs.
requiredunique
depth integer Zero-based depth of this node in the tree. Root node depth = 0. Derived from parent chain but stored for query efficiency.
required
sort_order integer Display order among siblings sharing the same parent_node_id. Lower values appear first. Defaults to 0.
required
status enum Lifecycle state of this node. Inactive nodes are hidden from mobile module registry and coordinator views but retained for historical reporting.
required
metadata json Arbitrary org-specific metadata for this node (e.g. contact email, postal address, meeting schedule). Not queried structurally; for display and export use only.
-
created_at datetime UTC timestamp when this node was created.
required
updated_at datetime UTC timestamp of last modification. Updated on any field change including moves.
required
created_by uuid FK to users. The org admin who created this node. Nullable to support seed/migration data.
-

Database Indexes

idx_ohn_organization_id
btree

Columns: organization_id

idx_ohn_parent_node_id
btree

Columns: parent_node_id

idx_ohn_path
btree unique

Columns: path

idx_ohn_organization_status
btree

Columns: organization_id, status

idx_ohn_organization_depth
btree

Columns: organization_id, depth

idx_ohn_bufdir_unit_id
btree

Columns: bufdir_unit_id

Validation Rules

name_not_blank error

Validation failed

node_type_valid_enum error

Validation failed

depth_matches_parent error

Validation failed

sort_order_non_negative warning

Validation failed

external_id_length error

Validation failed

bufdir_unit_id_format error

Validation failed

Business Rules

root_node_no_parent
on_create

A node with parent_node_id IS NULL is the root of its organization's tree. Each organization may have at most one root node. Attempting to create a second root for the same organization is rejected.

Enforced by: Hierarchy Service
no_cross_organization_parent
on_create

parent_node_id must reference a node belonging to the same organization_id. Cross-organization parent links are forbidden.

Enforced by: Hierarchy Service
no_circular_reference
on_update

A node may not be set as its own ancestor. On every parent assignment or node move, the service walks the ancestor chain to verify the target parent is not a descendant of the node being moved.

Enforced by: Hierarchy Service
path_consistency
on_update

The path column must be kept consistent with the actual parent chain. When a node is moved (parent_node_id changes), hierarchy-service recomputes path and depth for the moved node and all its descendants in a single transaction.

Enforced by: Hierarchy Service
delete_requires_no_children
on_delete

A node with active or inactive child nodes cannot be deleted. The delete endpoint returns a 409 listing the child count. Archiving is offered as the safe alternative.

Enforced by: Hierarchy Service
depth_limit
on_create

Hierarchy depth is capped at 5 levels (depth 0–4) to prevent unbounded recursion in path-based queries and to keep the admin UI manageable. Attempts to create a node deeper than depth 4 are rejected.

Enforced by: Hierarchy Service
inactive_node_hidden_from_registry
always

Nodes with status != 'active' are excluded from the module registry bootstrap response. Coordinator and peer mentor mobile views never surface inactive org units.

bufdir_unit_scoped_reporting
always

Bufdir report generation aggregates activity data scoped to nodes where bufdir_unit_id is non-null. Nodes without a bufdir_unit_id roll up into their nearest ancestor that has one.

Enforced by: Bufdir Report Service

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage