Skip to main content
fi-fhir docs

Quick Reference

Terminology Systems Planning

This document details healthcare code systems, version management, and the fi-fhir terminology normalization strategy.

Quick Reference

FeatureStatusImplementation
Code system URIspkg/terminology/mapper.go (LOINC, SNOMED, ICD-10, CPT)
CSV mapping loaderpkg/terminology/mapper.go:LoadFromCSV()
Mapper registrypkg/terminology/mapper.go:Registry
MapToLOINC/SNOMEDConvenience methods for common targets
LOINC file loaderpkg/terminology/loinc.go:LOINCLoader
Panel expansionpkg/terminology/loinc.go:ExpandPanel()
UMLS API clientpkg/terminology/umls.go:UMLSClient
Cross-walk queriespkg/terminology/umls.go:CrossWalk() (ICD-10↔SNOMED)
Concept normalizationpkg/terminology/umls.go:NormalizeCode()

Code Systems Reference

Clinical Coding Systems

SystemOIDURIDomainLicense
LOINC2.16.840.1.113883.6.1http://loinc.orgLabs, vitals, documentsFree (registration)
SNOMED CT2.16.840.1.113883.6.96http://snomed.info/sctClinical findingsNLM license (US free)
ICD-10-CM2.16.840.1.113883.6.90http://hl7.org/fhir/sid/icd-10-cmUS diagnosesPublic domain
ICD-10-PCS2.16.840.1.113883.6.4http://www.cms.gov/Medicare/Coding/ICD10US inpatient proceduresPublic domain
CPT2.16.840.1.113883.6.12http://www.ama-assn.org/go/cptProcedures (billing)AMA license ($)
HCPCS2.16.840.1.113883.6.285https://www.cms.gov/Medicare/Coding/HCPCSReleaseCodeSetsMedicare itemsPublic domain
RxNorm2.16.840.1.113883.6.88http://www.nlm.nih.gov/research/umls/rxnormMedicationsFree (UMLS)
NDC2.16.840.1.113883.6.69http://hl7.org/fhir/sid/ndcDrug productsPublic domain
CVX2.16.840.1.113883.12.292http://hl7.org/fhir/sid/cvxVaccinesPublic domain

Administrative Coding Systems

SystemDomainExample
HL7 TablesMessage componentsSex (M/F/U), Marital status
NUBCUB-04 form codesRevenue codes, condition codes
X12 Code ListsEDI transactionsClaim frequency, filing indicator
Place of ServiceFacility types11=Office, 21=Inpatient, 23=ER

Version Management Strategy

Annual Update Cycles

SystemUpdate FrequencyEffective DateKey Changes
ICD-10-CMAnnualOct 1 (Fiscal Year)Codes added/deleted/revised
ICD-10-PCSAnnualOct 1Procedure codes updated
CPTAnnualJan 1~300 changes per year
HCPCSQuarterlyJan/Apr/Jul/OctLevel II codes
LOINC~2x/yearVariable~5,000 new codes/release
SNOMED CT2x/yearJan/Jul (US Ed)Thousands of changes
RxNormMonthly1st MondayDrug updates

Version Handling in fi-fhir

fi-fhir supports pinning expected terminology release versions and enforcing them at runtime.

terminology:
  # PostgreSQL connection string for the terminology schema (terminology.* tables)
  db_url: ${secret:TERMINOLOGY_DATABASE_URL}

  # Pinned expected active versions per vocabulary.
  # Keys are case-insensitive and accept common aliases (e.g. "snomed" -> SNOMEDCT_US).
  pins:
    loinc: '2.77'
    icd10cm: 'FY2024'
    snomed: '2024-03-01'
    rxnorm: '2024-01'

  # Enforcement policy when pins do not match the active DB release:
  # - pass: ignore
  # - warn: emit ParseWarnings / startup warnings
  # - error: fail startup / fail CLI command
  policy: warn

