DEVELOPERS BLOG

A Flexible Architecture for Enterprise Mobile Applications, Part 2

In part one of this series, I introduced the Enterprise Vacation Requests application that I’m working on with John Murray and presented its architecture at a very high level using the diagram you can see here in the figure above. We’re currently developing a native version of the application using Cascades, but intend to develop the same application using HTML5. When we’ve done so, we’ll report back on our experience and what we managed to accomplish.

Flexible Architecture_1_3

Figure 1 – Architecture block diagram for the Enterprise Vacation Requests application

In the article of this series, I will review the remaining key architectural components of the native application, starting with Messages and Message Queuing.

Messages and Message Queuing

Flexible Architecture_2_3
Figure 2 –Internal Message Flows – GUI Initiated

To address the off-line working requirement outlined in part one, we decided our application should be “message-oriented.” Rather than making synchronous calls to functions or services to execute actions triggered by the GUI, we decided that the GUI would instead create special objects or messages and store them in a persistent queue for processing by a headless service. Having placed the message in the queue, the GUI could regard the request (or “operation”) to have been issued and continue on its way. Meanwhile, if there was a network available, the headless service could collect the operation from the queue and process it.

That’s the concept. In detail, we have two queues: one for outbound messages sent from the GUI to the headless service and one for inbound messages created by the headless service and directed to the GUI.  Each queue is implemented as a table in a SQLite database. Messages are either “requests” or “responses” and a request/response pair is termed an “operation.”

A request created by the GUI is placed in the outbound queue for collection by the headless service. The headless service places a corresponding response object in the inbound queue for the GUI to collect when the request has been processed. This “GUI initiated flow” is depicted in Figure 2a.

However, some requests emanate from an event in the back-end enterprise system. For example, a manager approving a vacation request will give rise to an “approval outcome request” object being created by the headless service and placed in the inbound queue for consumption by the GUI. The GUI would respond by placing an “approval outcome response” object in the outbound queue so that the headless service receives it as a kind of ACKnowledgement (sic). Figure 2b shows the “back-end initiated flow.”

Flexible Architecture_2_4

Figure 3 –Internal Message Flows – Back-end Initiated

In many ways, our approach resembles a communication protocol.

The Operations API

Flexible Architecture_2_5

Figure 4 – The Operations API

To isolate components from the implementation details of our message-oriented queuing mechanism, we created an API for submitting and receiving operations that both the GUI and headless service use. It consists of a singleton class with methods you can call (Operations.hpp in the image above) and a series of classes, each of which implements an operation request or response and extends a standard super-class that defines all operations in general. The super class is called OperationAl.hpp (“AL” for Annual Leave).

To make switching between an in-memory object representation of an operation and a persistent, SQLite representation, we made it a rule that all operations classes must implement marshall and unmarshall methods with which to translate between an object representation and a byte array representation, and we store the byte array representation in a BLOB field in our SQLite database.

virtual QByteArray marshall() = 0;
       virtual void unmarshall(QByteArray serialized_data) = 0;

Fortunately, Qt makes serialization (another term for marshalling/unmarshalling) really easy. Here’s an example from the OpSubmitBookingRequ class, which represents a request to submit a vacation request to a manager for approval:

QByteArray OpSubmitBookingRequ::marshall()
{
    QByteArray serialized_data;
    QDataStream stream(&serialized_data, QIODevice::WriteOnly);
    stream << localRequestId();
    stream << leaveYear();
    stream << fromDate();
    stream << toDate();
    stream << firstDayHalf();
    stream << lastDayHalf();
    stream << dayHalf();
    stream << note();
    return serialized_data;
}
void OpSubmitBookingRequ::unmarshall(QByteArray serialized_data)
{
    QDataStream stream(&serialized_data, QIODevice::ReadOnly);
    stream >> _local_request_id >> _leave_year >> _from_date >> _to_date >> _first_day_half >> _last_day_half >> _day_half >> _note;
}

Communication between GUI and Headless Service

How does the headless service know that there is a message in the outbound queue for it to process? How does the GUI know that the headless service has placed a message in the inbound queue for it to consume?

We could have addressed these technical requirements by each part polling the SQLite database tables by making periodic calls to the Operations API. But this would have been very inefficient in terms of battery life. So instead we leveraged one of our favourite features of BlackBerry 10: the invocation framework.

Flexible Architecture_2_6

Figure 4 – Using the invocation request framework for GUI:Headless Communication

When either the GUI or headless service places an object in one of the queues for its counterpart to process, it informs the other component by executing an InvokeRequest. On receiving that invocation, the component uses the Operations API to retrieve the operations objects that are waiting for processing from the relevant queue.

Using the invocation framework in this way is really easy. Here’s a code fragment that shows how to send a request from the headless service to the GUI:

static const char* TARGET_AL_GUI = "com.example.AnnualLeave";
static const char* TARGET_AL_ACTION_INBOUND_QUEUE = "com.example.annual_leave.INBOUND_QUEUE_DATA";
.........
    bb::system::InvokeRequest invoke_request;
    invoke_request.setTarget(TARGET_AL_GUI);
    invoke_request.setAction(TARGET_AL_ACTION_INBOUND_QUEUE);
    _invokeManager->invoke(invoke_request);

The BlackBerry Hub

The last key “ingredient” of our architecture is the BlackBerry Hub. It’s a fundamental feature of the Blackberry 10 user experience and a major boon in helping users manage the plethora of message and notification types that their applications generate. And guess what? Developers can exploit it too. Using a simple API, you can place a notification into the hub and link it to an invoke request that you want to be “fired” when the user selects the Open icon in the notification viewer.

Flexible Architecture_2_7 Flexible Architecture_2_8

Figure 5 – Screen shots of Vacation Request notifications in the BlackBerry Hub

Here’s how:

void Operations::notifyGuiInboundOperation()
{
    _notify->setTitle("Annual Leave");
    _notify->setBody(QString("An update for your annual leave requests has been received"));
    bb::system::InvokeRequest request;
    request.setTarget(TARGET_AL_GUI);
    request.setAction(TARGET_AL_ACTION_INBOUND_QUEUE);
    _notify->setInvokeRequest(request);
    bb::platform::Notification::clearEffectsForAll();
    bb::platform::Notification::deleteAllFromInbox();
    _notify->notify();
}

Over to you

We’re at the end of our review of the architecture of the native version of our Enterprise Vacation Requests application and I hope you’ve found this useful.

Do you think that an architecture like this might work for your application? Are you already employing ideas like these in your own enterprise mobile applications for BlackBerry 10?

Would you prefer to use HTML5 for your enterprise applications to maximize cross-platform reuse, even if you need to compromise on functionality or user experience? If your answer is “yes,” watch this space as we’ll cover that very topic in the coming months.

Additional Resources

About mdwrim