Bahndr is an iPhone app that we’ve been working on for the past few months. The concept behind Bahndr is simple, its a social game meant to capture those funny moments in everyday life where you’d normally snap a picture, give it a witty caption and share it with your friends. With Bahndr, we’ve built a simple game that encourages users to submit witty photos and captions, and rewards them for creating great content that the community likes.
In this post, I am going to nerd out for a bit and describe the architecture of the Bahndr app, which is interesting in that it melds a Objective-C front-end client atop a generic, REST-ful, WCF web service.
The Bahndr iPhone app runs completely off of a local, CoreData exposed, SQLlite database. This database is a cache of the master Bahndr database that sits in the cloud. As the user moves through the app, the view controllers only ever use the phone’s local database to display content. Separately, there exists a thin data synchronization layer that uses the user’s position in the app to predict what data the user will potentially look at next, and then makes asynchronous HTTP calls to the Bahndr server in order to get the data to the phone before the view controller needs to display it.
The backend of Bahndr is made of a JSON-enabled, WCF-HTTP service that sits atop a SQL Server 2008 database. While Bahndr’s entire backend is built on the Microsoft stack, we have shunned XML in favor of JSON wherever possible.
Designing the Service Layer
In designing the Bahndr web service, we had 2 basic requirements
- We wanted to create a reusable service layer and transfer protocol that we could use for all our future apps, without requiring us to build it from scratch each time.
- We wanted to ensure our development process wasn’t held up each time we changed a schema element because of a brittle, strongly-typed service layer.
With these 2 requirements, we designed the Bahndr service as a REST-ful data pump that is agnostic to the underlying application schema. Thus, the protocol between the client and server is weakly typed, and operates solely through the concepts of URL-addressable resources.
Our WCF service contract contains these 4 verbs:
Response Create(long objectids, string objectTypes, Stream resources);
Response Put(long objectid, string attributeNames, string attributeValues, string attributeOperationCodes, Stream binaryAttributeValues);
Response Delete(long objectid, string objectType);
Response Get(long objectid, stirng objectType);
The protocol the service layer supports is based off a simple Create-Read-Update-Delete (CRUD) semantic. We modified the Create method to support bulk processing, along with including support on the Create/Put for streaming binary data as part of any resource. Further, the protocol delivers responses to the client that not only include request status, but every response can contain a collection of arbitrary resources that the server includes to eliminate the need for further calls to the server.
Getting data out of the service is done through a basic enumeration protocol using a simple attribute-value-operator query language:
EnumerationResponse Enumerate(Query query, EnumerationContext context);
The Bahndr iPhone app includes a component that submits enumerations to the server, and can then page down the results of that enumeration on demand. The iPhone app uses both globally defined paging settings, along with internal heuristics to adjust how much data is being brought down from the cloud to the phone. The goal here is to minimize web traffic, and preserve battery life.
Outside of authentication methods, thats the basic structure of the Bahndr service. The server very much operates as a generic resource repository, with the Bahndr specific logic sitting in the layer between this interface and the database.
I’ll cover more of that in my next technical post….