Extending static entities – Part 1

Static entities are my favourite construct in OutSystems.

On 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 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 by looking at a few well-known examples. Then we’ll deep dive, exploring some advanced patterns enabled by static entities.

Some of the static entity patterns we’ll be discussing in this article.

Exposing a fixed lookup table

Some components use a pre-defined table with lookup values, especially in geographic systems. 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.

Geo component from Forge

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 may you 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, mapping the options into a static entity might be good.

Static entities exposed by OutSystems UI

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 suggest suitable values automatically. 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.

Some suggested colour values for the Tag component, derived from the records of the Color static entity.

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 define that directly on your code, so it’s easy to manage, debug, and maintain.

The static entities from OutSystems UI are much more similar to the enumerate types available in .NET or Java and are optimised to be used at runtime.

Some static entities cannot be referenced in database contexts.

The static entities from OutSystems UI are not mapped to database tables! You will receive an error if you attempt to reference one of these static entities through a foreign key. 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 for a library module, making 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 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 many more colours than that, and 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 define your custom colours! How many do you want? And they work seamlessly with every OutSystems UI component!

A custom colour that was added to the OutSystems UI palette.

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.

Subtyping

OutSystems is a procedural language, with very few concepts taken from the 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.

The Color input parameter, of Color Identifier type.

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 of 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 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 of 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.

MyCustomColor is a subtype of Color.

And now we see the problem with this solution. We have created a foreign key by changing the data type to Color Identifier. And do you remember what happens to foreign keys referencing Color? Yes, you get an error…

This would have 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.
 */
.background-rebeccapurple {
    background-color: rebeccapurple;
}
				
			
A tag with the rebeccapurple custom colour in design-time.

In my opinion, OutSystems should always allow creating 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.

MyCustomPaperSize subtyping the PaperSize entity.

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:

  1. The identifier of the static entity must not be an autonumber, and I would recommend using a Text data type.
  2. 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.

PaperSize static entity from Ultimate PDF is also constructible.

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.00×29.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 work.

So, how can this possibly be useful for creating custom colours or paper sizes?

Relax, we’re almost at the solution! We need to create a constructor function that inputs 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 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.70×42.00” would result from the image below.

Custom PaperSize using a constructor function.

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:

Creating a constructor function for custom OutSystems UI colours.

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 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.

Conclusion

We have seen a few different ways that static entities can be used in your OutSystems projects.

A lot of emphases was given on how to create static entities that are extensible. When designing a component, we would like to avoid exhaustively specifying all of the values up front but still allow users to specify new values for a static entity.

We analysed two concrete use cases for this: to augment the Colour static entity from OutSystems UI, to be able to define more colours than the 26 authored initially; and to create custom PaperSize values for Ultimate PDF, 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 masters 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. 

Leonardo Fernandes
Head of Technical Delivery
Connect with Leonardo on LinkedIn

This article was originally published on Medium.

A selection from our recent work