I’m currently working on a massive project for asset management in an offline UI5 application. Much of the framework and applications had already been created before I joined the project, and much more fixes and patches have been applied since.

This lead to some undesired behaviour in some applications in terms of performance. To fix those issues, I wanted to severely clean-up some applications. That’s where my story starts.

Refactoring

Refactoring existing applications is not something you do out of sheer pleasure. it’s because things are tits-up and you can’t wrap your head around the area-51 code anymore.

Typically I go about in a couple of simple steps, and I iterate over them until I’m happy with the result, and the application still does the same thing as before, only faster and better structured.

  • Clean up the app structure
  • move logic from controllers to assistance and model classes
  • de-anonymize functions (use named functions as much as possible, anonymous functions are a source of memory leaks)
  • Componentize (if you use the same view in two places, create a subcomponent)
  • comment and clean-up

This is something you can repeat over and over and over until your application is completely modularized and all dependencies have been decoupled.

Model and business logic

During step 2, after I had been crying tears of blood for 2 days, I finally had a class for workorders, with some methods defined upon them like save, createTimeConfirmation, check, updateStatus, calculateCost and whatnot. I won’t even go into detail on all the promises I decoupled. Basically the entire application is now a heap of promises which we chain together depending on the desired flow.

Anyway, I had this nice litte workorder class, and an assistance class with a static list of workorder classes. The result was that I could now execute these methods properly from my controller. Something like: Assist.getWorkorderFromCollection(id).createTimeConfirmation( {data} )
Where the id was actually retrieved from oEvent.getBindingContext().getPropert(“id”)

Not bad, right? Those who know a bit of WebDynpro will see what I did there.

The resulting time confirmation still had to be moved into the model manually, but hey, this was an improvement.

But then I started thinking. (which is a bad thing. when I start thinking, usually mayhem and chaos ensues as I rethink the entire universe. Not necessarily for the better)

Why do I still need to keep a separate collection of workorder instances, when I have a collection of workorder data-entities in my model? Can’t I just store workorder instances directly into the model?

Introducing the BusinessObjectModel

I already knew that you can inherit the base JSONModel and add magic to it, because we have a DBModel which queries indexedDb and moves the result into the redefined JSONModel. So I decided to take it up a notch. I further redefined the DBModel and changed it in such a way that you can pass a classname to the model, which you want to use as template for every resulting entry.

The BusinessObjectModel contains some nifty redefinitions and extensions to:

  • Create an entry
  • remove an entry
  • process all entries during setData (and setProperty) to convert the entry/entries in the desired model instance.
  • forward the getProperty and setProperty to the object instance
  • and some more fun

The actual BusinessObject classes are all redefined from the base BusinessObject class, which contains some smart mechanics to convert every key in the data object, into a property of itself, and expose them via getProperty and setProperty.

But not only that, it can also cope with the concept of timesliced data. (CR-model instead of CRUD)

I won’t go into details on the timesliced data. That’s for a different article altogether. Just remember it’s the most effective way to get rid of synchronisation conflicts, locking issues, change documents and whatnot.

Every specific businessObject then inherits from this base class, and extends it with it’s own methods and attributes.

But to what end?

Here’s why I introduced this layered approach. It allows me to access the businessObject directly from my model

BTW: I find the name “Model” in UI5 very confusing, because it’s not really a model, it’s a data-context, and the real model is in the backend implementation of the service. But since our application is offline, I need a frontend model as well.

So now I can do things like: oEvent.getBindingContext().getObject().createTimeConfirmation( {data} )

which automatically creates a new timeconfirmation instance in my workorder, and binds it in the model! Hurraaaay!!

As a result, I only have to define my businessobject once, and I can use it everywhere without the need to re-implement all sorts of logic.

I can get rid of the assistance class and

I can truly seggregate between the controller, data context and the business logic!

To report this post you need to login first.

4 Comments

You must be Logged on to comment or reply to a post.

  1. Wouter Lemaire

    Great post! I like the idea of extending the JSON model to use it as a DB model! I’ve also been working on a mobile app with offline capabilities. I created a generic database layer based on the metadata of the OData service. I’ll share more about it soon 🙂 It never though of extending the JSON model 🙂

    (0) 
    1. Tom Van Doorslaer Post author

      ah, that’s probably because you’ve always had the luxury of a SAP gateway system as backend.

      I was building something on a PHP server and was too lazy to create an oData service, so I just created a generic PHP file that returns a JSON representation of whatever table you query.

      But since I didn’t have an oData service, I was forced to work with the JSON Model anyway, and that’s when I figured that an extension with local DB capacity and businessObjects would make a lot of sense.

      (1) 

Leave a Reply