Extending static entities — Part 1
Static entities are my favourite construct in OutSystems.
In the one hand, static entities behave just like any normal entity. For example, you can create a foreign key relating a business entity with a Status static entity, and then join both together in an aggregate to get a list sorted by status.
On the other hand, they can be used directly in the code to build business logic. For example, you can reference Entities.Status.Approved directly in your code to check for approval status. With these strong references, you don’t need to stringly-type the status values, giving us full traceability of the constants and where they are used in the application.
But, in my opinion, their true power is in how they can be used to create easy-to-use and extensible components.
Because they are so powerful, there are lots of ways you can use static entities inside of components. We’ll warm up looking at a few well-known examples. Then we’ll deep dive, exploring some advanced patterns enabled by static entities.
Exposing a fixed lookup table
Some components, specially in geographic systems, use a pre-defined table with look up values. For example, a system that handles shipping to multiple countries will necessarily need a lookup table of country names.
This is exactly what the Geo component offers. It includes lookup tables for countries, states, currency, languages and timezones. You can use these tables as lookup tables, but you can also create foreign keys between business entities and Geo entities.
You might have noticed that the Country and CountryState tables are not static entities. Instead, they are exposed as normal entities, and populated by a bootstrap Excel file. Why, you may ask?
If they were both static entities, they would amount to thousands of records, and your IDE would… start… to feel… very sluuuuuggish! Not to mention creating and maintaining all those records, would be a tedious and error-prone job.
Remember that static entities, powerful as they might be, are not the best solution for all scenarios.
Static records for component options
Most UI components allow the user to change aspects of its behaviour through input parameters. If the options are not too many, then mapping the options into a static entity might be a good choice.
That’s the case with many of the components from the OutSystems UI library. Static entities are used everywhere to represent colours, sizes, positions, alert types, and many other options.
OutSystems UI uses static entities to guide the developer into a consistent pre-defined palette, standard spacing and sizes, etc.
Each component receives an input of the appropriate static entity type, and the IDE is smart enough to automatically suggest suitable values. So customising the appearance and behaviour of an OutSystems UI component requires just a few clicks, and no knowledge of CSS. True low code must be like that.
A number of other components use static entities in this same way. A few other notable cases are:
- OutSystems Maps, uses static entities for different map layers, zoom levels, styles, and many more options.
- Ultimate PDF, uses static entities to represent standard print paper sizes and margin sizes.
- ZXing Services, uses static entities to configure which barcode format to use.
Static entities from library modules
In contrast to what we saw with the Geo component, the OutSystems UI static entities are not really meant to be persisted. You wouldn’t want to persist the size and colour of your containers on the database, would you? You would just define that directly on your code, so it’s easy to manage, easy to debug, and easy to maintain.
The static entities from OutSystems UI are much more similar to the enumerate types that are available in .NET or Java, and are optimised to be used at runtime.
In fact, the static entities from OutSystems UI are not mapped to database tables at all! If you attempt to reference one of these static entities through a foreign key, you will receive an error. Every foreign key must reference a physical database table, but since no tables exist, you get an error. Keep that in mind, it will be important later.
These static entities are more restrictive because they belong to a library module, OutSystems UI. A library module is enforced to have no persistence, to ensure the operations exposed by such a module are stateless and efficient. So no database tables are created at all for a library module, which makes them considerably faster to deploy, more portable across stacks, and immune to referential integrity issues.
Extending static entities
Let’s analyse OutSystems UI in more detail. We have seen that there’s a static entity with a colour palette, so that a developer can use colours without writing a single line of CSS. In total, the palette offers 26 colours.
We know that humans can perceive a lot more colours than that. In fact, we can perceive about 1 million distinct colours. Having a static entity with 1 million records would be… interesting… but someone smart observed that we only typically use a handful of colours in an application, and decided 26 colours is plenty.
But, whenever such a decision is made, it must be questioned, so it’s my duty to ask: “what if I need more than 26 colours?”
Let me tell you that you can totally define your own custom colours! How many you want. And they work seamlessly with every OutSystems UI component!
Except, you can’t do that right now. Well, kind of… it’s complicated. But grab some tea/coffee and stay with me, this is about to get more technical.
OutSystems is a procedural language, with very few concepts taken from object-oriented paradigm. In particular, OutSystems has no inheritance features in its language, which coincidentally allows it to completely circumvent the object-relational impedance mismatch. Sometimes challenging the status quo pays off. I digress…
No inheritance features, that is, except for one. Get ready to explore it.
Each entity defines an Identifier type. For example, the User entity defines a User Identifier type. The Color static entity from OutSystems UI also defines a Color Identifier type. And the Tag component from OutSystems UI defines a Color input parameter of type Color Identifier.
It is this type information that the IDE uses to make suggestions of values on that input parameter. The IDE knows that every record of the Color static entity is a valid value for the input parameter, and so builds a list of suggestions based on Color records.
So, how can we add new custom Color values, and instruct the IDE that they are also valid values for the input parameter?
By creating another static entity, say MyCustomColor, that shares the same type information with Color. This is accomplished by setting the data type of the identifier to be Color Identifier, as can be seen below. Then any record references from MyCustomColor are considered compatible with Color, and will be treated interchangeably. The type MyCustomColor Identifier is a subtype of Color Identifier, however for convenience we say that MyCustomColor is a subtype of Color, even if the entities themselves could have different attributes.
And now we see the problem with this solution. By changing the data type to Color Identifier, we have created a foreign key. And do you remember what happens to foreign keys referencing Color? Yes, you get an error…
This would have totally worked if only OutSystems UI were not a library module! Even though you cannot publish, you can see subtyping works at design time, but make sure you define the following CSS declarations in your theme to support this new RebeccaPurple record in OutSystems UI widgets:
* Defines what Entities.Color.RebeccaPurple looks like
* when applied to a background such as in a Tag or
* CardBackground component.
In my opinion, OutSystems should always allow to create a foreign key referencing a static entity, even if defined in a library module, as long as the foreign key has Ignore delete rule. But unfortunately, I’m not in charge of the product, so all I can do is try to influence it by writing this article. You can join the effort by liking this idea in the community.
The case with Ultimate PDF is different. Since it is not a library module, you can subtype its static entities all you want, and use that to create custom paper sizes and margin sizes.
Constructible static records
We’ve been inspired by the problem of creating more colours or more paper sizes than the component author envisioned, of course without changing the component itself. The solution that relies on subtyping seems to work, but only in components that are not library modules.
There is an alternative solution that doesn’t rely on subtyping, but you must follow two simple rules:
- The identifier of the static entity must not be an autonumber, and I would recommend to use a Text data type.
- The static entity should never be queried from the database, nor have its Get action invoked.
Let’s look back at the Color static entity. It has a Text identifier, so the first rule is respected. If we inspect OutSystems UI, we can verify that the GetColor action is never used, so the second rule checks out. So, we can conclude that the Color static entity is compatible with the constructible pattern.
Now let’s look at the PaperSize static entity from Ultimate PDF. This static entity defines the width and height of a few standard paper sizes. We can open the module and check that it has a Text identifier, and it is never queried — which tells us PaperSize is also constructible.
Take a look at the identifier values of PaperSize, stored in the Size attribute. Notice how the values are defined? For example, the value “21.00x29.70” has the width and height values encoded in it. Strange, no?
If you think about it, there’s no way Ultimate PDF can retrieve the values of the Width and Height attributes, without violating the second rule of constructible records. Upon inspection, you will find that Ultimate PDF extracts the width and height information from the identifier value, not from the attributes. Indeed, you could delete all attributes but the identifier, and Ultimate PDF would still be working.
So, how can this possibly be useful for creating custom colours or paper sizes?
Relax, we’re almost at the solution! We just need to create a constructor function, that takes as input the information about the custom record, and builds an identifier value containing that information. Note that the returned identifier doesn’t need to exist in the original static entity, but since the application doesn’t query the static entity, it won’t make any difference!
You can find such a constructor function in Ultimate PDF — it’s called CustomPaperSize. You invoke it with the width and height values of the paper size, and it constructs a new PaperSize Identifier value containing that information. For example, “29.70x42.00” would be the result from the image below.
What about OutSystems UI? It doesn’t define a constructor function, but you could define one yourself. For example, create a CustomColor function that takes in a MyCustomColorId and returns a ColorId like the following:
Notice that you won’t get a good design time preview when using this constructor function, because during preview the IDE does not evaluate the function. If you need a high-fidelity design time preview, you can just literally type in the text value “rebeccapurple” and it will work. But at this point you’ve returned to stringly typing and became the very thing you set out to destroy.
We have seen a few different ways that static entities can be used in your OutSystems projects.
A lot of emphasis was given on how to create static entities that are extensible. When designing a component, we would like to avoid specifying exhaustively all of the values upfront, but still allow users of the component to specify new values for a static entity.
We analysed two concrete use cases for this: to augment the Color static entity from OutSystems UI, to be able to define more colors than the 26 originally authored; and to create custom PaperSize values for Ultimate PDF, in order to generate PDFs with a paper size that is not offered by the component.
We explored two techniques to make extensible static entities: through the use of subtyping, and by creating constructor functions.
There is one more pattern to go, before we become master of the universe (of static entities at least). That is coming up on Part 2 — we’ll see how a component can become aware of all custom values defined by its consumers.