|
| 1 | +# Generating CDA Logical Models |
| 2 | + |
| 3 | +CDA R2 (Clinical Document Architecture) and its derivatives — including the Australian Digital |
| 4 | +Health Agency schema used for MyHealthRecord — are published on the FHIR package registry as |
| 5 | +FHIR **logical models**. This page describes what is generated, how the packages are structured, |
| 6 | +and the output namespace layout. |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +## CDA Package Landscape |
| 11 | + |
| 12 | +### Available Packages |
| 13 | + |
| 14 | +| Package | Version | FHIR base | Registry | |
| 15 | +|---|---|---|---| |
| 16 | +| `hl7.cda.uv.core` | `2.0.2-sd` | 5.0.0 (R5) | `packages.fhir.org` | |
| 17 | +| `au.digitalhealth.cda.schema` | `1.0.1` | 5.0.0 (R5) | `packages.fhir.org` | |
| 18 | + |
| 19 | +Both packages use the standard FHIR `.tgz` format and are downloaded by the same |
| 20 | +`PackageLoader` used for FHIR R4/R4B/R5 packages. |
| 21 | + |
| 22 | +### Package Dependency Chain |
| 23 | + |
| 24 | +``` |
| 25 | +au.digitalhealth.cda.schema#1.0.1 |
| 26 | + └── depends on: hl7.cda.uv.core#2.0.2-sd |
| 27 | +``` |
| 28 | + |
| 29 | +Generate the core package before the AU extension package. |
| 30 | + |
| 31 | +### Australian Extensions vs FHIR Profiles |
| 32 | + |
| 33 | +FHIR profiles use `derivation: constraint` to **restrict** an existing type. |
| 34 | +The AU CDA schema uses `derivation: specialization` to **add new XML elements** — the same |
| 35 | +mechanism as the HL7 international core. AU classes (`au-ClinicalDocument`, |
| 36 | +`au-SubstanceAdministration`, etc.) extend corresponding core classes with |
| 37 | +Australian-specific XML elements. They are proper subclasses, not constrained views. |
| 38 | + |
| 39 | +--- |
| 40 | + |
| 41 | +## Structural Differences from FHIR R4/R5 |
| 42 | + |
| 43 | +### Every StructureDefinition is `kind: logical` |
| 44 | + |
| 45 | +``` |
| 46 | +FHIR R4/R5 CDA |
| 47 | +───────────── ────────────────────────── |
| 48 | +kind: resource kind: logical |
| 49 | +kind: complex-type kind: logical |
| 50 | +kind: primitive kind: logical |
| 51 | +derivation: spec. derivation: specialization |
| 52 | +``` |
| 53 | + |
| 54 | +There are no `resource`, `complex-type`, or `primitive-type` kinds anywhere in CDA packages. |
| 55 | + |
| 56 | +### CDA Inheritance Hierarchy |
| 57 | + |
| 58 | +``` |
| 59 | +http://hl7.org/fhir/StructureDefinition/Base ← FHIR root |
| 60 | + └── ANY |
| 61 | + └── InfrastructureRoot |
| 62 | + ├── ClinicalDocument |
| 63 | + ├── Section |
| 64 | + ├── Act, SubstanceAdministration, Observation, Organizer … |
| 65 | + ├── AssignedAuthor, PatientRole, RecordTarget … |
| 66 | + └── (AU) au-ClinicalDocument → ClinicalDocument |
| 67 | + au-SubstanceAdministration → SubstanceAdministration … |
| 68 | +``` |
| 69 | + |
| 70 | +V3 data types have their own branch: |
| 71 | + |
| 72 | +``` |
| 73 | +ANY |
| 74 | + ├── QTY → TS, INT, REAL, PQ, MO, RTO |
| 75 | + ├── ST → ED |
| 76 | + ├── BIN |
| 77 | + ├── II |
| 78 | + └── CS → CE → CD |
| 79 | +``` |
| 80 | + |
| 81 | +### CDA-Specific Element Type Codes |
| 82 | + |
| 83 | +CDA element types use fully-qualified CDA StructureDefinition URLs: |
| 84 | + |
| 85 | +| CDA type code | Meaning | |
| 86 | +|---|---| |
| 87 | +| `.../cs-simple` | `classCode`, `typeCode`, `moodCode` (XML attributes) | |
| 88 | +| `.../oid` | OID string values | |
| 89 | +| `.../II` | Instance Identifier | |
| 90 | +| `.../TS` | Point in Time | |
| 91 | +| `.../IVL_TS` | Interval of Time | |
| 92 | +| `.../CS` | Coded Simple Value | |
| 93 | +| `.../CE` | Coded with Equivalents | |
| 94 | +| `.../CD` | Concept Descriptor | |
| 95 | +| `.../ST` | Character String | |
| 96 | +| `.../EN` / `PN` / `ON` | Entity / Person / Organisation Name | |
| 97 | +| `.../AD` | Postal Address | |
| 98 | +| `.../TEL` | Telecom Address | |
| 99 | + |
| 100 | +(All prefixed `http://hl7.org/cda/stds/core/StructureDefinition/`) |
| 101 | + |
| 102 | +### XML Attribute Representation |
| 103 | + |
| 104 | +CDA properties that are XML attributes (not child elements) carry `representation: ["xmlAttr"]`. |
| 105 | +Examples: `classCode`, `typeCode`, `moodCode`, `nullFlavor`, and II sub-properties (`root`, |
| 106 | +`extension`). The generator emits these as `FhirProperty` with an `@`-prefixed |
| 107 | +`xmlSerializedName`, which the XML serialiser already reads correctly. |
| 108 | + |
| 109 | +### XML-Only Serialization |
| 110 | + |
| 111 | +CDA document instances are XML-only. Generated CDA classes carry a `#[LogicalModel]` attribute |
| 112 | +with `xmlNamespace: 'urn:hl7-org:v3'`, which the XML serialiser uses to emit the correct |
| 113 | +namespace declaration on the document root. JSON serialisation of CDA classes throws a |
| 114 | +descriptive exception. |
| 115 | + |
| 116 | +--- |
| 117 | + |
| 118 | +## Generated Output Structure |
| 119 | + |
| 120 | +CDA output is isolated from all FHIR versions under `Models/src/CDA/`: |
| 121 | + |
| 122 | +``` |
| 123 | +src/Component/Models/src/ |
| 124 | +├── R4/ |
| 125 | +├── R4B/ |
| 126 | +├── R5/ |
| 127 | +└── CDA/ |
| 128 | + ├── DataType/ ← V3 data types: II, TS, CS, CE, CD, ST, EN, AD, TEL, IVL_TS … |
| 129 | + │ Base types: ANY, InfrastructureRoot |
| 130 | + ├── Class/ ← CDA act/role/entity/participation classes: ClinicalDocument, Section … |
| 131 | + │ AU extensions: AuClinicalDocument, AuSubstanceAdministration … |
| 132 | + └── Enum/ ← ValueSet enums: NullFlavor, ActClass, ActMood … |
| 133 | +``` |
| 134 | + |
| 135 | +PHP namespaces: |
| 136 | + |
| 137 | +``` |
| 138 | +Ardenexal\FHIRTools\Component\Models\CDA\DataType\ |
| 139 | +Ardenexal\FHIRTools\Component\Models\CDA\Class\ |
| 140 | +Ardenexal\FHIRTools\Component\Models\CDA\Enum\ |
| 141 | +``` |
| 142 | + |
| 143 | +--- |
| 144 | + |
| 145 | +## Class-Level Attribute: `#[LogicalModel]` |
| 146 | + |
| 147 | +Every generated CDA class is tagged with the `LogicalModel` attribute from |
| 148 | +`Ardenexal\FHIRTools\Component\Metadata\Attribute\LogicalModel`: |
| 149 | + |
| 150 | +```php |
| 151 | +#[LogicalModel( |
| 152 | + url: 'http://hl7.org/cda/stds/core/StructureDefinition/ClinicalDocument', |
| 153 | + name: 'ClinicalDocument', |
| 154 | + fhirVersion: '5.0.0', |
| 155 | + xmlNamespace: 'urn:hl7-org:v3', |
| 156 | +)] |
| 157 | +class ClinicalDocument extends InfrastructureRoot { ... } |
| 158 | +``` |
| 159 | + |
| 160 | +The `xmlNamespace` field is `null` for JSON-capable logical models and non-null for XML-only |
| 161 | +targets (all CDA classes use `urn:hl7-org:v3`). |
| 162 | + |
| 163 | +`FhirProperty` is reused unchanged for all property-level metadata (type, cardinality, |
| 164 | +`xmlSerializedName`, `isArray`, `isRequired`, etc.). |
| 165 | + |
| 166 | +--- |
| 167 | + |
| 168 | +## Package Routing |
| 169 | + |
| 170 | +CDA packages are routed to a dedicated `'CDA'` BuilderContext rather than the R5 context, |
| 171 | +because both CDA and FHIR R5 report `fhirVersion: 5.0.0`. Routing is by package name prefix: |
| 172 | + |
| 173 | +| Package name starts with | Routes to | |
| 174 | +|---|---| |
| 175 | +| `hl7.cda.*` | CDA context | |
| 176 | +| `au.digitalhealth.cda.*` | CDA context | |
| 177 | +| anything else | R4 / R4B / R5 context as usual | |
| 178 | + |
| 179 | +CDA packages do not require FHIR terminology packages (`hl7.terminology.*`). CDA ValueSets |
| 180 | +(NullFlavor, ActClass, ActMood, etc.) are bundled in the CDA package itself. |
| 181 | + |
| 182 | +--- |
| 183 | + |
| 184 | +## Implementation Status |
| 185 | + |
| 186 | +| Milestone | Status | Description | |
| 187 | +|---|---|---| |
| 188 | +| M1 — Foundation | ✅ Done | `#[LogicalModel]` attribute; CDA BuilderContext slot; package routing | |
| 189 | +| M2 — Core Generator | Planned | `LogicalModelGenerator`; PHP class files under `CDA/DataType/` and `CDA/Class/` | |
| 190 | +| M3 — Enums | Planned | PHP enums under `CDA/Enum/` for NullFlavor, ActClass, ActMood, etc. | |
| 191 | +| M4 — AU CDA Schema | Planned | `au.digitalhealth.cda.schema` support; AU classes extend core CDA classes | |
| 192 | +| M5 — Serializer | Planned | `urn:hl7-org:v3` namespace on XML root; JSON exception for CDA classes | |
| 193 | +| M6 — Quality Gate | Planned | Full integration tests; PHPStan level 8 clean; documentation | |
| 194 | + |
| 195 | +--- |
| 196 | + |
| 197 | +## Architecture Decisions |
| 198 | + |
| 199 | +| Decision | Rationale | |
| 200 | +|---|---| |
| 201 | +| `#[LogicalModel]` not `#[CDAClass]` | Applies to any logical model IG without duplication | |
| 202 | +| Separate `'CDA'` BuilderContext | Prevents CDA types polluting the R5 namespace | |
| 203 | +| Route by package name, not `fhirVersion` | CDA and FHIR R5 both report `fhirVersion: 5.0.0`; the package name is the only reliable discriminant | |
| 204 | +| Reuse `FhirProperty` unchanged | CDA elements use the same SD element structure; `xmlAttr → xmlSerializedName` already works | |
| 205 | +| Output to `CDA/Class/` not `CDA/Resource/` | CDA has no concept of FHIR resources | |
| 206 | + |
| 207 | +--- |
| 208 | + |
| 209 | +## Risk Register |
| 210 | + |
| 211 | +| Risk | Likelihood | Impact | Mitigation | |
| 212 | +|---|---|---|---| |
| 213 | +| CDA V3 type hierarchy has circular `baseDefinition` refs | Low | High | Cycle detection in `LogicalModelGenerator` | |
| 214 | +| AU package depends on a core version not yet generated | Medium | Medium | Enforce load/generate ordering; validate parent presence | |
| 215 | +| CDA ValueSets use different `compose` structure | Low | Medium | Verify against a real `NullFlavor` ValueSet before M3 | |
| 216 | +| `Class` as a PHP namespace segment (reserved word) | Medium | Low | Rename to `CDA\ClinicalClass\` if nette/php-generator rejects it | |
| 217 | +| `au.digitalhealth.cda.schema` not on `packages.fhir.org` | Low | Medium | Fall back to local package path option in `PackageLoader` | |
0 commit comments