Designing mobile app architecture

Egor Taflanidi
red_mad_robot mobile
9 min readFeb 3, 2016

Number one sign of bad design: the presence of a ‘God’-object, the name of which contains ‘Manager’, ‘Processor’ or ‘API’

Avoiding specifics in all conversations and decisions is a common trait for every architect I’ve ever met. This is understandable, since the essence of the flexibility of any system is borne of its abstraction from concrete decisions. The longer you can put off a decision, the more flexible the system is. If the UI is sufficiently abstracted away from data storage-level modules, then you can easily switch from extracting data from a hard drive to getting the same information from a server API.

I’m going to describe an overarching approach to breaking down app logic into levels and layers, and also home in on specific actors in particular processes, with their own functions and environmental interactions.

The Setting

TL;DR: Developers are scientists too

University students study natural sciences, management, applied psychology and so on. Each discipline comes with a body of accumulated academic knowledge and experience which facilitate (and accelerate) science forward motion. This foundation also ensures that the science yields practical benefits: a manufacturing technologist would often much rather use tried and tested methods than innovate. Of course, this foundation constantly evolves and is enriched by new findings and practices.

The science that underpins programming is as serious as that which relates to the production of aspirin, for example. As a typical member of the formal sciences, computer science has the same set and structure of formal methods, it uses time-honoured approaches to discovery and naturally comes with with its own body of academic knowledge. Using a scientific approach ensures quality results, while understanding and applying basic principles of software development makes product support simpler.

Talking about time-honoured approaches, Christopher Alexander’s book Pattern Language, which gave impetus for the use of templates in programming, was published in 1977 and the Model-View-Controller pattern was described in 1979.

Armed with software development best practices and dressed in our lab coats, let’s now create a typical application design for a mobile platform.

N.B. It is quite likely that your project will look differently in real life (considering given business requirements and development timeframes). But you will definitely be able to say that your product is based on a considered set of decisions, not some random ideas.

Inputs

Let’s say we have a spherical system in a vacuum, like:

  • a server with a documented API from several web services;
  • a client application and several devices that the app needs to support: tablets, telephones, watches and so on;
  • UI application wireframes;
  • a set of user stories that define the behaviour of the app;
  • a list of non-functional requirements for the system, including security and performance benchmarks.

Hourglass model

Always two siths there are, no more, no less: one to build the service, one to put a UI on IT

First, let’s separate the Model from the Controller and the View, thus creating two app layers: a ‘bottom’ layer aimed at the server side, and a ‘top’, user-facing, layer.

Let’s stick with the classical principle that application layers interact through model objects — classes with ‘transparent’ execution consisting of accessors and mutators (get- and set- methods; essentially the class could only have properties). Such classes have no logic, they exist merely to store and transfer data.

The central entity of an application — the ‘neck’ of the hourglass — will be an object sometimes referred to as a ‘dependency inverter’. This will become the only singleton class in our mobile app, and it will have the sole function of providing services to the ‘top’ layers.

For mobile app projects, it generally makes a lot of sense for the Controller and View layers to be developed by one person, whereas the rest of the business logic, which affects persistence and data processing, can easily be coded by another developer. As a result, we channel the development into two streams connected by an interface between the services layer and UI.

Services

The story of a plug and socket connection

How do you create an ordinary server app? Easy. There’s always a database (there might be more than one) with tables. The tables contain records. Each record has a set of fields with data.

The records are model objects with fields, which creates a logical demarcation in the application based on object classes: a single table is a single object class. For example, the ‘Users’ table contains the object class ‘User’ (fields: ID, name, address). The ‘Messages’ table contains the object class ‘Messages’ (fields: ID, subject, body, recipient, sender, date). And so on.

Thus the logic is based on tables and object classes:

  1. Model object, which represents object classes

2. The serialization of the model object to save it in the table

3. The deserialisation of the model object from the database

4. The transformation of the model object: the calculation of statistics, data sorting, searching and so on

5. The serialisation of model objects to send over the network

6. The deserialisation of model objects received from the network

7. A web service deployed online which corresponds to the object class

This amounts to a kind of stack and the server application is made up of several of these such independent stacks, each one corresponding to a object class.

Basically, if your API runs the web-service api.domain.com/1–0/messages with a classical CRUD-interface, it will be underpinned by the ‘Message’ stack described above.

Create = POST;

Read = GET;

Update = PATCH;

Delete = DELETE.

N.B. There are different interpretations for the operators POST and PUT. Sometimes, POST is responsible for entity creation, other times — for updates. There is no single meaning but this is no bad thing.

As a rule, the server supports a standard set of requests which are distinguished based on URL suffixes for the corresponding web-service:

{api}/messages/{id} — entity operations on the given ID;

{api}/messages/{id}/{property} — entity operation on the {property} field with the ID={id}

THEREFORE:

