# Rule: LinkML Type/Types File Naming Convention **Version**: 1.0.0 **Created**: 2025-01-04 **Status**: Active **Applies to**: `schemas/20251121/linkml/modules/classes/` --- ## Rule Statement When creating class hierarchies that replace enums in LinkML schemas, follow the **Type/Types** naming pattern to clearly distinguish abstract base classes from their concrete subclasses. --- ## Pattern Definition | File Name Pattern | Purpose | Contains | |-------------------|---------|----------| | `[Entity]Type.yaml` (singular) | Abstract base class | Single abstract class defining the type taxonomy | | `[Entity]Types.yaml` (plural) | Concrete subclasses | All concrete subclasses inheriting from the base | --- ## Class Naming Convention 🚨 **CRITICAL**: Follow these naming rules for classes within the files: 1. **Abstract Base Class** (`[Entity]Type.yaml`): * **MUST** end with `Type` suffix. * *Example*: `DigitalPlatformType`, `WarehouseType`. 2. **Concrete Subclasses** (`[Entity]Types.yaml`): * **MUST NOT** end with `Type` suffix. * Use the natural entity name. * *Example*: `DigitalLibrary` (✅), `CentralDepot` (✅). * *Incorrect*: `DigitalLibraryType` (❌), `CentralDepotType` (❌). **Rationale**: The file context (`WarehouseTypes.yaml`) already establishes these are types. Repeating "Type" in the class name is redundant and makes the class name less natural when used as an object instance (e.g., "This object is a CentralDepot"). --- ## Examples ### Current Implementations | Base Class File | Subclasses File | Subclass Count | Description | |-----------------|-----------------|----------------|-------------| | `DigitalPlatformType.yaml` | `DigitalPlatformTypes.yaml` | 69 | Digital platform type taxonomy | | `WebPortalType.yaml` | `WebPortalTypes.yaml` | ~15 | Web portal type taxonomy | | `CustodianType.yaml` | `CustodianTypes.yaml` | 19 | Heritage custodian type taxonomy (GLAMORCUBESFIXPHDNT) | | `DataServiceEndpointType.yaml` | `DataServiceEndpointTypes.yaml` | 7 | API/data service endpoint types | ### File Structure Example ``` modules/classes/ ├── DigitalPlatformType.yaml # Abstract base class ├── DigitalPlatformTypes.yaml # 69 concrete subclasses ├── WebPortalType.yaml # Abstract base class ├── WebPortalTypes.yaml # ~15 concrete subclasses ├── CustodianType.yaml # Abstract base class └── CustodianTypes.yaml # 19 concrete subclasses ``` --- ## Import Pattern The subclasses file MUST import the base class file: ```yaml # In DigitalPlatformTypes.yaml (subclasses file) id: https://w3id.org/heritage-custodian/linkml/digital_platform_types name: digital_platform_types imports: - linkml:types - ./DigitalPlatformType # Import base class (singular) classes: DigitalLibrary: is_a: DigitalPlatformType # Inherit from base description: >- A digital library platform providing access to digitized collections. class_uri: schema:DigitalDocument DigitalArchive: is_a: DigitalPlatformType description: >- A digital archive for born-digital or digitized archival materials. ``` --- ## Slot Range Pattern When other classes reference the type taxonomy, use the **base class** (singular) as the range: ```yaml # In DigitalPlatform.yaml imports: - ./DigitalPlatformType # Import base class for range - ./DigitalPlatformTypes # Import subclasses for validation classes: DigitalPlatform: slots: - platform_type slot_usage: platform_type: range: DigitalPlatformType # Use base class as range description: >- The type of digital platform. Value must be one of the concrete subclasses defined in DigitalPlatformTypes. ``` --- ## Anti-Patterns ### What NOT to Do | Anti-Pattern | Why It's Wrong | Correct Alternative | |--------------|----------------|---------------------| | `DigitalPlatformTypeBase.yaml` | "Base" suffix is redundant; singular "Type" already implies base class | `DigitalPlatformType.yaml` | | `DigitalPlatformTypeClasses.yaml` | "Classes" is less intuitive than "Types" for a type taxonomy | `DigitalPlatformTypes.yaml` | | All types in single file | Large files are hard to navigate; separation clarifies architecture | Split into Type.yaml + Types.yaml | | `DigitalPlatformEnum.yaml` | Enums lack extensibility; class hierarchies are preferred | Use class hierarchy pattern | | `CentralDepotType` (Class Name) | Redundant "Type" suffix on concrete subclass | `CentralDepot` | ### Example of Incorrect Naming ```yaml # WRONG - Don't use "Base" suffix # File: DigitalPlatformTypeBase.yaml classes: DigitalPlatformTypeBase: # Redundant "Base" abstract: true ``` ```yaml # CORRECT - Use singular "Type" # File: DigitalPlatformType.yaml classes: DigitalPlatformType: # Clean, clear naming abstract: true ``` --- ## Rationale 1. **Clarity**: "Type" (singular) = one abstract concept; "Types" (plural) = many concrete implementations 2. **Discoverability**: Related files appear adjacent in alphabetical directory listings 3. **Consistency**: Follows established pattern across entire schema 4. **Semantics**: Mirrors natural language ("a platform type" vs "the platform types") 5. **Scalability**: Easy to add new types without modifying base class file --- ## Migration Checklist When renaming existing files to follow this convention: ### Pre-Migration - [ ] Identify all files referencing the old name - [ ] Create backup or ensure version control is clean - [ ] Document the old → new name mapping ### File Rename - [ ] Rename file: `[Entity]TypeBase.yaml` → `[Entity]Type.yaml` - [ ] Update `id:` field in renamed file - [ ] Update `name:` field in renamed file - [ ] Update class name inside the file - [ ] Update all internal documentation references ### Update References - [ ] Update imports in `[Entity]Types.yaml` (subclasses file) - [ ] Update `is_a:` in all subclasses - [ ] Update imports in consuming classes (e.g., `DigitalPlatform.yaml`) - [ ] Update `range:` in slot definitions - [ ] Update any `slot_usage:` references ### Documentation - [ ] Update AGENTS.md if convention is documented there - [ ] Update any design documents - [ ] Add migration note to changelog ### Verification ```bash # Verify no references to old name remain grep -r "OldClassName" schemas/20251121/linkml/ # Verify new file exists ls -la schemas/20251121/linkml/modules/classes/NewClassName.yaml # Verify old file is removed ls -la schemas/20251121/linkml/modules/classes/OldClassName.yaml # Should fail # Validate schema linkml-validate schemas/20251121/linkml/01_custodian_name.yaml ``` --- ## Related Rules - **Rule 0**: LinkML Schemas Are the Single Source of Truth - **Rule 9**: Enum-to-Class Promotion - Single Source of Truth --- ## Changelog | Date | Version | Change | |------|---------|--------| | 2025-01-04 | 1.0.0 | Initial rule created after DigitalPlatformType refactoring |