You Need to Know About Self-Referencing in Mendix

And how it falls short

Liam Hanninen
eXpSoftwareEngineering
7 min readDec 13, 2022

--

This article is going to throw some love on self-reference associations in Mendix, why you should be aware of them, and how they can break your heart. Self-reference associations (which I’ll call SRAs from now on) allow you to associate an entity to itself. This need is uncommon but in my experience every app ends up with at least one.

Will I ever use SRAs?

A family tree is a good use-case for SRAs because it leverages both of SRAs super powers: self-reference and unpredictable number of levels. I’ll touch on this more later. But there are many other instances where SRAs can be used: real estate MLS parent/child relationships, representing database tables, business hierarchy (2 levels or n number of levels), and any situation where objects could relate to other objects of the same type (entity).

Simple example

Imagine a family tree application with an entity called FamilyMember. To establish familial relationships you’ll want to somehow create a literal parent/child association. Mendix provides two avenues to do this: Inheritance/generalization and SRAs. Let’s very briefly explore the pros and cons of managing this via inheritance and then get into SRAs.

Inheritance (generalization) and why it’s not the right fit

I created a base FamilyMember entity and then created a pair of entities that inherit from it. I’m not saying anything new when I say this is where inheritance shines: it reduces the redundant noise and effort when recycling attributes from a common entity. I suspect the size of the table behind-the-scenes in the database is unaffected (it needs to store those fields and its data somewhere). Nonetheless, inheritance fits nicely here… initially.

Consider “I am my father’s son and my father is his father’s son.” So I would be the child and my father would be my parent. So far so good. But how do we record my father’s relationship with his father? Well, he must then be duplicated as a child and associated to his father, a parent. A dedicated yet under-informed developer may make this use-case work. Unfortunately, it would devolve into a suite of potentially masterful, yet complex microflows to buttress this poor design. More time than necessary would be spent building, maintaining, and debugging a less scalable solution.

How (and When) SRAs Can Save the Day

1. [Required] When you want to associate an entity to itself

2. [Optional bonus] When you don’t know how many parent/children/levels there could be.

SRAs are not unique to Mendix. They are simply a data relationship management strategy or design. We could say Mendix, for its part, enables self-referencing and makes it easy to implement. The way you set up an SRA is the exact same way you create any association; although in this case, you’re selecting the same entity for the association.

It is depicted in the domain model as a circular association pointing to its source entity. This is the easy part. The true power and tribulations of SRAs surface when interacting with the data and designing UI.

Before we get to the hard and heart-breaking parts, there are other easy parts and general benefits to this we should go over.

What to do with your fancy, new SRA

You now have a family tree with infinite levels! You can now record your great, great, great, great grandfather. You don’t need to create a Parent object and a Child object for everyone. You just need to build a CRUD interface and manage the associations. Data management design is clean, simple, scalable, and, for our purposes, done.

Secondly, you CAN retrieve in either direction. Reverse retrieves are not unique to SRAs but are particularly useful for them. Please see https://docs.mendix.com/refguide/query-over/ if you’d like to retrieve in the reverse direction.

Finally, a family tree has indefinite levels. But many SRAs are just two levels — this will depend on your use-case. Two-level SRAs are easy to display in data grids or general page widgets. They also skirt ALL of the heartbreaking pitfalls I share later.

Some need-to-knows

Consider this the warning section. These are not showstoppers but just things to consider when implementing an SRA.

First of all, you need to be consistent when establishing the relationship — the direction is implicit. So when you use a Retrieve by Association action it will consistently retrieve in the same direction. This means it will only retrieve children or only parents depending on how your CRUD interface designates. So documentation and clear naming is important.

You don’t need flags for retrieves but may want them to manage subsets of parents or children. This almost doesn’t merit mentioning since it would apply to non-SRAs as well. It is much cleaner to use a retrieve over the proper direction when possible — although, it’s possible to establish and leverage flags with SRAs when more granular retrieves are necessary.

Lastly, there is a problem with retrieves. As I mentioned earlier, you can perform reverse retrieves if you’ve got a parent, for example, and want to retrieve their children AND/OR visa versa. But you can only do this using from the database retrieve (not an association retrieve), which means you must create a microflow for widget data sources and visibility.

Heart breakers

Multi-level SRAs like this family tree example have serious trouble importing and exporting. Message Definitions for example will let you select any number of descendants BUT you must select a discrete amount. There is no option to toggle ‘Unknown Levels’ or ‘Self-Reference Association Figure it Out on the Fly’. So you have to pick a certain number of descendants that work for your situation. We selected 6 and tell our users that descendants beyond the 6th level won’t be imported or exported. This pitfall impacts JSON mapping in the same way.

The unknown number of levels also creates a problem in microflows. You’ll likely want to ascend from any arbitrary descendant all the way up to the first ‘parent’ or top level. To go up one level you can do a single retrieve action to get an object’s parent. You can do two retrieves if there’s two levels. But if you don’t know the number of levels then you can’t build a microflow around a certain number of retrieves. You have two options to solve this: build a reusable subflow that has a recursive loop or establish a relationship directly to the top level. In the recursive loop you simply check if there is a parent and if so you do another iteration. This might not be considered a showstopper, but I deem it a heart breaker, because you need to install this subflow any time you want to do a simple retrieve to the top level. Alternately you can create an association to the top level every time you create a new descendant. This allows you to do a single, simple retrieve but requires maintenance on the CRUD interface (and another self-referencing association).

And finally, visualizing this data is not great. If you want your users to see and explore all of the levels in one place you’ll need an app-store module. There is nothing native that will show your users relationship depth in one view. Data grids, for example, are not suited for showing an unpredictable number of descendants on the same row. The closest you get to native GUI features playing nicely with SRAs is pathing pages to the same page with different data. Users can keep descending by clicking ‘Next’ or ‘Previous’, recycling the page, but passing the next or previous descendant. This enables the users to traverse between objects easily, but still short of depicting the depth in a single view.

So there it is: SRAs can break your heart but don’t give up on that relationship. In this case it’s better to have loved and lost than never to have loved at all. SRAs can solve a unique set of problems better than anything else. Their heartbreaking pitfalls are not as impactful if you see them coming and can set expectations. They’re worth it when they fit the problem.

--

--