A tale of composition 🌳
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.2 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.
Lil' Bits 🚗 🚙 🚕
- Network layer.
- Parser via the
Mappableprotocol, heavily influenced by the work done in Argo
- Persistence layer that is able to store entities that conform with the
Mappableprotocol 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 🏰
patient at babylon health has an associated
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:
<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:
RegionBusinessController, is in itself formed by yet another BusinessController, in this case a
FetcherBusinessController. This begs the question: what's the purpose of this
To give meaning/context to a domain agnostic entity, in this case fetching regions.
FetcherBusinessController<T> is then defined as:
You are now starting to see a trend here, with the introduction of 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
- We already have a way to fetch a given region and persist it securely.
- Currently the
RegionBusinessControllerlacks the ability to persist.
- Since we are refactoring bit by bit, we will leave the persistence part to a later phase. In the meantime we will use the
RegionBusinessControlleras is, since it's fully tested and it is well aligned with the architectural approach we want to follow.
- 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
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 4.
Inheritance, is still sometimes desirable and the only alternative. ↩
Most of the time, this is exactly how non-technical people think apps are. In an ideal world they would. ↩
Unfortunately, it seems that these are still sold as fancy KVO replacers. ↩