In this series of blogs, I will discuss how to architect your android app starting from business requirements and design until we reach the development process in a scalable, maintainable, and testable way.
What makes me writing this series of blogs?
In 2019, I started to be a meetup speaker. So, I held several talks in different places. As a result of that, many android engineers began to ask me about how to improve their android level to reach seniority, and the most famous question was how to architect my app? What are the best practices, and what use-cases that I use clean architecture or MVs, for example?
So, we came up with creating an android community to help those interested and connect. We also thought of holding several meetups and talks for people to be always up to date. So, we built EGDroid. And after that, We did meetups about Clean Architecture, MVs, and Testing in Android.
In conclusion, I got an idea to create a series of blogs to discuss actual use cases that most startups now face. So, this series of blogs will simulate a use case of a founder - Christopher Nolan - who wants to establish a startup based on an android app. This android app will offer the customers a real-time platform for discovery, search, organized movies and tv shows that they want to watch and follow.
So, let’s start the journey :)
A LinkedIn Message from Christopher Nolan
Last Friday, when I checked my LinkedIn Messages in the morning, I found a message from Christopher Nolan. I said maybe I have still had this sleeping effect, and I have to get my coffee first. So, I did. Then, I rechecked my LinkedIn Messages, and I found the same thing. So, I opened the message:
I read this message more than once to be sure that what I am reading is accurate. I couldn’t believe that Christopher Nolan wants to build a startup and wants me to be his co-founder!
So, I started writing him a message to say it will be a great honor to be a founder in this startup, and I am ready to start this challenge from now:
After that, I started to plan how I can achieve that and begin working on establishing this startup.
As Chris said, he wants to make an entire movie app that people can discover, search, organize movies, and tv shows that they want to watch and follow instantly. Not only that, but they can create a watchlist to add the movies or tv shows they want to watch. Also, they can mark their favorite movie or tv show, and more.
For an MVP phase, what he wants is a demo to present the idea to the investors. This demo will be about an essential feature of making the users discover and search for any moves or tv shows they want and get the full details of a specific movie or tv show.
So, for now, we will have two main screens, maybe bottom tabs, one for showing the movies and the other for showing the tv shows. And for each movie or tv show, there is another details screen that will show the details of a specific movie or tv show.
After talking with Chris about what I will do, he wants more visualize things to understand how they will look and fill. Making a demo will help him confirm what I will do and be sure that what I will do is what exactly he wants not something else.
In this kind of case, we can use many free tools to make a demo to illustrate how the android app will look and feel. One of these tools is Sketch.
A sketch is a good tool that is easy to use and doesn’t need a lot of experience to make a demo of how the mobile app will look and feel. During my research for learning Sketch, I found this course from raywenderlich that helped me to learn more about Sketch in no time and start making the demo for Chris.
Start The Development Process
After getting the confirmation from Chris, I started working on the android app architecture by making some visualize diagrams about the android app itself like:
- What architecture pattern should I use?
- What presentation pattern should I follow?
- Am I going to make a database layer or remote network layer, or both?
And more questions came to my mind.
In the previous projects that I worked on, I usually follow SOLID principles in my development process. Also, in this project, I will follow the exact directions during my development process:
Single Responsibility Principle
- Each class has a single purpose. All its methods should relate to function.
- Each responsibility could be a reason to change a class in the future. Fewer responsibilities lead to fewer opportunities to introduce bugs during changes.
Open / Closed Principle
- Classes (or methods) should be open for extension and closed for modification. Once written, they should only be touched to fix errors.
- New functionality should go into new classes that are derived. This is popularly interpreted to advocate inheriting from an abstract base class.
Liskov Substitution Principle
- You should be able to replace an object with any of its derived classes. Your code should never have to check which sub-type it’s dealing with.
- Prevents awkward type checking and weird side-effects.
Interface Segregation Principle
- Define subsets of functionality as interfaces.
- Small, specific interfaces lead to a more decoupled system than a big general-purpose one.
Dependency Inversion Principle
- High-level modules should not depend on low-level modules. Instead, both should depend on abstractions. Abstractions should not rely on details. Details should depend upon abstractions.
- High-level modules become more reusable if they are ignorant of low-level module implementation details.
To know more about SOLID Principles, you can check out my presentation HERE starting from slide 36.
At the same time, I remembered that I read before about Software Architecture Patterns. In most of the cases, I was choosing Clean Architecture for architecting the whole android app. So, in this project, I will follow the same.
I used Clean Architecture as a Software Project Architecture, implementing it in multi-module layers. Each layer acts as a feature in the app. Inside every component / module, we implement data, domain, and presentation layers. This approach has a lot of benefits:
- More Separation of Concern
Every app module represents a simplified version of the app, with nothing else but only one particular feature.
- Faster Gradle Builds and Team Efficiency
Each team/developer only compiles the code they are currently working on. Even if other feature modules have changed, they are not in your compilation pipeline.
- Re-usability of common functionality across app / modules
The modules can even be placed in different repositories with different access rights, so you can be sure that a junior developer from another team will not accidentally add a few bugs into your module while you are on your vacations.
- Some Benefits from Micro-services Architecture
Each module can be treated like separate components containing its related database, remote calls, business logic, and presentation layer. Also, you can write one module in a Java and the other module in Kotlin.
As we can see from the diagram above that we have three main layers:
- Presentation Layer
It contains UI (Activities & Fragments) coordinated by Presenters / ViewModels that execute one or multiple use cases.
- Domain Layer
That is the most INNER part of the onion (no dependencies with other layers), and it contains entities, use cases, and repositories. Use case combines data from one or multiple list repositories.
- Data Layer
That has repository implementations and one or multiple data sources. Repositories are responsible for coordinating data from the different data sources. The data layer depends on the domain layer.
To know more about Clean Architecture, you can check out my presentation HERE.
So to illustrate more how to clean architecture will work in our interstellar movie app, the below diagram will make it easier for you:
I am going to discuss each layer bottom-up:
- LOCAL: Any logic related to store the feature data locally through Room, Realm, or whatever the third party we are going to use should initialize in this layer. Also, the local models and DAOs should initialize here as well.
- REMOTE: Any logic related to calling Remote webservices or backend APIs, integrating with Retrofit, Fuel, or whatever the third party we will use, should be initialized in this layer. Also, Remote and Response models should initialize here as well.
- DATA LAYER: Optional layer that holds the dependencies of both LOCAL and/or REMOTE layers.
- REPOSITORY: The layer that performs CRUD operations and deals with complex functions related to different data sources like REMOTE or LOCAL or both.
- USE CASE: This is the layer that handles one or more entities. It requests the data from one or more repositories and turns it back into the outer layers. Also, it prefers to encapsulate a single and specific task that can perform.
- PRESENTATION: That’s the layer that handles the user interactions and side effects that can happen. Invoke the business logic, addressing any weird behaviors resulting from use cases or repositories. Also, most of the cases, presentation layers can be Presenter in the MVP pattern or ViewModel in the MVVM pattern.
- UI: Anything related to the UI components should be in this layer. Activities, Fragments, Compound Views, Adapter, or anything related to the feature UI should be live in this layer.
- DEVICE: It contains the implementation of any related android platform stuff like Broadcast Receivers, Content Providers, Push Notifications…etc. We should implement any logic through interfaces to be easy for testing and making a default implementation that will be wrappers around android components.
- EXTERNAL: Here, I can add any external stuff that our app depends on, like Analytics, Crash reporting…etc.
For the presentation pattern, I was thinking between MVP and MVVM. According to the business requirements I have, it seems to me that I can go with MVP. I revisit the business requirements and found that implementing MVVM for the search feature will be more efficient than implementing it through MVP patter. So, I will go implementing MVVM with Unidirectional Data Flow flavor. It will make the app more scalable and maintainable for the upcoming features as well.
To make this diagram clear for you, I am going to talk more about each layer bottom-up:
- The cycling here is our user who starts to complete actions like pull to refresh, clicks on movies tab to get movie list, clicks on tv shows tab to get the tv shows list or even click on specific movie item to open its movie details.
- So, our architecture here starts to process these cycling actions inside the
ViewModelclass by calling the desired use case according to each action that the user did.
- After finishing, the use case returns some results that the cycling interested to interact. These results are processing to
emitViewStatefunction inside the
ViewModel.Then, the cycling can see the results and interact.
- As you can see, there is another thing called
SideEffect. It is more related to the things that will happen only once, like error messages, showing toasts, snack bars, or even navigating from one screen to another.
To know more about Presentation Patterns, you can check out my presentation HERE.
So, after we agreed on the software architecture and the presentation pattern that we are going to follow, I started to draw a diagram that can describe the full image of the project architecture and how these layers will interact with each other:
Working with teams can be pretty challenging if there is no process to review and check code before it merges, or more importantly, to prevent ominous code from getting to Production.
One essential tool that can collaborate with other developers hassle-free is a version control system. The most popular design is the Git.
Git is a system used to track changes in files and coordinate work on those files among multiple people; it uses for source code management in development. It is free, open-source, and handles everything from small to massive projects with speed and efficiency.
So the idea is to have two significant branches named Master and Dev which link to two environments: Staging (https://staging.exampleapi.com) and Production (https://exampleapi.com). Typically it would help if you had your Staging be a replica of your production environment.
Types of Branches :
- Master Branch is the production-ready version of our codebase, which automatically deploys to our production environment (https://exampleapi.com), with everything thoroughly tested and checked. “Thou shall not touch this branch.”
- Dev-Branch is where all feature branches marge after pull requests have checked, fixed, and all tests performed. Once all builds pass, this branch deploys to the staging environment (https://staging.exampleapi.com) for QA and UAT.
- Release Branch should be created from the Dev branch for final audit, e.g., cleanup and remove comments, versioning, etc. This branch is tagged and then merged to both Master and Dev branches.
After getting the confirmation from Chris about the features that we will have in the MVP and how the app will look and feel, we will also start the implementation process from the following blog after making the development roadmap and building the project. So, stay tuned, and let’s begin this journey together. 🤜🏻 🤛🏻