Skip to content

Commit 91e337e

Browse files
committed
docs(codegen): add CDA logical model generation guide
Covers: package landscape (hl7.cda.uv.core, au.digitalhealth.cda.schema), structural differences from FHIR R4/R5, output namespace layout, the #[LogicalModel] attribute, package routing by name prefix, implementation status table, architecture decisions, and risk register.
1 parent b1975c0 commit 91e337e

2 files changed

Lines changed: 218 additions & 0 deletions

File tree

docs/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* [Generating Base FHIR Models](code-generation/base-models.md)
1515
* [Generating Implementation Guides](code-generation/implementation-guides.md)
1616
* [Generated Output Structure](code-generation/output-structure.md)
17+
* [Generating CDA Logical Models](code-generation/cda.md)
1718

1819
## Models
1920

docs/code-generation/cda.md

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
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

Comments
 (0)