In 2017 Tiendeo mobile department faced a huge challenge: remake the app from the scratch.
Our legacy app was created on 2013. It was build with simple MVC architecture, Objective-C (of course), it used RestKit, AFNetworking and so many other pods… After years of development and a few developers, it became huge, unscalable, unstable, with massive view controllers, massive AppDelegate, some new features were coded in Swift, some refactorized part as well, so we had few Swift-Objective-C bridges, … it was a monster that ate children for breakfast.
Considering all these issues plus some changes in business orientation, the company, with the mobile department, decided to remake the app. ✨
So, while we provided minor maintenance for legacy app, we started to build the new one for both platforms, Android and iOS, at once. That fact marked the architecture that we’d use.
In my opinion, based on my experience, a modern app architecture needs to be robust, stable, scalable, testable, adaptable to changes and maintainable. I think that clean architecture provides all these things.
Basically, clean architecture structures your code in concentric layers with a dependency rule: nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in the an inner circle. That includes, functions, classes. variables, or any other named software entity.
Only one rule! That’s pretty cool! 👌 Inner layers contain business logic, middle layers contain controllers and use cases, outer layers contain UI, frameworks, DB, etc.
Structuring your code this way when any of external parts of the system become obsolete, like database or frameworks, you can replace those obsolete elements with minimum of fuss.
Some key technical benefits archived are:
- Abstraction over implementation
- Single responsibility principle
- Separation of concern
- Decoupled code
After this little introduction, let’s go back to our story!
MVP + clean architecture
We decided to apply MVP plus clean architecture using ReactiveX on both platforms. We also tried to keep same class and functions naming on both platforms.
Keep both platforms synchronized this way has been awesome for us, because when we plan new features, or discuss bugs, etc. both teams talk about same components, about same structure, almost same method names, … even if one developer wants to switch to the other platform, it’s much more easier this way.
The layers that form our architecture are:
- UI layer
- Domain layer
- Data layer
Here’s the scheme of the architecture:
Let’s dig on it:
- We use MVP for UI layer.
- We use dependency injection to inject dependencies to our Presenter, for example: use cases, custom objects, etc.
- Presenter works as a man in the middle between use cases and view controllers. It handles user interactions, launch appropriate business logic and send the response to the view controller.
- Presenter does not import UIKit classes to be more testable.
- UI layer has its own view entities different than domain entities.
- Business logic layer.
- Each use case is a reusable and independent component that executes an specific business logic.
- Domain layer does not know anything about other layers, just fetches data from a repository defined as a protocol and returns the result.
- Domain layer has its own domain entities different than view o data entities.
- It uses Repository Pattern (+info). Basically, repository pattern adds an abstraction layer over the the data sources from which the use cases get the data. With repository pattern you can query your model objects from different data sources (Core Data, Realm, web server, etc.) with a only single-entry point.
- Business logic shouldn’t know where the data comes from.
- Data layer has its own data entities different than domain entities.
Analysis of proposed architecture
- This architecture follows Clean Architecture principles.
- UI layer uses Presenters to handle presentation logic, but we could say that Clean Architecture is UI agnostic. That means we can use MVP, MVVM or anything else on UI layer.
- Domain layer is independent and reusable.
- Data layer abstract the responses from the sources.
- Every layer has its own model that works with, so UI model doesn’t have specific details of lower layers.
- Dependency injection allows us to injecting mock implementations as dependencies and make code much more testable.
- RxSwift makes communication between layers easier than to use callbacks. RxSwift also allows to combine, zip, map, etc. responses.
- Every layer is built in its own target on Xcode project. That has allowed us to export domain and data layers to other projects with no suffering.
- Following this approach is difficult, requires a good analysis skill and it’s hard when you’re a bit pushed for time, but it’s well worth when you need to maintain and scale the code and consider it as favor for future developers. 😉
During my years of experience as iOS developer I’ve worked on many projects with simple MVC architecture. All these projects failed in time following the same mistakes and bad habits: no scalable, no testable, becoming unstable, … These last years I’ve been working on Tiendeo app with this architecture and I’m very happy the way it works, the way it scales, the easy we test it, … obviously, it’s not perfect, but it’s been a huge improve.
I recommend all developers to go deep in the architecture of theirs apps, because, despite following this approach costs a little bit more effort, the benefits are terrific.
Hope you found this post interesting and useful for your projects. Any question or comment will be welcome!
Thanks and good luck!!!
In the following posts we detail other key components from our architecture: