2.2 Entity Generalization & Specialization
Key Takeaways
- Mendix implements entity inheritance using a Table-per-Type (TPT) relational database strategy, creating separate physical tables for generalizations and each specialization.
- Retrieving a specialization requires an automatic SQL INNER JOIN with its generalization table, while retrieving generalizations polymorphically can produce complex multi-table joins.
- Deep inheritance hierarchies (beyond 1-2 levels) significantly degrade database query and transaction performance due to compounding SQL joins on every CRUD operation.
- 1-to-1 associations should be preferred over generalization when an entity's role can mutate over time (e.g., a Person becoming an Employee or Customer), because Mendix does not support changing an object's entity type at runtime.
2.2 Entity Generalization & Specialization
Object-oriented domain modeling allows developers to define shared structures and behaviors through Generalization (base entities) and Specialization (sub-entities). In Mendix, generalization allows a specialized entity to inherit all attributes, associations, validation rules, and event handlers from its generalization entity. While this provides code reuse and polymorphic flexibility, the underlying database implementation has massive ramifications for query performance, transactions, and long-term architectural stability.
Principles of Domain Inheritance: Generalization and Specialization
In Studio Pro, setting an entity's generalization creates an is-a relationship:
Doctoris aEmployeeEmployeeis aPersonInvoiceis aFinancialDocument
When Doctor specializes Employee, any attribute defined on Employee (such as EmployeeNumber or HireDate) is automatically accessible on Doctor. Furthermore, if Employee has an association to Department, Doctor inherits that association without re-declaring it.
Mendix also provides system-level generalizations. The most common built-in generalizations are:
System.User/Administration.Account: Inherited when modeling custom application users to gain platform authentication, password management, and role assignment.System.FileDocument: Inherited to give an entity binary file storage capabilities (uploading, downloading, document generation).System.Image: A specialization ofFileDocumenttailored for image metadata, thumbnail generation, and image preview widgets.
Physical Database Architecture: Table-per-Type (TPT) Mapping
A critical Intermediate Developer exam concept is how Mendix translates domain model inheritance into relational database schemas. Relational databases do not have native concepts of object-oriented class inheritance. ORM frameworks typically choose between three strategies: Single Table Inheritance, Table-per-Concrete-Class, or Table-per-Type.
The Mendix Database Mapping Rule: Mendix uses Table-per-Type (TPT) inheritance for all persistable entity generalizations.
Under the TPT strategy:
- A distinct physical table is generated for the generalization entity (e.g.,
crm$person). - A separate physical table is generated for each specialized entity (e.g.,
crm$employee,crm$customer). - The specialization table stores only its own unique attributes, not the inherited attributes.
- The primary key of the specialization table is identical to the primary key of the generalization table, acting simultaneously as a primary key and a foreign key constraint.
Read and Write Mechanics Under Table-per-Type
-- Physical representation of Person (Generalization)
CREATE TABLE crm$person (
id BIGINT PRIMARY KEY,
firstname VARCHAR(100),
lastname VARCHAR(100),
email VARCHAR(200)
);
-- Physical representation of Employee (Specialization)
CREATE TABLE crm$employee (
id BIGINT PRIMARY KEY REFERENCES crm$person(id) ON DELETE CASCADE,
employeenumber VARCHAR(50),
hiredate TIMESTAMP,
salary DECIMAL(18, 2)
);
When a microflow creates and commits a new Doctor record (which specializes Employee, which specializes Person), the Mendix Runtime must execute three separate SQL INSERT statements across three physical tables in a single database transaction:
INSERT INTO crm$person ...INSERT INTO crm$employee ...INSERT INTO crm$doctor ...
Similarly, when retrieving a list of Doctor objects from the database, the runtime executes an automatic multi-table SQL INNER JOIN:
SELECT p.firstname, p.lastname, e.employeenumber, d.medicalspecialty
FROM crm$doctor d
INNER JOIN crm$employee e ON d.id = e.id
INNER JOIN crm$person p ON e.id = p.id
WHERE d.medicalspecialty = 'Cardiology';
The Performance Penalty of Deep Inheritance Hierarchies
Because of the Table-per-Type architecture, deep or wide inheritance hierarchies introduce severe performance degradation in high-volume enterprise systems:
1. Compounding SQL Join Overhead
Every level of inheritance adds another mandatory INNER JOIN to every Retrieve from database action. If your hierarchy is four levels deep, every simple query requires joining four physical tables, preventing the database query planner from utilizing single-table index scans.
2. The Polymorphic Query Join Storm
If you query the base entity polymorphically (for example, retrieving all Person records to display in a directory), and Person has six different specializations (Student, Teacher, Staff, Alumnus, Vendor, Applicant), Mendix must discover which concrete subtype each row represents. To do this, the runtime must execute LEFT OUTER JOINs across all six specialization tables simultaneously:
SELECT p.*, s.*, t.*, st.*, a.*, v.*, ap.*
FROM crm$person p
LEFT OUTER JOIN crm$student s ON p.id = s.id
LEFT OUTER JOIN crm$teacher t ON p.id = t.id
LEFT OUTER JOIN crm$staff st ON p.id = st.id
LEFT OUTER JOIN crm$alumnus a ON p.id = a.id
LEFT OUTER JOIN crm$vendor v ON p.id = v.id
LEFT OUTER JOIN crm$applicant ap ON p.id = ap.id;
As table row counts grow into millions of records, this polymorphic join query causes devastating database CPU spikes, full table scans, and out-of-memory errors.
Exam Best Practice: Restrict entity inheritance to a maximum of 1 to 2 levels. Never use generalization merely to share two or three generic attributes if the entities do not have a true, immutable is-a relationship.
Polymorphism in Logic: The Microflow Inheritance Split
One of the primary benefits of generalization is polymorphism—the ability to write generic logic that accepts a base entity parameter, while still allowing specialized execution when necessary.
To handle subtype-specific behavior without writing brittle conditional XPath statements, Mendix provides the Inheritance Split microflow activity. When you drop an Inheritance Split onto a microflow canvas and select an entity parameter that has specializations:
- The activity automatically generates distinct outgoing execution branches for each concrete specialization in the domain model.
- It provides an additional branch for the base generalization itself (when an instance is purely the base type).
- It provides an empty/null branch if the incoming parameter is uninstantiated.
Inside each specialized branch, the Mendix Runtime automatically downcasts the object, granting instant access to the subtype's specific attributes and associations without requiring any explicit typecast activity.
Generalization vs. 1-to-1 Association: The Architectural Decision Matrix
A classic scenario on the Intermediate Developer exam asks whether to model a relationship using Generalization or a 1-to-1 Association. Choosing incorrectly can cripple an application's long-term extensibility.
Architectural Decision Matrix
| Criteria | Generalization (Inheritance) | 1-to-1 Association (Composition) |
|---|---|---|
| Conceptual Relationship | Strict IS-A relationship (Car is a Vehicle) | HAS-A or Role relationship (Person has a CustomerProfile) |
| Runtime Type Mutation | IMPOSSIBLE. Object type is immutable once created. | EASY. Associate or unassociate roles dynamically. |
| Multiple Active Roles | IMPOSSIBLE. An object cannot be both Employee and Customer. | EASY. A Person can link to both EmployeeRole and CustomerRole. |
| Database Performance | Mandatory JOIN on every query; overhead on polymorphic reads. | Explicit query only when association is traversed; isolated tables. |
| Entity Access Rules | Access rules on base entity apply to all sub-entities. | Independent security access rules per associated entity. |
| Platform Features | Required for FileDocument, Image, or System.User extension. | Standard relational data modeling. |
The Mutable Role Problem: Why Composition Beats Inheritance
Consider an enterprise application tracking individuals in a university ecosystem. An individual enters the university as a Student. Three years later, they become a student employee (TeachingAssistant). After graduation, they become an Alumnus, and five years later, they return as a full-time Faculty member.
If you model this hierarchy using Generalization (Student, Alumnus, and Faculty specializing Person):
- When the student graduates, you cannot change the
Studentobject into anAlumnusobject. In Mendix, an instantiated object's entity type is permanently fixed. - The only way to transition the person is to delete the
Studentrecord and create a newAlumnusrecord. - Deleting the
Studentrecord destroys foreign key audit trails, course histories, historical grades, and created timestamps.
The Solution: Use Composition via 1-to-1 or 1-to-Many associations. Model a core persistable entity called Person, and create separate entities for StudentProfile, AlumniProfile, and EmployeeProfile. Link them to Person via 1-to-1 associations. The individual's primary identity (Person) remains permanent, while their roles are added, enabled, or archived cleanly over time.
How does the Mendix Runtime physically store entity generalization hierarchies in the underlying relational database (e.g., PostgreSQL or SQL Server)?
A developer needs to execute specialized validation logic based on the concrete subtype of an incoming Notification generalization object (EmailNotification, SMSNotification, or PushNotification) inside a microflow. Which microflow activity is specifically designed for this purpose?
An architect is designing an enterprise HR application. An individual can start as an external Contractor, later be hired as a full-time Employee, and eventually transition to an Alumnus. Why should the architect choose a 1-to-1 association from a core Person entity to these role entities rather than using Person as a generalization with specialized sub-entities?