Hybrid Online/Offline Architecture

Your application is deployed to mobile devices, which sometimes have network access, but always needs to function. The standard solution is to keep a local cache of the remote database, periodically synchronised when the device is connected.

However, in your application, it is essential to use the freshest data available. Furthermore, many deployments are to desktops on the same LAN as your data centre. Cached data is good for giving a sales demonstration in your client's underground conference room, but your desk-bound workers need live data.

You could write a desktop application for cached data and a web application for live data, but why maintain two applications just to handle changes in connectivity? A more intelligent data source is needed; one that can choose at run-time whether to use a local data cache, or your application server.



The database, data access layer, and business logic are deployed to both the server and client. The server exposes its business logic through a service layer, which the client's online proxy connects to. The client also has an offline proxy for accessing its local copy of the business logic.

Both proxies implement IProxy, which itself implements the business logic's interface. The client application accesses business logic through a reference to IProxy, which runtime dependency injection satisfies with the correct proxy.

(For simplicity's sake, the sample code does not contain any independent business logic. Data is retrieved directly from the data access layer.)


The sample code contains a SQL Server project for creating the sample database. Data access is via the entity framework, and WCF is used for the service layer. Ninject is used to inject the correct proxy into WPF view models. Please see the included Read Me.htm for a list of dependencies and where to find them.


Synchronisation begins by asking the server for the current schema version. If this is greater than the client's schema, a schema update is applied, and the new version is recorded.

Synchronisation initially used Microsoft's Sync Framework. This has the advantage of a mature API, and it can work through a 3-tier architecture. However, it cannot cope with schema changes to existing tables. To get around this, DDL triggers were used to update the framework's metadata whenever the schema changed. The last version to use the Sync Framework is thus tagged.

The client objected to relying on DDL triggers, and so later versions switched to ApexSQL's Diff API. This requires a direct connection to the server, so synchronisation is only performed from within the office LAN.

ID Ranges

To avoid primary key conflicts, each client is assigned a range of ID values to use when it first synchronises. After every synchronisation, each table in the client database is reseeded to the correct ID range.

The server does not need reseeding, as it uses positive ID values, while the client ranges are all negative.


This code is made available by kind permission of my client, Logican. It is provided for informational purposes only, and may not be used without their explicit consent. The hybrid approach was suggested by Joseph Babad.


Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.