glam/docs/ENUM_CLASS_SINGLE_SOURCE.md
2025-12-07 00:26:01 +01:00

4.9 KiB

Enum vs Class: Single Source of Truth Principle

Document Type: Design Decision
Status: Active
Last Updated: 2025-12-06


Executive Summary

This project follows a strict Single Source of Truth principle for schema elements. When a concept transitions from an enum (simple value list) to a class hierarchy (rich structure with properties), the original enum MUST be deleted to prevent dual representation.


Background

What are Enums?

LinkML enums define constrained value sets:

DataTierEnum:
  permissible_values:
    TIER_1_AUTHORITATIVE:
      description: Authoritative source
    TIER_2_VERIFIED:
      description: Verified data

Enums are good for:

  • Simple value constraints
  • Values based on external standards (ISO codes)
  • Values that don't need properties or relationships

What are Classes?

LinkML classes define structured entities with properties:

Curator:
  is_a: StaffRole
  class_uri: schema:curator
  slots:
    - role_category
    - typical_domains
    - common_variants

Classes are good for:

  • Concepts needing properties
  • Inheritance hierarchies
  • Rich documentation per value
  • Relationships to other entities

The Rule

When an enum is promoted to a class hierarchy, the enum MUST be deleted.

Why This Matters

Scenario Single Source Dual Source
Where is the definition? One place Two places (confusion)
Where do I make changes? One file Two files (maintenance burden)
Can they drift apart? No Yes (bugs)
Which is authoritative? Clear Ambiguous

Decision Workflow

Is this concept a simple value constraint?
│
├─ YES → Use enum
│         Examples: DataTierEnum, CountryCodeEnum
│
└─ NO, it needs properties/relationships/rich docs
          ↓
     Create class hierarchy
          ↓
     Update slot ranges: enum → class
          ↓
     DELETE the enum (archive it)
          ↓
     Document the migration

Case Study: StaffRoleTypeEnum → StaffRole

Before (Enum)

# StaffRoleTypeEnum.yaml
StaffRoleTypeEnum:
  permissible_values:
    CURATOR:
      description: Museum curator
    CONSERVATOR:
      description: Conservator

Limitations:

  • Can't distinguish formal title from de facto work
  • Can't add role_category, typical_domains, common_variants
  • Can't express inheritance (e.g., DataScientist is_a DataRole)

After (Classes)

# StaffRole.yaml (abstract base)
StaffRole:
  abstract: true
  description: |
    Base class for staff role categories.
    
    **IMPORTANT**: These are FORMAL job appellations/titles.
    Actual de facto work may differ or stretch beyond these classifications.    
  slots:
    - role_id
    - role_name
    - role_category
    - typical_domains
    - typical_responsibilities

# StaffRoles.yaml (51 subclasses)
Curator:
  is_a: StaffRole
  class_uri: schema:curator
  slot_usage:
    role_category:
      equals_string: CURATORIAL

Migration Steps Taken

  1. Created modules/classes/StaffRole.yaml (base class with RoleCategoryEnum)
  2. Created modules/classes/StaffRoles.yaml (51 specialized subclasses)
  3. Updated modules/slots/staff_role.yaml: range: StaffRoleTypeEnumrange: StaffRole
  4. Updated modules/classes/PersonObservation.yaml slot definition
  5. Updated 01_custodian_name_modular.yaml imports (removed enum, added classes)
  6. Archived enum to archive/enums/StaffRoleTypeEnum.yaml.archived_20251206

Enums That Remain Permanent

Not all enums should become classes. These remain as enums:

Enum Rationale
DataTierEnum Simple 4-tier hierarchy, no properties needed
DataSourceEnum Fixed source types, external classification
InstitutionTypeEnum Matches GLAMORCUBESFIXPHDNT taxonomy, single-letter codes
CountryCodeEnum ISO 3166-1 standard, external authority
LanguageCodeEnum ISO 639 standard, external authority

Indicators of permanent enums:

  • Based on external standards with defined values
  • Values are simple strings without properties
  • No need for inheritance or relationships
  • Used purely for validation constraints

Verification

After any enum-to-class migration:

# Ensure enum is only in archive
grep -r "StaffRoleTypeEnum" schemas/20251121/linkml/modules/
# Should return NO results

# Verify classes are imported
grep "StaffRole" schemas/20251121/linkml/01_custodian_name_modular.yaml
# Should show class imports


Change Log

Date Change
2025-12-06 Initial document created
2025-12-06 StaffRoleTypeEnum migrated to StaffRole class hierarchy