One Table Per Subclass Has a Real Cost
Why nothing becomes a table until you say so, why price should never be a float, and the real cost of representing inheritance as one table per subclass, worked through with a pen hierarchy.
An ORM automates the gap between a class diagram and a schema, but it doesn't do that automatically for every class in a codebase.
Nothing Is a Table Until You Say So
A plain class, by default, is just a class. An ORM has no way of knowing a class is meant to be persisted unless something explicitly tells it so. Every ORM has its own mechanism for that declaration, a decorator, an annotation, a schema definition, but the underlying idea is identical everywhere: nothing gets a table for free, only what's explicitly marked does.
Marking every class from the finished class diagram this way is mechanical, one entity at a time. City becomes an entity. Movie becomes an entity. Payment becomes an entity, and so does every other class already settled earlier in this series.
A Primary Key That's Already There, and Still Missing
Marking every class as an entity surfaces an immediate problem. Every one of them still gets flagged as missing a primary key, even after that gets fixed with its own explicit marker, one entity at a time.
The strange part: the ID field already exists. It lives on the shared base model every entity extends, the same pattern from earlier in this series, made so no entity has to write its own ID and timestamps again.
Why: ORMs Default to Representing Inheritance as a Relationship
The problem isn't the base model. The problem is that an ORM does not know these shared fields should just get copied into each child's own table.
Left on its own, an ORM tries to represent inheritance the same way it represents any other object reference: as a relationship between two separate tables, not as one merged row. That's the right move for an actual relationship between two different entities. It's the wrong move here, and understanding why means stepping away from BookMyShow for a moment.
The Pen Example: What Table-Per-Subclass Actually Costs
Picture a Pen base class holding an id and a color. Three subclasses extend it: BallPen adds a ballDiameter, GelPen adds an inkViscosity, FountainPen adds a nibWidth.
One instinctive way to turn this into a schema is a table per class, each one carrying its own copy of the inherited fields alongside its own:
pens:id,colorball_pens:id,color,ballDiametergel_pens:id,color,inkViscosityfountain_pens:id,color,nibWidth
Every real pen object lands in exactly one of these tables, never more than one. A plain Pen that isn't any specific subtype lives only in pens. A ball pen lives only in ball_pens, and so on, the same way a Player and a Bot live in two separate tables, never both.
Now ask a simple question: how many pens exist in total, across every kind? There's no single table to count. The only way to answer it is to count all four tables separately and add the results together yourself.
That's the real cost hiding behind "just create a table per class." It isn't wrong, exactly, but it turns a simple count into a query that has to check every subclass table by name and add up the results by hand.
Keep reading