eVaf overview

Introduction

My background is from industrial automation where the same application was used by several different customers who usually were also competing with each others. It was quite a challenge to develop an application that could be universal enough to satisfy all their needs and at the same time be unique for each customer.

Every customer had their own specific requirements starting with different communication methods with their servers and ending with changes to the user interface. One option was making these additional functions and features configurable to turn them off for installations where they were not needed. The problem was that with every new configuration parameter the total number of combinations increased and required more and more testing. The final configuration was done by service technicians on the field and they were extremely good at turning on options that were not designed to work together.

The solution was to make the software modular, where all the different features and functions were implemented in separate modules. The final application included only modules required for the particular customer and/or installation without any need or possibility to configure it on the field. eVaf is my attempt to take this idea to a new level and make a generic application development framework to help rapid development of modular applications. It is not for making fancy user interfaces; it is for making complex modular applications.

Main executable

Usually the application's main executable implements most of the application's logic. External modules or plugins are used to extend the main application.

This is not so in eVaf applications. The eVaf main executable is an empty container that needs to be filled with external modules to provide the required functionality. The eVaf main GUI executable, if run without external modules, shows just an empty window that can be closed to terminate the application. The eVaf main CLI executable runs until terminated with CTRL+C.

Modules

eVaf modules are loadable libraries (.so or .dll files) that implement the features and functions of the application. By combining together different modules, an unique application can be made in very little time. Every module implements a specific function or feature and only when put together, the actual application is created.

Modules should should implement as little features as possible, but do it as good as possible. While it is possible create one 'monster' module that implements all the required features, it is not the spirit of the eVaf and not why this framework was made. eVaf modules should be small, with well specified input and outputs. Only then can the same module be reused in many different applications.

For example, an application that downloads pictures from a digital camera and uploads them to Facebook, could have at least 3 modules:

By replacing the camera module with another one or making it possible to select the camera module, we make sure that the same application can work with different cameras. Or use the same camera module in a different application that stores pictures from digital cameras into local database.

The picture resizing module can be replaced with another image processing module when we need it. The same picture resizing module can be used in other applications where images need to be resized.

The Facebook upload module can be reused in an application that uploads pictures from the local computer or the module can be replaced with another one uploading pictures to ImageShack.

Interfaces

Interfaces are the way how the functionality of eVaf modules is used. Every feature implemented by a module has an interface used to feed the module with data or request information from the module.

Interfaces should be kept even more specific and simple. It is perfectly valid for a module to implement more than one interface if these interfaces have different purposes. For example, the digital camera module could implement two interfaces – one for downloading pictures and another to control the connection to the camera.

All the interfaces are identified by a unique name and published in a global interface registry. Modules that implement interfaces, register their interfaces in the global registry; modules that need to use interfaces published by other modules, query them from the same global registry. Modules can even change their internal logic if an optional interface is found in the global registry.

Interfaces can also be reimplemented. The global registry of interfaces keeps only the last registered interface with the same name making it possible to create interface chains where basic implementation of the interface is done in one module and extended functionality added by another module. Any module querying for the interface would get the latest implementation without knowing that some request are handled by two or more modules.

Events

Events are used by modules to send out information that they have collected or processed. While interfaces are the way how to feed modules with data or requests, then events are mostly for spontaneous data. For example, the digital camera module could send out an event when a new picture is downloaded from the camera.

Similar to interfaces, every event type is identified by a unique name and needs to be registered before use. Registering is usually done by the module from where the event will be originating. Other modules interested in the specific event query for the event and then subscribe to it. When the event is sent out or broadcast, the eVaf event queue forwards the event to every individual subscriber.

Multiple modules can subscribe to the same event and they all would receive it unless one of the modules decides to consume it. Consuming the event means that no other module receives it. For example, there could be an extra module that receives camera events with pictures before the module that uploads pictures to Facebook. This extra module shows a list of available pictures with an option to choose which ones should be uploaded. By consuming the event the module makes sure that the Facebook upload module does not upload any pictures until the user has made their selection.

Reference-counted data

Events broadcast by modules can have data objects attached to them. To avoid unnecessary copying of data, these data objects are shared and reference-counted. When the data object is attached to the event, its internal reference counter is increased by one. When the eVaf event queue has delivered the event to all the subscribers, it destroys the event and decreases the reference-counter of the data object by one. If no other module kept the data object, then the data object's reference counter becomes zero and it is destroyed too.

This way modules don't have to worry about the life-time of data objects that they send out in events. They can send and forget the data. If some other module needs to keep the data, it is their responsibility to increase the reference counter and decrease again when the data is no more needed.

Effects on development

In our small group of software developers everybody worked on their own projects and applications. Usually nobody wanted to touch somebody else's code, which meant that whole applications were developed and supported mainly by one developer only.

When we introduced a similar modular application development framework, we all started working on the same applications without any changes to the organization or to the way how we worked. Everybody was still working on their own modules and even when there was a need to modify somebody else's code, keeping modules small and simple made it much easier to dig into that code.

In our case the modular architecture of the application made it easier to develop and maintain applications even when it took a little more time to split the application into functional blocks. Well designed generic interfaces and modules made it much quicker to develop new applications and the initial investment was well worth it.