Implementing CRUD Wrappers — CRUD-ifying Apps!

Justin James
5 min readJun 21, 2020

This is it… what should be the final article to wrap up the CRUD Wrapper Saga! In this article we are going to take a look at how we take the wonderful CRUD wrappers that have been written and use them in your application. I promise this will not be hard! After all, we didn’t go through all of this work only to have a bad experience living with the CRUD wrappers. So let’s get them used in our application.

I was actually looking for a 3.5" floppy to put in this image when I discovered I owned a VHS tape! That’s got the ultrasound of one of my kids on it. Also, time to clean my cooling vents on the PC, yuck!

One of the tricks with the Remove and Upsert Actions we wrote was to violate the parameter naming convention that feels natural (such as naming the Id used in XYZ_Remove “Id” instead of “XYZId”) in favor of a convention that makes them a drop-in replacement for the Entity Actions. This makes the workflow a breeze.

Ideally, you would make Screen Templates in OutSystems using your CRUD wrappers to do things easily. But I know that not every team uses them, and I have a strong habit of using scaffolding for everything. Old dogs, new tricks, yeah yeah yeah.

To quickly and easily build a Screen (including a Screen Template) using scaffolding and the CRUD wrappers, follow this sequence (this works for Reactive and Traditional Web):

  1. In the Producer Module of the Entity/CRUD Wrappers, set the Entity’s “Read Only” property to “No” and publish.
  2. In the Consumer Module, make a reference to the Entity and the CRUD Wrappers, including the GetCanRemove.
  3. Use scaffolding to build the List/Detail screen pairs in the Consumer Module.
  4. In the Consumer Module, use “Find Usages” on the Create, CreateOrUpdate, Update, and Remove Entity Actions of the Entity, and use “Replace Usages” to replace with Upsert and Remove.
  5. After the use of Upsert and Remove, put an If checking the IsSuccess property of the EntityActionResult and throwing an Exception with its CombinedEntityMessageText as the Exception’s Message.
  6. For the “success” path, for the Message (Reactive) or FeedbackMessage (Traditional Web) use the CombinedEntityMessageText as the message text, and in Traditional Web, use the CombinedEntityActionMessageTypeId for the MessageTypeId of Feedback Message. In Reactive, you might want to use that Id in a Switch drive the “Message” to use the correct “Type” property; unfortunately Reactive does not allow this to be an Expression.
  7. On the Detail Screen, find the “Delete” button, and put it inside an If Widget. The condition of the If Widget should be XYZ_GetCanRemove(XYZId).EntityActionResult.IsSuccess. In Reactive you will need a “Fetch Data From Other Sources” to get this value. This will hide the button if the record cannot be removed. Alternatively you could disable it and use the CombinedEntityMessageText to use as a tooltip to explain why it is disabled, or perhaps the “False” branch of the If could use that text to explain why it can’t be clicked.
  8. In the Producer Module, set “Expose Read Only” back to “Yes” and publish. In the Consumer Module, refresh references and publish. You should not have any errors at this point.

If you are updated an existing application to use CRUD Wrappers, the workflow is exactly the same, except you skip step #3.

Typical “Save” Action using the Upsert pattern

And… well… that’s it! One of the goals of the CRUD Wrappers is specifically to not make life significantly more difficult or complicated for the people building the screens. We have masked all of the complexity (and really, there is not much anyways) in the CRUD Wrappers, and in exchange the people working in the front end agree to use the CRUD Wrappers and always check the IsSuccess flag in the output and the end result is a well-architected, highly modularized design that can rapidly and easily be transformed to fit your needs, whether that be building REST services, shared among Mobile, Reactive, and Traditional Web, set up for BDD or unit testing, load testing, or anything else you can come up with.

As a word of warning: many developers fail to build the habit of checking “IsSuccess”. This is wrong and this is dangerous. Always check the “IsSuccess” to verify that the action functioned as expected. Failure to do so will result in situations were a data change was rolled back, but your code continued as if it wasn’t. You do not necessarily need to re-throw that as an Exception, especially in bulk actions like data import logic. But you do need to determine what to do when an action has failed.

You may decide that you want your CRUD wrappers to throw Exceptions instead of returning an IsSuccess. I will not argue against that and I often consider it myself. Let’s look at the pros and cons:

Pros:

  • IsSuccess enables a few specialized scenarios like bulk importing data.
  • IsSuccess provides consistency between the GetCanRemove (and any additional validators you may choose to implement) and the Remove and Upsert CRUD wrappers.
  • IsSuccess allows highly granular output in terms of exception messaging.
  • OutSystems does not allow Exceptions to be made public, forcing you to lose the granularity in exception handling that other languages would allow.
  • Developers have traditionally known to check the output of functions for NULL output and other similar situations; checking IsSuccess is no different.

Cons:

  • Most of the OutSystems world works on throwing Exceptions. In the handful of places where developers should be checking output before moving forwards (for example, using the GetXYZ Entity Actions when you got the Id from somewhere else), we tend to see high rates of errors from less-experienced developers.
  • Checking IsSuccess requires extra code.
  • Doing an automatic replacement of the scaffolding involves a spot of extra work.
  • The scenarios that IsSuccess dramatically improves are edge cases. IsSuccess may be viewed as a form of gold plating.
  • With the introduction of Reactive in P11 and the mobile apps introduced in P10, the Message action does not allow us to pass in a static entity Id to set the message type at run time like FeedbackMessage did, instead that is set at design time. As a result, some of the more interesting and useful functionality of this pattern (being able to set “IsSuccess” to True while giving an resulting type of Info or Warning) is deprecated with no obvious replacement.

So the summary here is that while I still prefer to use IsSuccess, and continue to use and support it within my Application Framework, this is a discussion that I do not considered “case closed” by any means, and if IsSuccess does not work for you in your environment, do not use it!

And that is it for CRUD Wrappers! Thank you for sticking with me through this very long journey.

My next article will be on Mobile Sync Best Practices, so hit that “Follow” button!

J.Ja

--

--