File formats
Four ways the fact-based modelling community exchanges models — and where Factum sits among them.
The landscape
There is no agreed exchange standard for fact-based models. There are four serious formats, and the first thing to notice is that they do not all describe the same kind of thing. Two are conceptual — they talk about object types, fact types and roles. Two are logical — they talk about tables, columns and keys, with the fact-based content surviving as annotations.
That distinction decides everything about which format can losslessly hold a model and which can only receive one.
| Format | Level | Syntax | Machine-readable contract | Origin |
|---|---|---|---|---|
Factum .orm.json | Conceptual | JSON | JSON Schema 2020-12 | This project |
NORMA .orm | Conceptual | XML | XSD | ORM Foundation / NORMA |
FBM Exchange MetaModel .fbm | Conceptual | XML | XSD + .NET classes | FactEngine community |
| Unified Modelling Schema | Logical | YAML | JSON Schema 2020-12 + Delphi / VB.NET classes | FactEngine community |
| Apache Ossie | Logical core, conceptual ontology | YAML / JSON | JSON Schema 2020-12 | Apache Software Foundation (incubating) |
NORMA .orm
The ORM 2 XML written by NORMA, and the de-facto archive format for the community. It is the most faithful ORM 2 metamodel of the four: everything the notation can draw is in there, including the join paths of external constraints and the full diagram geometry.
Its costs are verbosity, a strong tie to Visual Studio, and a great deal of tool state stored
alongside the model. Factum reads it — ORM: Import NORMA (.orm) File — and keeps the
NORMA element GUIDs as its own ids, so a model imported from NORMA keeps its cross-tool identity.
FBM Exchange MetaModel
An XML format published by the FactEngine community as a deliberate exchange standard for fact-based models — Object-Role Modeling and FCO-IM alike — with an XSD and ready-made .NET classes. It is markedly simpler than NORMA's format while covering the same conceptual ground.
It carries value and entity types, fact types with role groups, readings decomposed into predicate parts, role constraints with join paths, subtype relationships, model notes, synonyms, sample populations, and a multi-page diagram section where each page has a language.
meta and a
hints object: GUID (an identity separate from the readable id),
DBName (a physical name for relational generation), GraphLabel (a label
for property graph generation), and LongDescription / ShortDescription.
Unified Modelling Schema
A YAML format, also from the FactEngine community, with a JSON Schema and generated Delphi and
VB.NET classes. Unlike the other three it is logical rather than conceptual: the unit is
a type with properties, primary keys and relationships, and the fact-based content survives as
FactTypeReadings attached to those properties and relationships.
That makes it a good target for generation and a lossy source. By the time a model is expressed as types with properties, the attributes have already been formed — the design decisions ORM exists to postpone have been taken, and you cannot recover which ones they were.
Apache Ossie
An incubating ASF specification for exchanging semantic metadata across analytics, AI and BI platforms, defined as YAML with a published JSON Schema and a growing set of vendor converters.
Its core specification is a logical semantic model — datasets, fields, relationships, metrics — but its ontology section is conceptual, and it is ORM in all but name:
- concepts typed
EntityTypeorValueType— object types and value types; - relationships with ordered
rolesand optionally named role players — fact types with role names; multiplicityofManyToOneorOneToOne— uniqueness constraints;identify_by— the preferred identifier;extends— subtyping;requires— value and other constraints, as expressions;derived_by— derivation rules;verbalizes— readings, complete with the hyphen-binding convention (has description- {String}) that ORM uses when a reading binds tightly to its role player.
Its ontology_mappings then bind concepts and relationships down to fields of a
logical dataset — the same job Factum's relational and graph mappings do, expressed as data
rather than as code.
hints.
One fact type, four ways
Person works for Company, each role mandatory on the Person side and functional — one elementary fact, written in each format.
Factum .orm.json — roles and readings are separate, and the
uniqueness constraint that makes the fact type functional is its own addressable element:
{
"id": "ft_works",
"roles": [
{ "id": "r1", "objectTypeId": "ot_person" },
{ "id": "r2", "objectTypeId": "ot_company" }
],
"readings": [
{ "id": "rd1", "roleOrder": ["r1", "r2"], "text": "{0} works for {1}", "isPrimary": true },
{ "id": "rd2", "roleOrder": ["r2", "r1"], "text": "{0} employs {1}" }
]
}
// elsewhere, in constraints:
{ "id": "uc1", "kind": "uniqueness", "roles": ["r1"] }
{ "id": "mc1", "kind": "mandatory", "roles": ["r1"] }
FBM Exchange MetaModel — the same content, with readings broken into predicate
parts and constraints in a separate RoleConstraints collection:
<FactType Id="WorksFor" GUID="_64A69DFB-..." Name="PersonWorksForCompany" DBName=""
IsObjectified="false" IsDerived="false" LongDescription="">
<GraphLabel />
<RoleGroup>
<Role Id="_3678ED9A-..." SequenceNr="1" Mandatory="true" JoinedObjectTypeId="Person" />
<Role Id="_785CF2B5-..." SequenceNr="2" Mandatory="false" JoinedObjectTypeId="Company" />
</RoleGroup>
<FactTypeReadings>
<FactTypeReading Id="...">
<PredicateParts>
<PredicatePart SequenceNr="1" Role_Id="_3678ED9A-...">
<PredicatePartText>works for</PredicatePartText>
</PredicatePart>
<PredicatePart SequenceNr="2" Role_Id="_785CF2B5-..." />
</PredicateParts>
</FactTypeReading>
</FactTypeReadings>
</FactType>
Unified Modelling Schema — already a foreign key. The fact is recoverable only from the readings:
- Type: Person
Labels: [Person]
PrimaryKey: [Person_Id]
Properties:
- Name: Company_Id
DataType: Integer
Constraints: [NotNull]
FactTypeReadings:
- Language: Not Defined
Readings: [Person works for Company]
Relationships:
- Name: WORKS_FOR
Source: Person
Target: Company
From: [Company_Id]
To: [Company_Id]
Readings: [Person works for Company, Company employs Person]
IsRelationshipType: false
Apache Ossie ontology — conceptual again, with the relationship grouped under the concept that plays its first role:
- concept: Person
type: EntityType
identify_by: [nr]
relationships:
- name: works_for
roles:
- concept: Company
multiplicity: ManyToOne
verbalizes:
- '{Person} works for {Company}'
- '{Company} employs {Person}'
Read those four in order and the trade-off is visible. FBM says the most per element and costs the
most to write. UMS has already decided that Company is a foreign key on Person. Ossie says almost
exactly what Factum says, in half the space, by hanging relationships off concepts and folding
the uniqueness constraint into a multiplicity keyword.
What each format carries
| Concept | Factum | NORMA | FBM | UMS | Ossie |
|---|---|---|---|---|---|
| Object types and value types | yes | yes | yes | partial | yes |
| N-ary fact types | yes | yes | yes | no | yes |
| Multiple readings per fact type | yes | yes | yes | yes | yes |
| Full constraint family | yes | yes | yes | no | as expressions |
| Join paths on set-comparison constraints | no | yes | yes | no | as expressions |
| Subtyping and objectification | yes | yes | yes | no | subtyping only |
| Derivation rules | stored, not evaluated | yes | yes | no | yes |
| Diagram geometry | yes | yes | yes, multi-page | no | no |
| Stable GUIDs | meta.guid | yes | yes | no | names are the identity |
| Descriptions per element | meta.description | notes | yes | no | yes |
| Synonyms | meta.synonyms | no | yes | Alias | ai_context.synonyms |
| Physical name for generation | hints.relational.tableName | no | DBName | Labels | mappings |
| Graph label for generation | hints.graph.label | no | GraphLabel | Label / Labels | no |
| Guidance for AI tools | meta.aiContext | no | no | no | ai_context |
| Multilingual readings | lang | no | page language | per reading | no |
| Sample populations | no | yes | yes | yes | no |
| Vendor extension namespace | x- | no | no | no | custom_extensions |
Why Factum keeps its own format
Adopting one of the others outright was considered and rejected, for four reasons.
A conceptual model must stay conceptual
UMS is out immediately: saving to it would mean deciding, at save time, which fact types become columns and which become tables. That is precisely the decision ORM exists to defer, and it is the decision Factum's two mappings exist to let you make twice, differently, from the same model. A format that cannot hold an unmapped model cannot be the native format of a conceptual modelling tool.
Text that diffs and merges
Factum's premise is that a model is source code: it lives in the repository next to everything else and goes through review. Both XML formats defeat that. A NORMA or FBM document is a single deep tree where an element's identity is its position as much as its id, so a two-line conceptual change produces a diff nobody can read and a merge conflict nobody can resolve.
Factum's structure is deliberately flat and id-addressed for exactly this reason. Adding a constraint appends one object to one array; nothing else in the file moves.
The editor is a custom editor over a text document
The diagram is a VS Code custom editor backed by the file's text. That is what makes the dirty
indicator, the undo stack, file watching and source control work without any special handling,
and what makes ORM: Open Model Source (JSON) possible at all. It requires a format
VS Code can parse, validate and hand back as text — which in practice means JSON.
An exchange format and a working format are different jobs
The last reason is the most important one, and it is why this is not a rejection of the others. The FBM Exchange MetaModel is explicitly an exchange format: its job is to be written once, read once, and lose nothing in between. A working format is edited a hundred times a day by a human and a program in turn, and is judged on how well it diffs and how easy it is to fix by hand at two in the morning.
These are different jobs and they want different shapes. The right relationship between them is a
converter, not a merger — and a converter needs the source format to carry what the
target format expects. That is the whole reason for
meta and
hints.
Which standard it is closest to
Two different answers, because two different things are being standardised.
Conceptually: Apache Ossie's ontology
Of everything surveyed, Ossie's ontology section is the closest match to what Factum stores, and the alignment is close to term-for-term:
| Factum | Ossie ontology |
|---|---|
objectTypes[].kind | concept.type: EntityType / ValueType |
factTypes[].roles[] | relationships[].roles[] |
roles[].name | roles[].name |
readings[].text with {0} placeholders | verbalizes[] with {Concept} placeholders |
| uniqueness constraint on one role | multiplicity: ManyToOne |
isPreferredIdentifier | identify_by |
subtypeRelations | extends |
| value constraint | requires expression |
derivationRule | derived_by |
meta.aiContext | ai_context |
The differences are real but narrow. Ossie groups each relationship under the concept playing its first role, where Factum keeps fact types in a flat list; Ossie has no diagram, no objectification and no explicit constraint objects, expressing constraints as expressions instead. Everything Factum stores that Ossie also models can be written out; the reverse is nearly true too.
Structurally: the JSON Schema and OpenAPI conventions
For the shape of the file rather than its content, Factum follows the conventions that have actually been adopted across the wider ecosystem, not anything specific to fact-based modelling:
- JSON Schema 2020-12 as the published contract, referenced from the document
by
$schema— the same choice UMS and Ossie both made, and the one that gives validation and completion in any editor and a compilable contract in any language. x-prefixed extensions for anything outside the specification, the convention used by OpenAPI, AsyncAPI and CloudEvents. Ossie solves the same problem with acustom_extensionslist of vendor blobs; thex-prefix keeps extensions in place, next to what they extend.- An additive major version. Version 2 only adds optional keys, so every version 1 file is a valid version 2 file.
x- namespace and in hints, where unknown target keys are legal. A typo
is caught; a deliberate extension is not.
The bridge to each format
The point of the metadata added in format version 2 is that a converter to any of these three now has somewhere to put what it reads, and somewhere to read what it writes.
| Factum | FBM | UMS | Ossie |
|---|---|---|---|
meta.guid | GUID | — | — |
meta.description | LongDescription | — | description |
meta.shortDescription | ShortDescription | — | — |
meta.synonyms | Synonyms | Alias | ai_context.synonyms |
meta.aiContext | — | — | ai_context |
meta.uri | — | — | — |
hints.relational.tableName | DBName | Labels | datasets[].source |
hints.graph.label | GraphLabel | Label | — |
hints.graph.labels | — | Labels | — |
readings[].lang | Page/@Language | FactTypeReadings[].Language | — |
hints.<target> (open) | — | — | concept_mappings |
The last row is the one worth planning around. An Ossie concept_mapping binds a
concept to a dataset and an expression — a per-element binding to a generation target, which is
the shape of a hints entry. An Ossie exporter would read hints.ossie
and need no change to the format to do it.
Importing and exporting
Factum reads and writes all four formats. ORM: Import Model takes a
.orm, .fbm or .yaml file and writes an
.orm.json beside it, picking the reader from the file — the extension where that is
decisive, and a marker in the text where it is not, since Ossie and UMS share
.yaml. ORM: Export Model As writes any of the four back out.
| Format | Import | Export | What survives |
|---|---|---|---|
NORMA .orm | yes | yes | Object types, fact types, readings, constraints, subtypes. Geometry is read but not written. |
FBM .fbm | yes | yes | The whole conceptual model, plus GUIDs, descriptions and synonyms. |
| Apache Ossie ontology | yes | yes | Concepts, relationships, verbalizations, multiplicity, identifiers, subtyping, derivation. |
| Unified Modelling Schema | yes | yes | Export is faithful. Import recovers the logical schema, not the model behind it. |
Both directions return warnings rather than dropping content silently. Exporting a model with an objectified fact type to Ossie tells you that Ossie has no objectification and that the entity type was written plain; importing UMS tells you that what came back is the shape of the data rather than the elementary facts behind it.
What each conversion has to reconcile
The interesting work is never the elements that match. It is the handful that do not.
- Readings. FBM decomposes a reading into predicate parts, each carrying a role
and the text that trails it; Factum uses NORMA's
{0}placeholders. Ossie names its placeholders by concept —{Person} works for {Company}— so conversion matches placeholders to roles by player name, falling back to the role name when one concept plays more than one role. - Mandatory roles. FBM records mandatory on the role itself; Factum makes it a constraint. Neither the role attribute nor the constraint list alone carries both.
- Uniqueness. Ossie has no constraint objects.
ManyToOneis uniqueness over every role but the last, andOneToOneadds the reverse. - Ring types. FBM concatenates them into one attribute, so
AsymmetricIntransitiveis two constraints' worth of information in one string. - Built-in concepts. Every Ossie ontology implicitly includes
Integer,Stringand the rest. Redeclaring them on export would turnextends: [Integer]into a subtype link on the next import. - Value constraints. NORMA nests them under the object type or role in a
ValueRestrictionrather than listing them with the other constraints.
Status
All four formats can be read and written. What is still missing is at the edges of each one:
- FBM sample populations (
Instance,Fact) and multi-page diagrams are read but not modelled, so only the first page survives and populations are dropped. - Join paths on set-comparison constraints are read as plain role sequences. Both NORMA and FBM can say more here than Factum models.
- Ossie's
ontology_mappings— the binding from concepts down to dataset fields — is neither read nor written. It is the natural home for ahints.ossietarget. - The NORMA exporter writes no diagram geometry.