-
Notifications
You must be signed in to change notification settings - Fork 5
Home
MERLin' is a reactive framework that aims to simplify the adoption of an events based architectural style within an iOS app. It emphasise the concept of modularity, taking to the highest level the principle of separation of concerns.
MERLin' uses RxSwift to power the chosen messaging design pattern which is Publish-subscribe.
With high modularity comes high testability, control over dependencies and a lower build time.
When a greenfield app is started, the analysis of its complexity drives the team to choose one, among many possible architectural design pattern to build each feature. Consistency, at that point, is important and while the development continues, you might find your choice an overkill for many feature, or not good enough for more complex ones.
I've seen apps built in VIPER where presenters were simply proxying the interactors. In fact the presenter didn't had much to do given the low feature's complexity. I also saw MVVM applications where the ViewModel was doing too much... What's the tradeoff?
How do you choose an architecture that takes in account not the overall app complexity, but the complexity of any single feature?
Asking myself this questions I found that the answer is: you can't.
If your app is simple enough you might find common ground among features and be able to happily build them following the same architectural design pattern. Unfortunately, in real life if you choose VIPER soon or later you'll get a couple of features were presenter and interactor are overkill.
Thinking out of the box for a new app architecture in HBC Digital, I started thinking "wait... but when we use third party libraries, do we care about their specific architectural choices?" Obviously the answer is no. More curious developers might dig in their implementation but ultimately, we use the library as a black box. Give input -> expect an output -> do something with this output.
Can we treat our features as black boxes as well?
That's when I started thinking about modularity: A Module representing a feature, initialised with some inputs, able to give me outputs and expose a ViewController.
That's when the M
of MERLin' is born.
Following the idea of Module
I started exploring different design patterns to give these inputs to modules and listen to their outputs. Delegate pattern was too limiting. Multicast delegate (then multicast closure patter) was fitting better my purposes giving me the freedom to have more delegates waiting for module outputs, but then I realised that with any variant of delegate pattern, delegates were somehow connected to the module. The handshake had to happen somewhere and the delegate had to know that the delegating object was interesting for him.
Modularity is all about freedom of switching/changing/replacing modules without affecting other areas of the app, so this was killing my idea of modularity. The switch of a module would cause most likely the changes in the parts of the app where the handshake happens.
It's when I started thinking that for the sake of modularity the paradigm had to change from imperative to reactive. Modules had to emit events, and listeners were listening and reacting to events, no-matter-who was causing them to happen.
The reactive paradigm helps to have more listeners interested in the same set of events. This means that each listener can react in different ways. One listener might log the event in console while another might send it to the analytics provider(s).
Having the idea of module in place, knowing that modules would produce events and listeners would listen to them and react, it's easy to imagine of a Routing Events Listener. It should be interested in those events that should cause the routing to another viewController, and it should transform the event to a routing step.