Read Part 2 here!
Microservices architecture has become a well explored and beloved architecture in building large complex applications. Micro services allows the decomposition of a large monolith application into smaller loosely coupled entities that can leverage each other using http protocol. It is an implementation of the SOA architecture, and allows one to be able to leverage several advantages associated with building applications using this approach.
The advantages of loosely coupling components include,
- Independent technology stacks
- Targeted scalability
- Separate teams that focus on specific functionality
- Ability to deploy parts of the application separately, without having to deploy a large monolithic application every single time.
Monolith Mobile Apps vs. Microservices Mobile Apps
I ventured to explore the ability to implement micro services architecture when building large complex mobile applications.
The idea was to bring the advantages of the micro services world to mobile application development.
Let us take for example the Alexa Mobile App. This single app is actually a fairly sophisticated and complex app, that provides a single portal for the user to interact with Alexa, and encapsulates several different functionalities, including IoT, VoIP call, music streaming and several others, all within a single complex mobile application.
While the single monolithic application approach may work well for simple mobile applications, surely there is a better way to build large complex mobile applications that have several different functional elements, using the micro services paradigm, thereby allowing for:
- Separate code repositories for different parts of the application
- Separate teams that could focus on specific functionality within the application, eg. separate teams working on IoT portion of a mobile app, and a different team working on the Music streaming portion of the mobile app
- The ability to deploy the mobile application in partial increments versus the entire large mobile app monolith
The first thought was to simply build a native mobile app using loosely coupled modules, that communicated with each other via well defined interfaces. However this was a far too simplistic an approach and would not really result in any of the aforementioned benefits of a micro services approach.
Mobile applications are different, complex and have their own unique features that need to be addressed.
The micro services solution would need to consider:
- Cross platform support (iOS, Android, UWP), code reusability and maintenance
- On-device data persistence, native and non-native databases
- App update without App Store (Code pushify)
- Push notifications handling
- Login and user session management
- Leveraging MBaaS services like Amazon Mobile Hub, Azure Mobile Hub etc. for infrastructure of the mobile app
- App performance and user experience
- Ability to integrate with native code if required, and ability to interface with native device API libraries if required.
React Native as choice for cross platform development
At the outset we explored a cross platform solution versus a native approach. Besides the obvious advantages of cross platform portability, in terms of code maintenance etc. we were also keenly aware of performance issues that were a frequent complaint of cross platform hybrid applications. To this end we deeply investigated the options of Cordova, Xamarin, Sencha, Ionic, and React Native.
Cordova simply wrapped the JS code and ran it within a web browser container. To that end the UI simply mimicked the native UI. Ionic libraries could be used for faster out of the box native UI mimicking. However both did not actually compile to native.
Xamarin and React Native were the two choices that complied to native. In that when you describe a button within your JS code in React Native, the RN bridge actually uses the native button component to render it within the specific app platform.
React Native in particular had the following features,
- It used the concept of diff (similar to virtual DOM) to render only those components that had changed, making the refresh faster
- React Native had the ability to interface easily with existing native code, such that apps written in native could be migrated incrementally to React Native without having to build the entire app in React Native outright.
- React Native was backed by Facebook and had a healthy community of and vibrant community of developers and resources available.
For this and other related reasons the choice of React Native as the cross platform framework for mobile application development was appealing.
It is important to remember that when thinking of cross platform the aim is not to achieve a 100% parity of shared codebase between multiple platforms. In reality about 90–95% of the codebase will be shared code base and cross platform, however there will still be a segment of code that will have to be customized for a particular platform (iOS, Android), to obtain performance efficiencies and leverage native platform capabilities.
React Native and mobile application development using micro services architecture
After having narrowed down to React Native as the platform of choice for cross platform mobile app development, several questions came up,
Some of these questions related to RN ability to implement cross platform features, and interface with native code, all while preserving performance.
* What are the performance issues when using React Native?
* What are some common issues observed when using React Native?
* What are the most frequently used production libraries used in React Native?
* Can React Native work with existing native code? How does it integrate with native code? Can it integrate with both Objective C and Swift in iOS?
* Does React Native have enough third party API library and support?
* What is the future of React Native?
Other questions centered around features that are unique to mobile applications, and how these features would be implemented in React Native.
* What are the on-device data persistence options when using RN?
* How to handle user authentication, login and maintain persistent user session in ReactNative?
* How do push notifications (remote and local) work in React Native? How will push notifications work under the framework of mini-apps?
* How can we leverage Amazon Mobile Push SNS within React Native?
* What are the options for ReactNative Performance Monitoring Tool?
* What kind of unit testing and integration testing automation tools are available in RN?
Others related to implementing the micro services architecture within the RN framework,
Can we build React Native apps using micro services architecture?
The answer to this last question was crucial and opened another pandora’s box of questions.
* Can we build Mini Apps using React Native, with separate code repositories and separate compilations?
* What is the workflow when building the mobile app as combinations of mini apps?
Introducing React Native Mini App
While exploring the implementation of microservices in mobile applications i stumbled onto this interesting article by Walmart Labs.
The Walmart Labs team was looking for an effective solution and technology platform to migrate to and had chosen React Native as their choice of cross platform technology.
They introduced this concept of MiniApps which leveraged the Electrode Native platform and allowed developers to create small independent apps using React Native called “mini-apps”, that could focus on one particular functionality. Each Mini App could be compiled, run and tested separately and then a group of mini apps could be combined together into the Electrode Cauldron (similar to an npm file), and run within an Electrode Container. This allowed developers to combine the functionality of there different apps into a single integrated app. Whats more, mini apps can be integrated into native iOS or android without cumbersome interface logic, by simply importing it as an external library within xCode or Android Java.
Mini Apps can be combined together with other Mini Apps or native mobile components to build a full fledged, scalable and complex mobile application.
Besides the obvious cross-platform advantages of using React Native, Mini Apps built using Electrode Native open source libraries has the following added benefits.
- Independent Repos:
Using Electrode Native each MiniApp can have its own repositories and have their own life cycle and DevOps process. Additionally each Mini App can be run individually as a standalone MiniApp, or as part of a group of Mini Apps, using the Electrode Native Runner. This allows you to effectively develop, run, debug, and test each functionally independent Mini App, without it being part of a single complex monolithic mobile app.
- Micro services in Mobile Apps:
The simple but powerful concept of independent repos and mini apps gives developers the ability to architect mobile apps as micro services, and accordingly structure teams around functional area of ownership. For example you may have the “Checkout” team own the React Native checkout area of the app that lives in its own repository, or the Item Page team with their own repo, allowing the teams to scale without too many cooks in any one kitchen.
- Seamless ReactNative to MobileNative communication:
Electrode Native provides mobile application developers a single third-party library known as the “Electrode Native Container”. The container includes all their MiniApps and developers interact with it as they would with any other third-party native library. If you need to update a MiniApp version or add new MiniApps to your container, all it takes is triggering a new container generation and publication. Developers only need to update the container version that is to be used within their mobile application.
Thus, you simply add the container as a new native dependency to the native mobile application, as any other native third-party dependency, and the Electrode Native container does the heavy lifting of generating all the integration code. The Electrode Native container does this by packaging your React Native app into an AAR (Android) or framework (iOS), depending on the platform.
- OTA Updates (for hot fixes, without going through App Store):
You can publish new versions of these MiniApps as often as you need to, and release them over the air using CodePushify, without going through the App Store. This allows you to quickly push hot fixes in production mobile apps. Note that this feature is still in its experimental stage when it comes to customized OTA updates for Mini Apps.
Data Persistence and on-device storage options with React Native
Data persistence is holding onto data in case the application or device is reset. Native iOS apps store on-device using CoreData or SQLite, and Native Android apps also have a SQLite implementation. In React Native the out-of-the-box option for on-device storage and data persistence is AsyncStorage.
AsyncStorage is a simple, unencrypted, asynchronous, persistent, key-value storage system that is global to the app.
On iOS, AsyncStorage is backed by native code that stores small values in a serialized dictionary and larger values in separate files.
On Android, AsyncStorage will use either RocksDB or SQLite based on what is available. AsyncStorage has a default limit of 6MB on Android. It is possible to configure a larger limit (on Java code) or use redux-persist-filesystem-storage as storage engine for Android.
Storage stays local to the device, is unencrypted, goes away if you delete the app and persists during upgrades (both native upgrades ala TestFlight and code upgrades via CodePush).
The downside to AsyncStorage is that it has slow runtime and has no indexing capabilities. Because AsyncStorage only accepts string as its value, all data must first be serialized into string before being inserted, and deserialized once retrieved.
AsyncStorage is not suited when dealing with a large amount of data.
Realm is a relative new comer in the on-device data persistence option. It is an object-database management system, with first stable release in January 2017. Like SQLite, Realm is serverless and cross-platform. It can be stored both on disk as well as in memory. It is highly versatile with cross-platform support for iOS, Android, Xamarin, ReactNative and UWP (May 2017).
Realm Database is an alternative to SQLite and Core Data, is faster than AsyncStorage and SQLite, and has several other advantages including,
- Ability to secure on-device data with transparent encryption and decryption
- Reactive architecture, means it can be directly connected to UI, and if data changes it will automatically refresh and appear on screen.
- One application can have multiple Realms, both local and remote
- Can set different permissions for different users.
- Realm is free of charge, and is fast and easy to use
In order to maximize the performance, Realm has statically typed database which means you first need to define the structure of database (using JS classes) before you can add/remove anything to it.
When dealing with a large amount of data, or simply require a faster local storage, Realm is a better and more attractive option.
Real World performance issues when using React Native
Most of the material in this section is sourced from various articles, websites and forums on the subject, and is meant to abstract some of the common and frequently encountered real world issues observed by teams using RN.
Some Commonly Used React Native Libraries Use in a Production React Native App
- React Native
- react-native-camera, A reasonably feature-complete photo/video camera component
- react-native-code-push, This is a fantastic library by Microsoft for updating React Native code in production.
- redux-persist, This handy library allows a Redux store to be persisted to and restored from disk.
- react-native-system-notification, Send or schedule Android system notifications for React Native.
- react-native-notifications, Handle all the aspects of push notifications for your app, including remote and local notifications, interactive notifications, silent notifications, and more. All the native iOS notifications features are supported!
Performance Limitations of React Native and How to Overcome Them
I came across the following video where Tal Kol(Wix.com) provides a smart approach to overcoming the performance limitations of React Native.
He first examines why React Native is faster than say cordova or phone gap bridge that does exactly the same thing, and passes data between the JS world and the Native world. He concludes that
- ReactNative passes this data between the bridge async, and in batch and once in a while.
- It brings the concept for the React Diff to achieve this update.
He advocates keeping the passes over the bridge to a minimum, since it is not the JS realm that is slow, or native that is slow, it is the changing from the JS to the native, the bridge is the factor to slow it down
React Native Parity and the Leaky Bridge Issue
While React Native does have some fantastic selling points and does a good job bridging the gap between iOS and Android, you’re not going to achieve complete parity between the two operating systems. There are certain things that one platform can do that the other can’t handle, mostly related to styling views, but also more important considerations such as performance testing.
There are some inconsistencies between the functionality of React Native on iOS and on Android. Some React Native behaviors and style implementation differ between the platforms. Some properties are supported on iOS but not Android and iOS seems to have more features than Android. In the React Native documentation you can see these properties and features marked as “Android only” or “iOS only.”
Like any cross-platform abstraction, React Native can be leaky. To write a cross-platform app that purely lives inside JS Runtime, you have to write React-only code. React and React Native doesn’t have ways to handle primitives like UINavigationController — they want your entire app to be represented as a series of components that can be mapped across many platforms.
End of Part 1
Read Part 2 here
In part 2 of this article we will delve into push notifications, MBaaS options, and explore how React Native interfaces with existing native code using Native Modules. We shall the tie all of these pieces together in concluding remarks and will present a holistic proposed implementation of microservices architecture using mini apps, MBaaS, push notification services, realm database, and electrode native container.
Found this post useful? Hit the 👏 button below to show how much you liked it :)
Follow me on Medium for the latest updates and posts!