Equivalent environment variables:

  • FI_FHIR_TERMINOLOGY_DB_URL
  • FI_FHIR_TERMINOLOGY_PINS (e.g. loinc=2.77,icd10cm=FY2024)
  • FI_FHIR_TERMINOLOGY_POLICY (pass|warn|error)

Code Normalization Pipeline

Input → Canonical → Output Flow

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Source Code    │────▶│  Canonical Form │────▶│  Target Code    │
│  (may vary)     │     │  (normalized)   │     │  (as needed)    │
└─────────────────┘     └─────────────────┘     └─────────────────┘

Examples:
HL7v2 OBX: "WBC^White Blood Count^LOCAL" → LOINC 6690-2 → FHIR Observation.code
CSV: "glucose" → LOINC 2345-7 → FHIR Observation.code
EDI 837: "99213" → CPT 99213 → FHIR Claim.item.productOrService

Mapping Table Structure

// Code mapping entry
type CodeMapping struct {
    SourceSystem  string `json:"source_system"`
    SourceCode    string `json:"source_code"`
    SourceDisplay string `json:"source_display,omitempty"`

    TargetSystem  string `json:"target_system"`
    TargetCode    string `json:"target_code"`
    TargetDisplay string `json:"target_display"`

    Equivalence   string `json:"equivalence"` // equivalent, wider, narrower, inexact
    Notes         string `json:"notes,omitempty"`
}

Mapping File Format (CSV)

source_system,source_code,source_display,target_system,target_code,target_display,equivalence
LOCAL_LAB,GLU,Glucose,http://loinc.org,2345-7,Glucose [Mass/volume] in Serum or Plasma,equivalent
LOCAL_LAB,HGB,Hemoglobin,http://loinc.org,718-7,Hemoglobin [Mass/volume] in Blood,equivalent
LOCAL_LAB,WBC,White Count,http://loinc.org,6690-2,Leukocytes [#/volume] in Blood,equivalent

LOINC Specifics

LOINC Code Structure

Component|Property|Time|System|Scale|Method
   │          │      │     │      │     │
   └──────────┴──────┴─────┴──────┴─────┴── 6 axes define the observation

Common LOINC Categories

Panel/GroupLOINC CodeTests Included
CBC58410-2WBC, RBC, Hgb, Hct, Plt, MCV, MCH, MCHC
BMP51990-0Glucose, BUN, Creatinine, Na, K, Cl, CO2, Ca
CMP24323-8BMP + Albumin, Bilirubin, ALT, AST, ALP
Lipid Panel57698-3Total Chol, HDL, LDL, Triglycerides
Urinalysis24356-8Color, clarity, pH, specific gravity, etc.

LOINC Version Compatibility

# LOINC version handling
loinc:
  preferred_version: '2.76'

  # Codes that changed meaning between versions
  deprecated_codes:
    - code: '2093-3' # Old cholesterol code
      deprecated_in: '2.60'
      replacement: '2093-3' # Same code, different definition

  # Cross-version mappings
  version_mappings:
    - from_version: '2.65'
      to_version: '2.76'
      file: ./mappings/loinc_2.65_to_2.76.csv

SNOMED CT Specifics

Hierarchy Navigation

SNOMED uses an "is-a" hierarchy. A child concept implies the parent:

  • 44054006 (Diabetes mellitus type 2) IS-A 73211009 (Diabetes mellitus)
  • Queries for "diabetes" should find both

Expression Constraint Language (ECL)

# All types of diabetes
< 73211009 |Diabetes mellitus|

# Direct children only
<! 73211009 |Diabetes mellitus|

# Diabetes OR hypertension
73211009 |Diabetes mellitus| OR 38341003 |Hypertensive disorder|

US Edition vs International

  • US Edition adds US-specific content (ICD-10-CM mappings)
  • Released January and July
  • Backwards compatible with International Edition

ICD-10 Specifics

CM vs PCS

