Hi there AppWorks fans,
Welcome to a new installment of AppWorks tips.
For this week, an interesting concept showing why you need to “think” upfront before building the fanciest entity models. With entity modeling you want to make a solution as configurable as possible with minimal hard-coded items. Your administrators will love you for it! We already explained the implementation of configurable feature toggles in this post with a project specific configuration entity. This post will show a fascinating concept on not breaking links and keep your data persistent with configurable ‘Dictionaries’ behind a list.
Let get right into it…
Why this post? Well, we discovered an unpleasant smell in our own “smart” decisioned implementation!? This validates we’re still human! Watch and learn from a common mistake…
Dive into your workspace and create yourself an entity with name ‘Case’. This case gets a
case_name of type ‘Text’, but also requires a case type! This case type must be configurable for the administrator of the platform. To make this possible, we create a second entity ‘Case type’ which will get two ‘Text’ type properties:
With these entities in place, we can create a
to_one_case_type relation from ‘Case’ to…dûh!…’Case Type’; On the ‘Create’ form of our ‘Case’, we can add a relation via a drop-down component…Just like this:
I also make sure to uncheck the ‘Create’ option on this dropdown; It’s not in the screenshot!
Publish both entities to runtime and make sure to create some ‘Case type’ instances. After that, create a ‘Case’ entity instance, select a ‘Case type’ from the dropdown, and open the ‘Case’ entity with a simple view like this:
Now open yourself a second browser tab and change the key value for the selected ‘Case type’ on your ‘Case’ instance! After this change, do a refresh on the other tab (where the ‘Case’ instance is still open)…WHAT!? You see a case-type change on the case instance!?!? With the analyst-guy/gal on your desk telling you: That’s not how it should behave in runtime!? You should save the case-type with the case instance! I hear you thinking: We require a
case_type property on the ‘Case’ entity with a static enumerated list? WRONG! Why? well, this is not manageable for your administrator in runtime!
Have a check on your own when you delete the ‘Case type’ instances selected on your case instance! Or selected on a thousand other cases in production! What do you think will happen? You ordinarily break the link of the relation, and the case-type value is gone from the list! 😭
So, how to solve this problem? Well, the simple solution would be making the
case_type_key read-only after creation (via the ‘Security’ BB on a role), and making sure you can never delete the ‘Case type’ entities related to another entity:
The better way would be the implementation of a dynamic enumerated list which is a combination of the previous section. For this solution we introduce that
case_type property on the ‘Case’ entity with a dynamic Enum type of field. The dynamic Enum will start a BPM and retrieves the configured instances of our ‘Case type’ entity! Oehhhh…That’s nice! Let’s see the benefits with some craftsmanship…
Great, back to our ‘Case’ entity where we first remove that
to_one_case_type relation and introduce a new dynamic-Enum-text property with
case_type. Make sure to set the cache timeout to a lower value (for development), and add a new bpm:
Give the new BPM a logic name like
bpm_get_case_types, save it in the
bpms folder of the project, and do a first publication of the BPM.
Now, move back to the ‘Case’ entity and update the
Create form with our new property. Save it all, publish the entity, and have a check in runtime:
The dropdown is there, but the list behind it is empty…What to expect here? Well, how about a list of key/value pairs based on the ‘Case type’ entity instances!? 😎 ??? But how? Don’t we need a service on the ‘Case type’ entity? Yes, my friend!…And with this magical service in place, we can call it from our short-lived (have double-check!) generated BPM and put the result in the output message (see two screenshots back!). Ahaaaa…It’s BPM-time!
Before we dive into the BPM, we first jump into the ‘Case type’ entity where we expose the ‘Web Service’ BB, and create a new find operation with name
Save it all, and publish it for our next step…
I also expose the ‘Read’ operation, but I don’t know (yet) if it’s required…I enable it because we can, but normally I recommend to only enable operations if you also use them in your solution.
BPM-time…YOLO!! WAIT…We forget one thing! Yes, we need a new service container to manage our exposed service operations! This is a service container of type ‘Application Server Connector’, and you know by now how to create one from the ‘System Resource Manager’; Comment me otherwise…
Now it’s BPM-time where we create something smart calling the
FindAllCaseTypes service, loop over the retrieved entries and push the results to the output message. We start with a BPM change like this:
The for-each is a ‘Group as’ option in the context menu of the activity! Also, make sure to connect the arrow-flows to the correct components.
The next step is to insert (from the activity context menu) our ‘Find’ webservice to the first activity in the BPM, and update the for-loop with a selected condition; Just like this:
For making a valid message mapping on the activity in the loop, we create a temporary ‘local’ data element. Jump to the ‘Message Map’ tab, select the green ‘Start’ construct and make sure to create a new element like this:
In quick steps:
- Right-click the ‘Process Specific Messages’ and ‘Create Element’
- Give it the name
local; it will resolve into
- Expand the ‘bpm:GetEnumerationOutputMessage’ and copy the XML structure of ‘bpm:EnumerationValue’
- Right-click the
bpm:localand paste the XML as element to get that result.
After this change, select the ‘Activity’ construct and make a mapping like this (from a consolidated view):
You understand what we’re doing here…correct? You see the power of key/value pairs of a configuration entity mapped to value/display-name pairs with an ‘isDefault’ flag to ‘false’ (for now!). We save this information into our ‘local’ element and finally add it as full element to the output message! Is this low-code or what? 😁
Great, time for a publication and a view into runtime:
…It’s a party!
Now what? Well, select a value from the list (
Process_2 in my case) and save the ‘Case’ entity instance.
FYI: We’ll find the key behind this value in the database (
p2is my case).
Move back to the ‘Case types’ overview and update (or delete!) the key for the selected type (I change my
p4 including the display value). When you get back to your ‘Case’ entity, you will get an interesting view like this:
You see the
p2 key is still in place (which is our persistent data!) on the entity; Exactly how we would like to see it. You now have three choices to make:
- Leave the value as is; Because we see the key-value (and not the display-value) we directly have an indication this type is not available anymore for new cases, but still is valid for the old-cases!
- Select another valid type for the case.
- Create a new instance of the ‘Case type’ with a matching key (
p2is my case); You can add a post-fix in the display-name, so you have a change indication on the type. Like this:
If you want to make it fancier, you can add a
case_type_is_default‘Boolean’ type of field and set the value in the BPM activity mapping! I leave this one with you for further exploration on your own…
Another interesting property would be
case_type_is_active! Imaging yourself what you can do with it! 🤔
That’s enough input for this post; I give it a “DONE”. We saw a simple workaround for making data on an entity persistent; We also made an insight on what will happen when you don’t think upfront about how you want to make your data persistent. Several techniques are possible, now it’s up to you to implement the best strategy. I leave it with this conclusion…Have a great weekend and till the next post, next week, on AppWorks Tips! 🎉