In this topic I would like to continue spreading SOLID practices described in my previous article:
Applying SOLID principles in day to day architecture
Why clean code/architecture is so important?
but this time on a much higher level — architectural level.
What is architecture for?
We work on many apps on a daily basis. Sometimes these apps are smaller projects e.g. for a small store, sometimes these apps are big, like DAZN. As always, we need to choose a proper tool. This kind of tools are not only IDE, language or frameworks. We should consider architecture like any other tool. No architecture is still architecture as well but well planned architecture is much better because it allows us to create a better, well readable and extensible software. It also makes our life easier when we have to change our software in the future. Good plan for the app but also for specific feature is the key to success. Good news is that we can choose our architecture every day even if the project is big and developed for many years already.
Popular architectures for mobile apps
Let’s review a couple of popular mobile apps architecture (in this case for iOS app but they could be used for any other technology as well).
Model View Controller (MVC) — MVC is a default architecture for iOS apps. Every screen is splitted into three components:
- Model — where we put our business entities and application models
- View — where we show formatted data to the user
- Controller — to transform data and handle user interactions
Model View Presenter (MVP) — This architecture is very similar to MVC, but all the UI business logic is stored in the presenter. For example, we can ask presenter to show the next view in case of handle button action. Also presenter interacts with a model to fetch and parse business entities. In other words, presenter contains method called by view and makes actions on a view. In this case presenter contains reference to the view.
Model View ViewModel (MVVM) — Another similar architecture but in this case we could treat viewmodel as another side of a view abstraction. Viewmodel provides wrapper to model data that can be linked to view. Viewmodel contains command (methods) to communicate with the model and the view.
Example: View could ask for service data in case of tapping button. ViewModel is responsible for fetching the data from service, parsing, processing them and returning to the view in a proper manner.
The main difference between ViewModel and presenter is that viewmodel doesn’t know anything about the view.
Another difference is that viewmodel could be shared for other views. Presenters do not. Presenters are associated with a concrete view.
Difficult day to day choices
As you can see we have many choices. But which one is best for us? It is hard to say it, it depends on what we need.
The main rule is to know that there is going to be a situation where you need to go back to your code and change something. Extend our app for new features. It should be doable. It should not cause us pain.
What if we have an application written with MPV pattern? Should we use it everywhere then? The answer is no. We could decide that we will implement only viewmodels now, we could also decide that we will choose our architecture based on our needs. If we build simple view we could still use MVP but for more complicated use MVVM. This is also about how we could maintain and improve our code.
If an application has been created long time ago, it does not mean that we could not improve it. We should act as a catalyst for a change. We should fix broken window and make our app better. We could and we should do it always not only at the beginning of the project.
It is always a a good time to make a good decision.
Every architecture has a one common thing — separation of concers. And to be honest — this is the most important thing. The name of the architecture is not that important. Clean codebase is important regardless of how we call it.
Long time ago Uncle Bob presented a concept which is known as Clean Architecture. An experienced developer may use it on a daily basis not even knowning about it. But we could do better and standardize it.
Let’s have a look:
We have 4 different layers here. Let’s go deeper.
The highest layer are the Entities where we store our business models coming from backend (typically). We could also have some methods there but it is a good practice to keep them as POI (Plain Object Interface). These objects are not ment to be changed. Our navigation logic should not touch them as any other low level logic.
Use cases — the second layer is responsible for fetching entites from backend. Just fetching — not parsing! This could be known in our app as Services. We could communicate with server using sockets, REST or any other known way. Use cases (or services) are not affected by controlles, viewmodels etc but it could be affected if some of entites change. This scenario is known as Dependency Rule and we use pattern called Crossing Boundaries. We could cross architecture boundaries only in a specific way. Only from the top to the bottom.
Interface adapters (controllers, viewmodels, interactors, presenters) — this layer is application specific. We use them to change bussines models to applicatation models which will be used by our views later. For example, we could store our entites (formatted, processed) in a database.
UI, Views — the last and the lowest level layer is responsible for presenting our data to end user. Generally we don’t write much code here. We only use this layer to communicate with other layers from our circle.
You are not limited to only these layers. You could also add another one if you need it. You have to only keep the rule that specific layer has a specific job to do. This is what the architecture is about.
Good example how to use Clean Architecture in real life is to introduce a repository pattern.
Our repository could be a simple interface with one method eg. observeScoreboard. But it is up to you how you will implement this method. You could have RESTRepository which will download latest data from some REST service but also you can have SocketRepository that will be fetching data in real time from a websocket. Then you could decide in your view model which repository you will use depending on your need but the API will not change. As a developer you will ask for the data but the implementation details will decide on where the data should be fetched from.
As you can see, all depends on your needs. But the important thing is to keep one rule — separation of concers.
If you do things this way, you could go back to your code many times and you will still feel that you have done a good job.