AspectICD-10-CMICD-10-PCS
UseDiagnosesInpatient procedures
Length3-7 characters7 characters (always)
StructureCategory + etiology + detailsSection + Body System + Root Operation + ...
ExampleE11.9 (Type 2 DM)0SG00ZZ (Knee fusion)

Code Validation Rules

// ICD-10-CM validation
func ValidateICD10CM(code string) error {
    // Length: 3-7 characters
    if len(code) < 3 || len(code) > 7 {
        return errors.New("ICD-10-CM must be 3-7 characters")
    }

    // Format: Letter + 2 digits + optional decimal + 0-4 chars
    pattern := `^[A-Z]\d{2}(\.\d{1,4})?$`
    if !regexp.MustCompile(pattern).MatchString(code) {
        return errors.New("invalid ICD-10-CM format")
    }

    // Note: Actual validation requires lookup against code set
    return nil
}

Fiscal Year Handling

// Determine which ICD-10-CM version to use
func GetICD10Version(serviceDate time.Time) string {
    // ICD-10-CM updates October 1
    fiscalYear := serviceDate.Year()
    if serviceDate.Month() >= time.October {
        fiscalYear++
    }
    return fmt.Sprintf("FY%d", fiscalYear)
}

Implementation Plan

Phase 1: Core Infrastructure ⚠️

  • Code system URIs (LOINC, SNOMED, ICD-10, CPT, etc.) - see pkg/terminology/mapper.go
  • MappingEquivalence enum (equivalent, wider, narrower, inexact, unmatched)
  • CodeMapping struct with full provenance fields
  • Version tracking per code system (pins + pass/warn/error policy)

Phase 2: LOINC Support ✅

  • LOINC file loader (CSV from loinc.org) - see pkg/terminology/loinc.go:LOINCLoader
  • Code lookup by code/display - see GetCode(), LookupByDisplay(), SearchCodes()
  • Panel expansion (CBC → individual components) - see ExpandPanel(), GetPanelMembers()

Phase 3: Mapping Engine ✅

  • CSV mapping file parser - see pkg/terminology/mapper.go:LoadFromCSV()
  • Mapper with lookup (Map, MapToLOINC, MapToSNOMED) - see pkg/terminology/mapper.go
  • Mapper Registry for managing multiple source→target pairs
  • HasMapping() check for validation
  • Confidence scoring for fuzzy matches - see pkg/terminology/fuzzy.go:FuzzyMatcher

Phase 4: UMLS Integration ✅

  • UMLS API client - see pkg/terminology/umls.go:UMLSClient
  • Cross-walk queries (ICD-10 ↔ SNOMED) - see CrossWalk(), ICD10ToSNOMED(), SNOMEDToICD10()
  • Concept normalization - see NormalizeCode(), GetConcept(), GetConceptAtoms()
  • Search functionality - see Search() with configurable options
  • Rate limiting and caching - built-in token bucket limiter and result cache
  • Ticket-based authentication - automatic TGT/ST management

Testing Strategy

Unit Tests

  • Valid/invalid code format detection
  • Version comparison logic
  • Mapping lookup accuracy

Integration Tests

  • Real LOINC file parsing
  • End-to-end code normalization
  • FHIR CodeableConcept generation

Test Data

var testCases = []struct {
    input    string
    system   string
    expected string
}{
    {"GLU", "LOCAL", "2345-7"},      // Local → LOINC
    {"E11.9", "ICD10CM", "E11.9"},   // Passthrough
    {"INVALID", "LOCAL", ""},        // Unknown code
}

External Dependencies

DependencyPurposeSource
LOINC TableCode definitionshttps://loinc.org/downloads/
SNOMED CTUS Edition fileshttps://www.nlm.nih.gov/healthit/snomedct/
ICD-10-CMCode listhttps://www.cms.gov/medicare/coding
UMLSCross-walkshttps://uts.nlm.nih.gov/uts/

See Also

References