All we have to do is build a plug for the socket.

Specifically:

1. Create a transport layer for the app as a module which will comprise an HTTP connection with all settings (security, timeouts and so on)

  • the module interface should be the same CRUD as the web-service. If you have access to the address {api}/messages — the corresponding four methods should be in place; if you have access to GET {api}/messages/{id}/{property} — create a separate method that will receive data from this {property}
  • the module should be replaceable. If at some point your {api}/messages web-service stops working, you will need to implement a single class that matches the transport level interface, but which fetches data from the file system

2. Develop the parsers and serialisers, which will generate model objects and, conversely, transform them into a type that is suitable for your transport level.

  • parsers and serialisers contain information about the transformation of model objects into JSON and XML-type dictionaries. Don’t try to attach this information to the model objects themselves — it have no relationship to other layers of the application

3. Design an entity that will be responsible for caching.

  • The entity should be able to provide secure access to the received data, overwrite and update this data with new data

4. Design the ‘service’ itself — an entity that will be responsible for coordination between the transport level, parsers, serialisers and the cache.

  • the service interface should fully meet the business requirements of the UI level. If the UI requires a particular entity, there should be a way to receive this entity. If the UI requires saving the entity with certain parameters, the service should have a method with these parameters. If the UI requires a dataset with objects of a particular type sorted by a particular criterion — the service should provide a method for returning the dataset
  • And let’s not forget the classical principle: application layers interact through model objects. The use of dictionaries/maps for data transfer is unacceptable — only use strict typing

We thus essentially create our own stack, which corresponds to that on the server, but which operates in reverse. This is the sequence of steps that the data has to go through to get from the central repository to the end user:

There are as many services as there are object classes. This number can be easily calculated from the API specification.

N.B. ‘Pure’ services are fairly uncommon. Some services serve several object classes. This happens because model entities can be layered inside each other: one entity can contain a dataset for objects of a different type or have a similar object as a property.

This is no big deal, but your parsers and serialisers need to appropriately treat such nesting and generate the respective types of objects.

Services should be as autonomous as possible, which doesn’t stop you from building logical dependencies between them. For example, it makes a lot of sense for an application to have a service that is responsible for authorising users and for supporting a server session that other services depend on — they use the token it provides to create requests.

N.B. Each service will have a CRUD-like interface based on the object class processed by this service. This can be a great reason for separating out an abstract service with an abstract CRUD-interface that can later be inherited by other services, ensuring a high level of code reuse.

The design of a model level in this way is the essence of superimposing a service-oriented architecture over a multi-layered architecture pattern.

We haven’t invented anything new.

UI Level

A time for great stories

So, you’ve started on a project and downloaded the source code for the application. What’s the easiest way to figure out what this app does? Exactly— launch it! You will see familiar tables with cells, rendered buttons, date spinners, navigation, screen names… The question now is how do you match that up against the source code that is scattered across several hundred files in the IDE?

We have already pulled out one of the application layers — the model layer can now be a completely autonomous module that is pluggable into another project that uses the same back-end. Or even use it for a different back-end if you have observed all design principles and preserved a certain level of abstraction.

But here’s the hiccup: we still have two (!) more layers, View and Controller, and the only thing that gives any indication about what sits where in the source code is the app itself, running on a device or an emulator. And here’s where user stories come to the rescue.

If your app is backed by at least some common sense, it will match particular user stories which define the scenarios required to carry out a particular action. The classical example are the registration and authorisation user stories. When registering, the user enters some personal data, which is checked for completeness, a login and password is specified; there might be some additional security measures like SMS confirmation and so on. Accordingly, there is a whole story around registration. Logic dictates that the easiest solution would be to follow these user stories in building UI-level structures.

Moreover, the application SDK for iOS provides the full set of tools required for this: it is simple enough to create a storyboard for each story and divide up the project structure such that all classes are built around these stories. These stories are then dynamically linked up, and this approach works even for the older versions of the SDK through simple libraries, but these are implementation details.

N.B. Do not mix up user stories from this article with those from the agile methodology. We are talking about user scenarios applied to the storyboards, which gives us a “user story” collocation.

In the end, you will have a structure that is divided into modules based on stories. Each module will have its own views and controllers with utility classes and the structure will be easy to navigate solely on the basis of what’s happening on the screen of the device or emulator.

N.B. Of course, don’t forget about classical design principles. Apply SOLID, DRY, KISS, “Tell, don’t ask” and YAGNI in full — don’t be lazy, separate classes based on their functions. For all else, let experience be your guide.

Here is a rough diagram of classes:

Obviously, there is no end to perfection, and this is but one approach.

Conclusion and results

We briefly considered how to create a streamlined architecture for a mobile application. We combined classical project approaches (MVC, SOA and Layered Architecture) and also applied logical separation of source code into modules, based on user stories.

--

--