This documents describes loose-coupled integration based on event-driven synchronization, its implementation, the alternatives and how standards could improve interoperability between applications for the whole web application community.
Please take a look at the overview of Gallery 2 Embedding.
Event-based loose-coupled integration is a method to integrate or embed one web application in another, e.g. to integrate a photo album application in a content management system, or to merge a blog with a forum.
The two terms loose-coupled and event-based can be explained separately, but only the combination of the two provides a complete solution for integrating / merging two or more web applications.
Loose-coupled means that the two applications are unchanged and each application exposes an interface through which it should be accessed by other applications. The two applications do not depend on each other, but they can delegate responsibilities like login / authentication to another application by communicating through the interfaces.
Integrating application A into another application B is done by having some glue code on top / as a plugin of B which calls A to get the generated output (HTML) and then B puts the generated output in its own output and returns the whole output to the user.
User -- request --> Application B (e.g. a CMS) |____ B initializes etc. |____ B authenticates the | active user |____ If the request is for -- B calls --> Application A (e.g. a forum) | an integrated page of |____ A handles the request | of A in B and returns the output | <- A returns - | |____ B assembles the whole | page output and embeds | A's output in it |____ B returns the requested | page to the user User <-- output --- |
Application A relies on application B supplying enough information in its call to application A. Most importantely information about the authenticated user. For web applications, it's also important that B tells A how the URLs are supposed to look like that it should generate.
But its important to note that A and B just communicate with each other through a clearly defined interface or protocol and they don't access each others data structures or functions directly.
TODO: Say something about: interfaces, API, protocols, master-slave relationship
Since the applications are loose-coupled and don't share the same data, they need to notify each other of changes or generally events that could affect the other application. Such events include:
So each time a new user gets created in one application, it notifies the other application through the well-defined interface of this event along with the user name, user data etc. Then the other application can take an appropriate action which is in this case the creation of a corresponding user and to map the two users by their name or id number.
TODO: Say something about: hooks, events
Loose-coupled integration together with event-based notification ensure that the two applications:
Usually the integrations are one-sided, that is, as in this example A is embedded in B. B is the master, A is the slave and the communication / notification in this master-slave relationship is only simplex. B notifies A of changes, requests data from A, but it's never the other way around.
The alternative to a loose-coupled system is a tight integration where data structures (database tables, ...) and / or function calls of the two applications are interweaved to a point where it becomes unmaintainable, there are more and more cross-dependencies all over and the resulting system is (almost) a fork of the two other applications.
What happens if one of the applications has a new release? The authors of the integration would then have to backport all the changes to their forked integration system. Or apply all the hacks to the new release.
This is not a fictional scenario, there are plenty of examples of such integrations, e.g. PNphpBB (PostNuke fork of phpBB).
Such tight integrations are not easy to maintain and require a dedicated team to keep it up to date and to port security patches and fixes from the original applications to the fork / hacked integration. Depending on a third, often very small and not well-organized team of developers for a production-level application is sure not preferable. Developers lose their interest and it's important to have applications backed by large development teams guaranteeing continued support and development. Also, being able to quickly apply critical security patches is also of great importance.
The main goal of loose-coupled integration is to provide a very well integrated experience for the end-user while keeping the involved applications as separate as possible in the back-end. Well defined interfaces and a good design guarantee that there are no drawbacks for the end-user compared to tight integrations.
The alternative to event-based notifications of changes is on-the-fly notifications that is, instead of notifying the other applications exactly when the event happens, the other application is notified on-the-fly when a user requests the next embedded page.
Example:
In this case we talk of on-the-fly user creation.
The problems of on-the-fly user creation are obvious:
Therefore we classify on-the-fly synchronization as a low-tech fallback solution for applications that are not ready to provide events / hooks such that plugins or other applications can hook into core functionality like the user creation.
In this example we use the same pattern of a master-slave relationship as the one used before.
The master application is the principle application receiving and handling requests by users and serving the resulting pages. A typical example is a content management system.
Let's further assume the master application is modular and developers can extend its functionality with plugins (or modules, extensions, componennts, ...). To embed a slave application, e.g. a forum or a photo management system, in the master application, you would then write a new plugin for the master. The plugin consists of a small wrapper scripts which communicates with the slave.
Sample for the wrapper script:
$userId = getUserId(); // get the userId of the current active user in the master application $baseUrl = getBaseUrl(); // get the base URL of all URLs that point to the master application include('some/path/to/slaveApplication/embed.php'); // include the interface exposed by the slave application embed_init($userId, $baseUrl); // initialize the slave application with the authenticated user and the base URL $html = embed_run(); // let the slave application handle the current request and return the resulting HTML
Then the master would embed the generated $html in one of its own templates and return the complete rendered page to the user.
We also assume the master application has a built-in event or hook system that enables its plugins to execute their own code on events such as user creation, login or configuration changes. An illutrative implementation of an event system could look like this (we don't encourage the use of globals, but they are illustrative):
function create_user($username, $password) { ... // create the user as usual
global $hooks; if (!empty($hooks[CREATE_USER])) { // check if there are any registered event listeners / hooks foreach ($hooks[CREATE_USER] as $event_handler) { $event_handler->handle_event($username, $password); } } }
A plugin of the master application could register an event handler like this:
$GLOBALS['hooks'][CREATE_USER][] = new my_create_user_handler();
And the handler itself would look like this:
class my_create_user_handler { function handle_event($username, $password) { // synchronize user creation to our embedded application include('path/to/slaveApplication/embed.php'); // include the interface to the slave application embed_init(); // initialize the slave application embed_create_user($username, $password); // create the user in the embedded application } }
Of course you wouldn't implement an event / hooking system that way, but the important part is that there is an event when a user is created in the master application and that plugins of the master application can register themselves as an event listener or hook somehow into core functionality such that they can execute their embed_create_user() function when a user is created.
The primary task of the slave application is to offer an interface or a protocol of integration related methods. This interface must offer methods to:
A sample interface could look like this (the embed.php file referenced above):
function embed_init($userId=null, $baseUrl=null) { include(..); // include some files, intitialize the application $user = get_user_by_mapped_external_userid($userId); set_active_user($user); } function embed_run() { $mode = 'embedded'; $html = main($mode); // call the application in embedded mode (return html instead of printing to browser, ...) return $html; } function embed_create_user($username, $password) { some_api_call_to_create_user($username, $password); }
The slave application must of course meet a lot of requirements that allow it to run in embedded mode. But the important bit here is that the inner architecture of the application is hidden and only a stable, simple interface is exposed to the master application.
Integrating a web application into another can be quite complicating since only a small percentage is designed to also work when embedded and only a few frameworks / applications are designed to offer the means to embed other applications into them.
If there was a standardized way of integrating applications with specifications that must be met by the master and by the slave applications, writing an integration could be faciliated a lot. Everyone would profit:
They key requirements at master applications is the event or hooking system. Without, only a low-tech fallback solution like on-the-fly user creation with periodic synchronization runs is possible. How this event system is implemented, does not matter at all.
The slave applications must expose a well-defined interface or protocol. The details of such an interface are not that important (for now, we can always improve the standardization at a later point), as long as the interface follows the same basic principles to offer functions to initialize, handle the request, create a user, etc.
If all interfaces were standardized one could assemble a CMS that has a feature-rich forum, gallery etc. by just writing a little glue-code. Seeing each CMS writing their own (mostly poor in features) forum solution or multimedia framework and thus wasting their own resources and making users less happy than with feature-rich solutions is reason enough for such a standard.
A standard should have different levels, meaning that not all applications can meet all goals of such a standard, but they should still work together, just on a lower level of standardization.
CMS devs usually want to develop their own solution, favor their own forum or gallery solution etc since they believe that one day, they'll have all the features needed, plus it's then an integral part of their CMS, well desgined and their code is better anyway :) Of course we have to accept and appreciate this position, but with integration/interoperability standards, applications could achieve the same, without having to develop specialized solutions like a forum system.
As a master application:
As a slave application:
Event-based loose integration is an obvious and the most prominent approach to software integration for a long time now. These are a few papers and references on the topic:
More links specific to web / PHP interoperability: