A tale of composition đŸŒŗ

In my mind there are two ways of thinking about composition, when we have FRP or FP in the mix. The first one is often mentioned when compared to inheritance[1]:

Composition over inheritance in object-oriented programming is the principle that classes should achieve polymorphic behaviour and code reuse by their composition (by containing instances of other classes that implement the desired functionality) rather than inheritance from a base or parent class.

The second is achieve via FP concepts like Functors and Monads.

I like to think about this like pieces of LEGO, that we just assemble to form and build our apps.[1:1] The traditional problem, in iOS development is that, by default, the building block that allows a piece to attach itself to another, is unfortunately nonexistent.

If we consider a NSOperation as a building block, that encapsulates in itself a task, how easy is it to pass its success/failure to the next operation? Valiant efforts have been attempted, here and there, but ultimately, it seems, in my opinion, that they are trying to fix a broken leg with a bandage. This LEGO piece needs to be able to play nicely with other LEGO, otherwise it stops being a building block and its purpose becomes unclear.

The ability of compositing these different pieces, is the real selling point of frameworks like ReactiveCocoa/ReactiveSwift.[1:2]

Lil' Bits 🚗 🚙 🚕

The fundamental pieces, are based on the open source work done on the Reactor project and its core:

  1. Network layer.
  2. Parser via the Mappable protocol, heavily influenced by the work done in Argo
  3. Persistence layer that is able to store entities that conform with the Mappable protocol in the app's sandbox and on the keychain.

With these in place, we can pretty much add any business layer on top of it and it will cope nicely, since all those bits are completely domain agnostic.

Building a Castle 🏰

A patient at babylon health has an associated region. This region defines things like what features is the patient able to access. Because a region, by itself, has a lot of information, when we get a patient, it only comes with a regionId. At a certain point in the flow, we need to use that regionId to fetch the region. Let's start by defining what we are expected of this entity:

These <Domain Specific>BusinessController, from an architectural point of view, sit below a ViewModel, but above the previously mentioned entities. In the case of our concrete entity RegionBusinessController, it is define as:

This RegionBusinessController, is in itself formed by yet another BusinessController, in this case a FetcherBusinessController. This begs the question: what's the purpose of this RegionBusinessController?

To give meaning/context to a domain agnostic entity, in this case fetching regions.

The FetcherBusinessController<T> is then defined as:

You are now starting to see a trend here, with the introduction of the Connectable:

The Connectable protocol is just a thin wrapper on top of our Network layer, which can be pretty much anything, from a URLSession to Alamofire.

This might seem like over engineering, but to give a bit of context:

The babylon app is a 3 year old application, with many different architectural approaches, it's important that each piece is completely decoupled, so we can easily refactor parts of the app while we are still shipping new features.

In the case of the RegionBusinessController:

  1. We already have a way to fetch a given region and persist it securely.
  2. Currently the RegionBusinessController lacks the ability to persist.
  3. Since we are refactoring bit by bit, we will leave the persistence part to a later phase. In the meantime we will use the RegionBusinessController as is, since it's fully tested and it is well aligned with the architectural approach we want to follow.
  4. Once we are in a comfortable position to persist the region using the new architecture, we will use an entity similar to the FetcherBusinessController<T> that, not only makes a network request and parses the response, but also stores the entity. This new domain agnostic entity, will make use the FetcherBusinessController<T> and another entity that is able to persist Mappable entities.

When using composition, we are able to have cleanly decoupled entities that do one and only one thing, but that are still able to play nicely together with the help of FP concepts [1:3].

  1. "In functional programming, monads are a way to build computer programs by joining simple components in robust ways." - Monads on Wikipedia ↩ī¸Ž ↩ī¸Ž ↩ī¸Ž ↩ī¸Ž