How to organize your mobile projects in an easy and flexible way using 4 layers
Organizing projects was a big mess in my past experience, with a lot of unanswered questions:
When is too much code in a class?
In which component should I put the API logic?
How do I test this massive view controller?
How can I continue development now that the web service is down?
How can I prevent a new UI change breaking my entire code structure?
And so on.
In this post I want to explore a way that made most sense for so far in iOS development.
We will explore how to group the components, what benefits it has and when to apply it.
How to group components?
Short answer: Domain — Use Cases — Presentation — Platform
This layering structure is based on Robert Martin’s (Uncle Bob’s) work on the clean architecture (http://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
Domain layer, it contains all data models of your app. This is the core of your app and should be guarded well from outside dependencies, like API & UI details. Here you find mostly simple structures like User, Feed, Item relationships between team and other representations of your domain.
On top of the Domain we add the layer of Use Cases which makes up the features of your app, like LoginUser, FetchFeed, CreateNewAccount, DeletePost and so on. This Use Case layer is depending on the entities and is making up the flows of your app. Beside the Use Case components, we need also some interfaces to get to the entities, which will be later implemented by components like API or DB or even mocks. I’d like to call them Entity Gateways.
Next comes the Presentation layer.
Here we host mostly the logic of your Presenter, from MVP or your ViewModel from MVVM.
These are components linking the events of your app (user interactions, notifications, app events…) with use cases. Here we will find the states or as Apple puts it, “The Source of Truth”. Most of the times, each screen has its own presenter component to handle the events and the state of the screen. I see presenters as the screen but without the UI. We never import UIKit in this layer to prevent it from being coupled with the UI.
Last we have the Platform layer which for iOS projects, would be most of the times a UI. But think big and imagine that you could have a watchOS or tvOS app or a SwiftUI layer.
I prefer to keep it named as Platform since I will be adding here all the dependencies which are closely linked to the Platform, beside UIKit or SwiftUI, also CoreData, MapKit, CoreLocation or CloudKit.
In the platform layer is all the coupling taking place. We have the AppDelegate (or SceneDelegate or SwiftUI App… ) which will create the first view with its presentation component. At this point we inject also the dependencies inside the presentation layer, like CoreData, API by making concrete implementations of our Entity Gateways.
What are the benefits of taking care of so many structures and layers?
For one, we have an overview of our project and an easier way to understand the domain, what use cases it has to offer, mostly just by looking at its file names. It’s also way easier to find a specific component in a debug session or when trying to implement new features or make changes to existing features.
These layers can be organized in Groups/Folders or in a separate module like a Framework or Swift Package.
Reusability is a key benefit, since these use cases are platform agnostic, as long as you have another app with a similar Login use case, you can share this component easily.
Testability is a given at this point, having all the separations. I like to create unit tests for Use Cases and the presentation components. These will be easy to create, adding mocksstubsspies.
Separation of responsibilities becomes easier as we have a set of rules & examples on what logic goes in which layer.
Massive ViewControllersViews or massive VMPresenters are pretty much eliminated when we take care of separating the app in these types of layers.
Remove dependency on web service APIs. I love this benefit, since it has affected my projects most of the times. The struggle with API changes is no joke. It can create a lot of tension between the backend them and the mobile team. Luckily we have a solution which will benefit the entire team. If we keep our entities from the domain layer clean from any API details, we make the remote APIs implement our entity gateways and conform to the needs of our apps. Practically we create a bridge which maps the API response to our domain models. In this way, when a change in the API has been made, we will know where to look for and how to fix it, without making changes to our domain layer.
Having this separation, you could even start to create gateways implementations with happy flows for demo purposes or development.
Flexibility is important; as this is not a template solution, such as VIPER. In a big enterprise project I can imagine having many more layers and in a MVP app probably 2 or 3. This should be a team decision and grow naturally as complexity increases in your project.
We have explored the Domain — Use Case — Presentation — Platform layering to organize the project’s components.
As we have seen, there are a lot of benefits having this organization system. The effort shouldn’t be too high. There are more files to be created, but the code quantity is almost the same as in a Massive View Controller app.
I’d like to hear from you:
- How do you like to organize your app?
- Do you have a test suite in your app? And have you struggled creating unit tests?
- What layer would you like us to explore in more depth