All Business— Mobile Architecture for a Multi-Platform Future

Mark Dappollone
4 min readOct 27, 2019

--

Modern architectural patterns have lots of benefits for front-end applications: code is clearer and layers become separate, which insulates them from change, reducing the likelihood of regression issues, and enabling easier refactoring, modification and augmentation. Architectures also promote testability in a hardware-agnostic environment. But a new problem is emerging for software architectures to try and handle — multi-platform.

Wait, what?

Build once, run everywhere… the dream of multi-platform apps has been dreamt since the dark ages of aught nine, starting with the siren-song of Phone Gap, moving to Xamarin, and followed by a host of other attempts like Flutter and, maybe the one that this article is ultimately concerned with: Kotlin Multi-Platform.

Multi-Platform apps are built with a shared codebase comprising the business logic of the application, which is theoretically capable of delivering presentations and interact-ability to a variety of platform specific view layers.

In this environment, one particular architectural principle is key:

All Logic Is Business Logic

Ok, not all logic. But that rule establishes a good mindset for the process of multi-platform preparation. Unless it’s reading from or writing to the screen, it’s probably business logic. This approach is also great for single-platform apps, as it forces you to abstract everything and concoct new ways to separate the layers of your application, even in cases where it would be much easier to simply let the view do the work. Let’s look at some examples:

Strings

wrong String

We’re going to focus on Android in the examples, but there are analogs to these on all platforms. Strings are generally built-in to the platform-specific infrastructure of a mobile app, because they have to be read/writable to persistent storage. But, there are things you can do to abstract out the process of getting strings.

Remote Config

One way to remove the platform dependency of strings is not to store them in the application package at all. Instead of keeping a string repository in your app, you can keep the entire thing in a remote CMS, and download it when your app launches. The secondary bonus with this method is that you can remotely change the strings any time you want, without an app release.

Abstraction

Most platform level concerns can be abstracted out, and then handled by a logic layer with no dependency on platform code, and strings are no different. You can see an example of this here, where I talk about how to abstract out Android “Resources” and make them moregeneric. But now we also have ResourceProvider which will do all that work for you, using annotation processing. Learn more about that here.

Data Passing

Not like that.

Another example we can consider, that we haven’t before, is data passing. Getting data from one place to another in your app is highly dependent on the specific platform’s framework for doing so. For Android, data is generally passed and received in a format built into the OS called Bundle. A Bundle object can basically be stuffed with all different kinds of data, and then individual data can be retrieved using a key. Some typical Android code for doing this might look like this:

But that’s a lot of business logic that has to be handled by an Activity or Fragment — SDK objects generally associated with the View layer. That object needs to know an awful lot the structure of data being passed around (what the keys are, and what they mean), and which presentation functions to call for each piece of data, AND none of that logic can be unit tested. Wouldn’t it be better to just let the presenter handle all of that? With some basic Kotlin trickery, now we can make that happen. Observe:

With a couple simple extension functions, we can translate the Bundle, defined in the Android SDK, to an ordinary Map, and back. This allows the presenter do all the work of figuring out what needs to happen based on the contents of that Map, without ever needing to know anything about the Bundle, while keeping everything unit testable. Then the above block of intent processing becomes simply:

Much better

Takeaway

With Kotlin Native, the promise of a multi-platform future is closer than ever, and without any code translation or having to learn Dart. But in order to get there, we have to start implementing entirely passive Views in our architectures, so that all the logic can be handled by common objects in the presentation and data modeling layers.

Conveniently, these techniques and their results are beneficial to our platform-specific code as well.

--

--

Mark Dappollone
Mark Dappollone

No responses yet