It can be tedious to make network calls during the development of a cloud-based application. It's possible to make thousands of calls across the wire. There is a way to reduce the impact of the network during development -- wait until later to decide on a cloud-based solution.
One of the things that stuck with me
from an Agile conference last spring was Bob Martin's talk about
putting off database implementation as long as possible. He described
an application on which he and a partner worked where he knew the
application would eventually need to store information in a database.
For the most part databases are going to be used in the same way –
handling reads and writes of data.
I knew that if I wanted to create an
application with a cloud-based backend, that choosing which service
to use could be a big deal. But I wanted to minimize the impact of
the backend on my application as much as possible and I didn't like the idea of calling though to a cloud-based network during application development.
Having experience with both Google'sApp Engine and Amazon's Simple DB, I found both to have good and bad.
Before hearing Uncle Bob speak, the first thing I did was consider
which cloud-based storage solution to pick. I spent most of my time
weighing the pros and cons of each service. Having worked with Amazon
Simple DB in the past, including on some proof of concept projects at
Northwoods, I knew Amazon was a reliable, and well documented
service. But at the beginning of this project, I ended up choosing
Google's AppEngine. AppEngine seemed to have the easiest cloud
solution to implement. With AppEngine, Google offered an Eclipse
plugin that made it simple to simulate a cloud environment. AppEngine also provides a starter project that wires up the network
access ahead of time. The project even implements a push notification architecture.
However in experimenting with
AppEngine, I found my implementation started to become closely tied
with the implementation of the cloud-based backend. I had cloud-based
database calls in nearly every activity. These activities also were tied to Googles object-relational model. I started to think that it wasn't in my application's best interest to have proxy objects that can only talk to one
kind of service.
The tighter coupling with Google also
became more apparent when it decided to bring its Cloud to Device
Messaging service out of the beta phase and into full production.
There was enough of a difference migrating to the new version of the
service that it didn't make sense to further bind the application to
Google. There was far too much left to be done on the actual program,
and what if by the time it was done, Google's Cloud service changed
again?
Turns out what I ended up doing (and
should have done in the first place) was put off the real database
stuff. The best way I've found to write a cloud application is to create an
interface that will serve as my application's face to the cloud. I
can simulate interaction with the cloud by making calls through this
interface to a class that implements this interface and returns the
expected data. Essentially I can implement an in memory database
using HashMaps that function as if I were making calls to a reliable
and fast network. When the time comes to actually choose a cloud
solution, I'll use the adapter pattern and create a class that
implements my application's data access interface as well as the
interface provided by the cloud solution provider of my choosing. In
this adapter class is where I can put the logic to handle network
conditions. One of the major changes I'll have to keep track of is
creating the UI screens that must be present to handle loading data
from the network. In this architecture, those pages can get lost
since the “network” calls return asynchronously during
development.
This approach has had many merits,
especially when it comes to mocking and test-driven development.
Although the Google AppEngine solution allowed me to debug with
breakpoints on a local server, this approach of defining my own
interface has allowed me to create mocks without a concern for what
is happening on the other side of the wire. Since the database interface is injected into a base activity, it is available to all other activities that inherit from it. This is useful for mocking and test driving with Robolectric. This architecture also has forced me to keep
logic in the application layer while keeping the cloud database with
the sole responsibility of storing and retrieving data.
General Architecture

No comments:
Post a Comment