Skip to content

System design ​

openbim-ifc is built around two separations. Both are enforced by tests rather than by convention, and almost every other design choice follows from them.

1. The model knows no domain semantics ​

ifc-model::Model stores (id, type_name, attributes). It has never heard of a wall, a cost item, or a task. Domain crates are views that borrow a &Model and interpret it.

Swipe diagram horizontally

Diagram source
flowchart LR
  accTitle: Structural model between codecs and domain views
  accDescr: STEP, XML, and future JSON codecs read and write one structural model. Cost, schedule, and property crates borrow that model as replaceable typed views.
  STEP["ifc-step"] --> Codec["Codec trait<br/>owned by ifc-model"]
  XML["ifc-xml"] --> Codec
  JSON["future ifc-json"] -.-> Codec
  Codec <--> Model["Model<br/>records + unknown data"]
  Model --> Cost["ifc-cost view"]
  Model --> Schedule["ifc-schedule view"]
  Model --> Properties["ifc-properties view"]

Three consequences, in order of importance:

Data you do not understand still round-trips. A file full of cost entities parses and re-exports intact in a build compiled with no cost crate at all, because storage is structural rather than a domain struct. This is the property that makes the project safe to use while large parts of it remain unimplemented. Verified by crates/openbim-ifc/tests/costing_roundtrip.rs.

Thin applications stay thin. A viewer compiles only the domains it selects. Verified by crates/openbim-ifc/tests/thin_build.rs.

Interpretations are replaceable. A different reading of the same entities is another crate, not a fork of the model.

2. The model knows no serialization ​

Codec is a trait in the model crate; ifc-step and ifc-xml implement it. IFC-JSON would be a third implementation requiring no change to the model.

Format conversion is therefore not a feature — it is reading with one codec and writing with another.

Codecs never import domain semantics. Domain crates never import codecs.

Dependency tiers ​

Swipe diagram horizontally

Diagram source
flowchart BT
  accTitle: openbim-ifc dependency tiers
  accDescr: Dependencies point downward from orchestration through the facade and optional domain or geometry crates to codecs, schema metadata, and the record core.
  L4["L4 orchestration<br/>applications, and the JS / Python / C bindings"] --> L3["L3 facade<br/>openbim-ifc"]
  L3 --> Domains["L2 domain views + validation"]
  L3 --> Geometry["L2 geometry bridges<br/>ifc-geometry / alignment / georef"]
  Domains --> Schema["L1 schema metadata"]
  Domains --> Model["L0 record core<br/>ifc-model"]
  Geometry --> Model
  Geometry --> Axiolid["Neutral Axiolid representation crates"]
  L3 --> Codecs["L1 codecs<br/>ifc-step / ifc-xml"]
  Codecs --> Model
  Schema --> Model

Dependencies point down. Sibling domain crates do not depend on one another; cross-domain workflows belong at L4. ifc-model remains schema-, codec-, and domain-agnostic. The language bindings live in this repository at L4: they wrap the facade, one crate per target (ADR 0013).

Partitioning by pipeline role, not by IFC schema name ​

IFC resource names are evidence, not crate boundaries. The schema mixes storage, geometry input, presentation, and domain semantics in the same resource documents, so the crate layout follows the role a declaration plays:

  1. Geometry input is lowered by ifc-geometry, ifc-alignment, or ifc-georef into format-neutral geometry values.
  2. Domain semantics are borrowed projections over ifc-model.
  3. Geometry-derived outputs such as area and volume are computed outside the semantic crate, then written through it by an application service.

One IFC entity may therefore have projections in two crates. ifc-model owns the record; neither projection owns or duplicates it. For the resources that mix roles, the split is:

IFC conceptGeometry projectionSemantic projectionOrchestration
IfcProfileDefexact shape in ifc-geometryname/type only when neededapplication chooses the use
IfcMaterialProfile*profile reference, cardinal point, offsets in ifc-geometrymaterial, name, priority, category in ifc-materialapplication associates the product
IfcMaterialLayerSetUsagedirection and offset geometry input in ifc-geometrylayer identity and composition in ifc-materialapplication chooses the representation
IfcElementQuantitynoneauthored values, read and write, in ifc-propertiesapplication computes and populates
Geometric representation contextlocal project frame and precision in ifc-geometrynoneapplication composes the map transform
Map conversion, projected CRSnonegeodetic metadata and transform in ifc-georefapplication applies it at the export/query boundary
Presentation appearancenoneall style semantics in ifc-stylerenderer joins by entity ID
Structural analysisgeometry and profile IDs onlymodels, analytical members, connections, actions, loads in ifc-structuralsolver computes results
Construction resourcesnonecapacity and allocation in ifc-resourceapplication links resources to processes and products

Classification, resources, schedules, structural analysis, systems and cost are deliberately separate domains, not candidates for consolidation; ifc-resource is construction-resource planning and never absorbs structural-analysis semantics.

Where geometry stops ​

ifc-geometry answers "what does this IFC entity mean geometrically" and lowers it into the neutral axiolid-model DAG. The bridge itself does not triangulate, evaluate NURBS or perform booleans. Meshing is an opt-in feature that calls a swappable backend (ADR 0012); the default build links no kernel.

See the Axiolid boundary.

Context files ​

Context lives beside what it describes, so a contributor reads only what is on the path to the change:

  • AGENTS.md — the one root file: layout, dependency rule, behaviour rules and the gate.
  • Crate README.md — what the crate is for, how to install it, and the crate-wide design notes no test or ADR already carries.
  • Module docs (//!) — why a module is shaped the way it is.
  • GitHub issues — open work. A marker in code names its issue as TODO(#N), so the tracker and the code point at each other.

Progress logs, task lists and speculative TODOs belong in none of them. crates/ifc-model/tests/progressive_context.rs enforces that the root AGENTS.md is the only one, that every crate has a small README, and that every README or AGENTS.md pointer resolves; cargo run -p xtask -- todo --check rejects a marker without an issue.

Further reading ​

Released under the AGPL-3.0-or-later licence. ISO and CEN standards material is not redistributed.