We built our graph with an RDF/OWL ontology on top. The RDF vs LPG framing was the wrong question.
TL;DR: every fact in our graph is its own node, with properties on it. The vocabulary behind those nodes is RDF/OWL. Neither camp would call this clean, and Wikidata is not strictly one or the other either.
Here is why we ended up there.
What forced it
Our facts need to carry things that are not part of the fact: where it came from, how confident we are, whether a human has reviewed it, and when it was true. Not “this document mentions X”, but “this specific assertion came from page 4 of this version of this document, confidence 0.8, reviewed by nobody yet, valid from March”.
In plain RDF that means reification or named graphs. Both work. Both mean a single fact becomes several triples or its own subgraph, and every query that touches provenance pays for it. At our read patterns that was not a tradeoff we could take.
In a plain property graph it fails differently. An edge cannot be the target of another edge, so “this review points at this fact” has nowhere to live. We needed that constantly.
So facts became nodes. Metadata hangs off them directly. Reviews, conflicts and time ranges point at them like anything else.
What RDF still gives us, which is why the ontology stayed
Customers arrive with their own OWL and SKOS vocabularies and do not want to give them up
Global identifiers mean two datasets can be merged without a mapping table that rots
Class hierarchies, inverses and transitivity get derived instead of hardcoded
Export is a real requirement, not a checkbox
So the storage model is a property graph and the vocabulary is RDF/OWL. We export to RDF. The reasoning we need happens against the ontology, not against the store.
What this costs us, since that is the interesting part
We are off the standards path where it counts. SPARQL federation against us is not a thing. SHACL validation needs translating rather than just running.
Our uniqueness constraints are awkward, because there are now several assertions per subject-predicate pair and only one is current.
Every engineer who joins has to learn why we did not do the normal thing, twice, because the normal thing is different depending on which camp they came from.
Closed world validation at write time gives us data quality and then gets in the way the moment we integrate with someone whose model disagrees with ours.
On RDF 1.2
Triple terms solve exactly our problem, properly. They are also still a Working Draft. We had to ship in 2026, so we built this and will migrate the parts that map cleanly when it stabilises. If anyone here is running RDF 1.2 in production already, I would genuinely like to know how that is going.
The question I am actually asking
Is the hybrid a legitimate position or a transitional hack that we will regret in three years? The strict-RDF argument that worries me most is interoperability: we are optimising for our own read path at the cost of being part of a larger graph. I do not have a good answer to that one yet.


Replies
Be the first to reply
Have a question or a thought to share? Add a comment above to start the conversation.