Servoy Tutorial: Event-Driven Architecture

Servoy Tutorial Photo Credit: Fast-Auto.fr via Compfight

This Servoy Tutorial is a discussion on how to build an application using an event-driven architecture, employing a loosely coupled modular design. This Servoy tutorial is an extension of the prior tutorial on Encapsulation. You should read that tutorial first before attempting to understand why we would use an approach like this. You also might want to familiarize yourself with object-oriented programming in Servoy, a topic for which I have written numerous Servoy tutorials.

Let’s take a real-world scenario, and assume for a moment we are building a dashboard. The dashboard can contain an unspecified number of widgets, each showing different information. One might be a chart, another a table view showing sales data, another showing order summary information, etc. The user has the ability to add and remove widgets, so you don’t know which ones will be there on the dashboard at any given time. On the dashboard, the user can set filter criteria, like a date range, sales territory, sales rep, etc. When the user makes a filter change, you would not want to have the entire dashboard rebuild with the onDataChange() event, where each widget looks to globals (or form variables) that were set in order to determine what information they need to display. Nor would you want to fire off forms.refresh() methods on all your widgets with the onDataChange() event. A loosely coupled, well architected system, would “broadcast” that a date filter change just happened, letting any of the widgets that are interested in that message, and “listening” for it, receive the message and respond. Other widgets that are not affected by a date filter change, would not be “listening” for that event, and hence would not respond. Does that make sense?

You might be wondering how we can do event-driven programming in Servoy, since it has no native EventHandler api, like many other development tools (not yet, anyways). Well, we can build our own. Let’s give it a try, shall we?

The first step is to create a table where we will store all our registered events. This will allow us to maintain the events from session to session. We can also build a nice UI for managing the events if we want, but that is beyond the scope of this Servoy tutorial. Here is a simple event table.

encap_1

The next step is to create some global relations, so we can quickly locate a foundset of records for an event by name (you do recall from the Servoy Tutorial: Optimizing Code Performance that a relation is the fastest way to get the records from the table, right?). I’m actually going to need three variables to do this:

And two relationships:

encap_4

This first relationship allows me to quickly check to see if an event registration already exists, by event name, the callback method, and the module attempting to register the event.

encap_3

The second relationship allows me to quickly pull a foundset of all events that are registered for the event that has been fired.

Now that we have our table and some relations in place, we can create a new object in scopes.events, that will intercept all fired events from anywhere in the application. We are going to keep the example simple here, so this event listener has only a minimum of methods, register, remove and post.

The register method will set the globals with the module, event name, and callback method, and then check to see if it is already registered in the event table. If not, then it adds the event to the table. It always returns the event record in case the caller registering the event needs some information like its ID.

The remove method, removes the event from the event table, if it exists. It returns true or false to the caller.

The post method does all the work when an event is fired. It first pulls from the event table a foundset of records containing all the registered callbacks for that event name. In the example of our dashboard widgets, these could be all widgets that have registered to be notified if the date filter has changed. It loops through all the registered callbacks, splits them into an array, and then checks to see if they call a “forms” method or a “scopes” method. You could also run the string as a function using “eval” if you are comfortable with that (I commented it out), in which case you would not need to do the workaround to determine the type of method to call.  The method being called is a single public eventHandler method on the form, and it is passed arguments that allow the eventHandler to route the call to private form methods (good place to check all parameters are valid). The post method also traps for any errors, allowing for some centralized error handling. By the way, you could fire error events from anywhere in your system and have them handled this way as well, by one central eventListener.

Now lets turn our attention to the imaginary widget that is going to go on the dashboard. The widget will be interested in several events that could fire, like a date filter change, sales rep change, etc. In this case, we are simply going to listen for generic events called EVENT_1 to EVENT_4, but in the real-world you would call the events something like “DBRD_DATE_FILTER_CHANGE”, etc. In the widget’s form .js file, I setup an object which will be used to route specific events to private functions (preserving encapsulation).

When a widget is added to the dashboard, it will initialize, and register for the events it needs to respond to. The eventListener which receives the events, will then know to notify this widget when one of its registered events is fired. Notice that in the example below, I am registering a second callback for EVENT_1 in a completely different part of the system other than my form callbacks. When EVENT_1 is fired, two methods will be called.

While we are at it, here is how the widget would un-register itself, if it where removed from the dashboard by the user.

I created the following eventHandler for the widget in the .js file, which relies on the object map shown earlier.

And finally, here is how you would fire an event, from anywhere in the system. The fired event will be received by the eventListener in scopes.events, and all the registered callback methods for that event will be processed. You can pass as many arguments as you want in an array, which I used in this example, but obviously you could pass an object(s) as well, which would give you even more flexibility.

I created four methods that fire the different events, and hooked them up to buttons on a form, along with a Register Events and Remove Events buttons.

Here is one of the simple callback methods I created. I created four of these, one for each of the events that will get fired. These methods are only accessed by the eventHandler which routes the call through the object.

Here is my simple form for testing the event-driven model.

encap_2

First I press the Register Events button, so the callbacks are registered for the events. Then I press “Fire Event 1” button, and I see the following in the console:

The first output is from the method I created on the form, and the second is the additional event I registered for the same event, so that I could demonstrate multiple callbacks firing off the same event.

So there you have it; a simple event-driven system that preserves encapsulation. This approach is loosely coupled, easy to maintain and expand. In fact, if you think about it, our imaginary widgets could be used anywhere in the system, and still function exactly the same, because they are listening for the events that affect them, and ignoring everything else. Perhaps in the future, all Servoy applications will function like this.

That concludes this Servoy tutorial. It was an interesting exercise in using an event-driven architecture, demonstrating how to preserve encapsulation and use a loosely coupled modular design. I hope you enjoyed it, and I look forward to bringing you more Servoy tutorials in the future.

Please subscribe to receive notification when new articles are published, and share your comments below.

Gary Dotzlaw
Gary Dotzlaw has 20+ years of experience as a professional software developer and has worked with over 100 companies throughout the USA and Canada. Gary has extensive qualifications in all facets of the project life cycle, including initial feasibility analysis, conceptual design, technical design, development, implementation and deployment. Gary is an expert Servoy Developer, with extensive large-commercial project experience, and has written numerous advanced tutorials on Servoy development.