Mendix Rapid Developer study materials
Mendix Rapid Developer study resources

Lift-servicing app: generalising Visit entities and sorting by a visit count

Mendix Rapid Developer · Put your knowledge to work in context.

All case studies

Lift-servicing app: generalising Visit entities and sorting by a visit count

FieldOps Support maintains a Mendix app for a lift-servicing company. The domain model contains Equipment (persistent), Technician (a specialisation of System.User) and Visit, which stores a service date, status, technician and equipment. The business now needs two kinds of visit: InspectionVisit, which carries a checklist result, and FollowUpVisit, which carries a root-cause text and a repeat counter. Both share date, status, technician and equipment. One developer proposes two separate new entities, each with its own associations to Equipment and Technician, plus a calculated attribute VisitCount on Equipment so the overview grid can sort by number of visits. In acceptance the database holds 12,000 visits and 400 equipment records. Two further symptoms: deleting a decommissioned Equipment record fails with an error about existing references, and the team cannot explain why. The association Visit_Equipment is owned by Visit.

1. Should the two new visit types be modelled as specialisations of a generalised Visit entity, or as two standalone entities with duplicate associations? Give the recommendation and the trade-offs.

Read the worked answer

Recommendation: generalise. Both types share the same four core attributes and both hang off Equipment and Technician, so a persistent Visit with specialisations InspectionVisit and FollowUpVisit keeps one definition of the shared data.

Reasoning:

  • Associations defined on Visit (Visit_Equipment, Visit_Technician) are inherited by both specialisations, so you cannot accidentally attach a follow-up to an entity type that lacks the link.
  • One overview can retrieve Visit and show every visit type in a single grid, and an XPath constraint such as [Status = 'Open'] applies to all subtypes. Two standalone entities would need two grids or a union, and every report would have to query twice.
  • Type-specific attributes stay on the specialisation, so the checklist result does not clutter FollowUpVisit.

Trade-offs to state:

  • An object cannot change type at runtime; a Visit created as InspectionVisit can never become a FollowUpVisit. If the business ever reclassifies a visit, you need a copy-and-delete microflow.
  • Entity access is configured per entity, not inherited from the generalisation. Both specialisations need their own access rules for every module role, and forgetting one silently blocks the page.
  • Retrieve on Visit returns all subtypes; retrieve on InspectionVisit returns only that type. Use the narrow retrieve when the page must not show follow-ups.

Two standalone entities are only better if the shared part is small or the two flows diverge heavily (different workflows, different security, different lifecycles). Here the shared 70 percent and the single reporting need favour inheritance.

2. The team wants VisitCount as a calculated attribute on Equipment so the overview grid can sort on it. What are the realistic options and which do you choose?

Read the worked answer

There is no calculated or computed attribute in the Mendix domain model. Attributes are stored columns; nothing recalculates them, and a grid sorts in the database, so the count must come from somewhere real. Three options:

  1. View entity (OQL). Define a read-only view entity that aggregates Visit grouped by Equipment and join the equipment list to it. Sorting and paging happen in the database, which is the only option that stays correct at scale. Costs: OQL skills, read-only, and it must be maintained when the model changes.
  2. Compute in a microflow into a non-persistent helper entity. Retrieve Equipment, count visits per record, return a list of non-persistent objects. Simple and safe, but the whole set is loaded into memory and the sort happens in memory, not in the database. With 400 equipment records that is perfectly acceptable; with 400,000 it is not.
  3. Denormalise: a persistent VisitCount attribute maintained by microflows on create/delete of a Visit. Fast and sortable in the database, but it drifts the moment someone imports data, deletes via a different path, or a transaction rolls back. It also needs a one-off data migration for the existing 12,000 visits.

For 400 equipment records on one overview page, option 2 is the cheapest correct answer and can be shipped today. If the page must be filterable and sortable server-side over a growing dataset, go to option 1 and accept the read-only limitation. Only choose option 3 with an explicit reconciliation job that recounts nightly and logs mismatches.

3. Deleting an Equipment record fails with a reference error even though the user has delete rights. Explain the cause and give options with consequences.

Read the worked answer

Cause: association ownership, not security. Visit_Equipment is owned by Visit, which means the foreign key lives in the Visit table. When you delete the Equipment record, Mendix would leave Visit rows pointing at a row that no longer exists. Rather than silently corrupt the reference, it refuses the delete with an error. Security is a red herring here; a user without delete rights gets a different message.

Options and consequences:

  • Delete through a microflow. Retrieve the associated visits with an XPath constraint on the association, delete them (or reassign them to another Equipment record), then delete the Equipment, all in one transaction. Correct and explicit, but irreversible: historical visits disappear, which may break audit and reporting.
  • Change the association owner to Equipment. Deleting Equipment then clears the foreign keys and leaves orphaned visits whose Equipment is empty. Referential integrity is satisfied, but the visits lose their context and any page showing visit.Equipment will render blanks. Only sane if visits are meaningless without their equipment.
  • Soft delete. Add an active/decommissioned status and filter it out in the overview's XPath. In a maintenance domain with inspections that must be retained for years, this is usually the right call, because regulators and customers expect visit history to survive decommissioning.

Whatever you choose, add a confirmation page that shows how many visits will be affected, and check that the module role has both the entity delete rights and the execute rights on the deleting microflow.