Article illustration 1

When Data Models Fall Short

In modern software systems, there is a persistent disconnect between how we think about data and how we are forced to store it. In your application code, polymorphism is a standard concept: objects inherit from a base class, override behavior, and participate in systems that treat them according to shared traits. But in most databases, this concept is nearly impossible to model cleanly.

Relational databases give you rigid tables. Document databases let you store anything but validate nothing. Graph databases offer flexibility but often lack robust typing or inheritance. That is one of the big gaps TypeDB was built to close.

Polymorphism is a foundational idea in software engineering: it allows different types to be treated through a shared interface, simplifying logic and enabling flexible, reusable design. It's what allows a Triangle and a Circle to be treated as a Shape, or a Manager and Intern as Employees.

But this power rarely makes it to the data layer. Most databases treat polymorphism as an afterthought or force users to replicate it manually through complex JOINs, label juggling, or dynamic typing. This means your codebase often has to bend over backward to make up for what the database can't express. Logic gets duplicated, integrity checks move to the application layer, and querying becomes more about remembering structure than capturing meaning.

We believe the data layer should be just as expressive as the application layer, and that starts with making polymorphism a first-class citizen.

Why Most Systems Get Polymorphism Wrong

We introduced it because real-world domains are inherently polymorphic:

  • A robotic arm might act as both a Sensor and an Actuator.
  • A financial instrument can be both a Bond and an Equity.
  • A person might act as an Employee, Contractor, and Mentor simultaneously.

Trying to model this in a traditional system usually means duplicating logic across tables, introducing runtime branching with brittle if/else checks, or manually synchronising schemas. These workarounds are fragile and grow increasingly complex as your domain evolves.

Polymorphism in the Wild

Let's walk through a few real-world domains where polymorphism makes a concrete difference:

Finance

Convertible notes are both debt and equity. Trading platforms often treat financial instruments as a flat list, but the logic to handle settlements, taxation, or risk varies by subtype. With polymorphism, a convertible note can be a supertype of both bond and equity, and logic is inherited and enforced.

Robotics

A robotic gripper may function as a sensor, actuator, or tool, depending on the context. In rigid models, you'd either duplicate the object or flatten it, losing critical information. With polymorphism, one object can express its multi-role nature without breaking structure.

Life Sciences

In genetic studies, a single gene might act as both a regulator and a target. Polymorphism enables layered classification systems and makes it easy to express composite roles or behaviors without resorting to label hacks.

In all these domains, what seems like "edge cases" become central use cases, and polymorphism is the only clean solution.

From Flat to Flexible: A Schema Walkthrough

Let's say you're building a system to track people and their employment types. In a traditional relational setup, you might have:

  • A table for employee
  • A table for contractor
  • A table for intern

Each has overlapping but not identical columns. You'll need UNIONs or separate queries, and any relationships (like benefits, teams, or projects) need to be redefined for each type.

Now contrast that with a polymorphic model in TypeDB:

```entity person,
plays assigned:assignee;
entity employee, sub person;
entity contractor, sub person;
entity intern, sub person;

relation assigned,
relates assignee,
relates project;


You write your query once:

```match $a isa assigned (assignee: $p, project: $proj);
select $p, $proj;

…and regardless of whether $p is an intern, contractor, or employee, the database returns results safely and correctly.

This illustrates a key point: you define relationships in terms of shared traits, not every possible subtype, and the database enforces the rules. As a result, it's far easier to maintain as your model evolves.

A Concrete Example: Cyber Threat Intelligence

Let's say you are modelling threat data. In the STIX 2.1 standard, you have dozens of types: Malware, Infrastructure, Domain Name, IP Address. Each can be used in different ways, but they often share behavior. For example, many can be "used by" a threat actor.

With TypeDB, you can model them like this:

```entity cyber-object,
plays uses:used;
entity malware, sub cyber-object;
entity infrastructure, sub cyber-object;
entity domain-name, sub cyber-object;

relation uses,
relates user,
relates used;


Now you can query:

```match $r isa uses (user: $actor, used: $asset);
select $actor, $asset;

TypeDB will return all uses of any cyber-object — malware, domains, infrastructure — without needing separate joins, filters, or label gymnastics.

Why It Matters: The Outcome for Different User Groups

For Engineering: Safer Code, Less Boilerplate

With polymorphism, you define shared behavior once in your schema. The database enforces relationships safely and automatically. Instead of hardcoding logic or scattering rules across services, you gain a single, expressive model that does more with less.

For Product Leaders: Agility & Future-Proofing

Polymorphism means fewer migrations and less schema churn. Need to add a new subtype? Just define it, and your existing logic continues to work. Want to query at a higher abstraction level? The schema already supports it. It is a model that scales with complexity instead of collapsing under it.

For Data Teams: Semantic Discovery

Analysts shouldn't have to know the physical storage location of every data point. TypeDB allows for semantic querying. You can ask for Risk-Factor and get back every relevant subtype without having to manually union tables or filter diverse document collections.

Performance and Optimization

It is fair to ask: does polymorphism slow things down? In TypeDB, the answer is no, because the type hierarchy powers the inference engine.

  • Pruning: Type constraints are used to prune irrelevant paths during query execution.
  • Optimization: Shared roles and inherited properties let the engine optimize matching.

In fact, polymorphism often improves performance in large, complex queries because the engine can reason over the type graph to find minimal execution plans.

Beyond the Traditional ORM

Many teams try to solve database expressivity by layering object-relational mappers (ORMs) on top of relational databases. While ORMs help in application code, they don't change the underlying limitations of the database.

Feature TypeDB Traditional ORMs
Type Inheritance ✅ Native ❌ Syntactic only
Schema Validation ✅ Strict ⚠️ Limited by underlying DB
Polymorphic Queries ✅ Native ❌ Manual (Joins/Unions)

Polymorphism in ORMs is syntactic. In TypeDB, it's semantic. ORMs create the structure in code. Language integration in TypeDB is a much more direct, simple, and maintainable layer of code. ORMs map flat tables to nested classes. TypeDB lets you design schemas that are hierarchies.

If you're building logic-rich systems where correctness matters, pushing expressivity into the data layer avoids duplication, improves performance, and ensures consistency.

Polymorphism and Semantics

Polymorphism is a robust foundation for semantics. When your schema expresses what things are, what they inherit, and how they relate, you unlock:

  • Inference: Queries that generalize correctly over all relevant subtypes.
  • Safety: Schema-level validation of constraints and role compatibility.
  • Composability: Logic that layers rather than fragments.

In TypeDB, polymorphism is how you encode rich meaning into your data. It's the difference between a schema as metadata and a schema as logic.

Final Thought: Expressive Models Are Safer Models

Most bugs in data systems are caused by bad assumptions. When your schema expresses your intent, and your queries are checked against that schema, you eliminate a whole class of silent errors. Polymorphism represents safety, clarity, and correctness at scale.

Your data is already polymorphic. It's time your database understood that.

Source: This article is based on content from TypeDB's blog at https://typedb.com/blog/the-case-for-a-polymorphic-database