An android app built using Kotlin that consumes TMDB API to display current trending, upcoming and popular movies🍿 and tvshows🎬. It has been built following Clean Architecture Principle, Repository Pattern, MVVM Architecture in the presentation layer as well as jetpack components.
I created this repository for a few reasons:
- To learn the approch of implementing clean architecture and SOLID principles in an android app.
- To learn libraries/tools supported by Google and most of the android development communities.
- To experiment with modularization and dynamic feature modules.
- To learn implementation of Picture-in-Picture.
Complex architectures like the pure clean architecture can also increase code complexity since decoupling your code also means creating lots of data transformations(mappers) and models,that may end up increasing the learning curve of your code to a point where it would be better to use a simpler architecture like MVVM.
So let's get started ...
A well planned architecture is extremely important for an app to scale and all architectures have one common goal- to manage complexity of your app. This isn't something to be worried about in smaller apps however it may prove very useful when working on apps with longer development lifecycle and a bigger team.
Clean architecture was proposed by Robert C. Martin in 2012 in the Clean Code Blog and it follow the SOLID principle.
The circles represent different layers of your app. Note that:
-
The center circle is the most abstract, and the outer circle is the most concrete. This is called the Abstraction Principle. The Abstraction Principle specifies that inner circles should contain business logic, and outer circles should contain implementation details.
-
Another principle of Clean Architecture is the Dependency Inversion. This rule specifies that each circle can depend only on the nearest inward circle ie. low-level modules do not depend on high-level modules but the other way around.
- Loose coupling between the code - The code can easily be modified without affecting any or a large part of the app's codebase.
- Easier to test code.
- Separation of Concern - Different modules have specific responsibilities making it easier for modification and maintenance.
-
Single Responsibility: Each software component should have only one reason to change – one responsibility.
-
Open-Closed: You should be able to extend the behavior of a component, without breaking its usage, or modifying its extensions.
-
Liskov Substitution: If you have a class of one type, and any subclasses of that class, you should be able to represent the base class usage with the subclass, without breaking the app.
-
Interface Segregation: It’s better to have many smaller interfaces than a large one, to prevent the class from implementing the methods that it doesn’t need.
-
Dependency Inversion: Components should depend on abstractions rather than concrete implementations. Also higher level modules shouldn’t depend on lower level modules.
This is the core layer of the application. The domain
layer is independent of any other layers thus ] domain models and business logic can be independent from other layers.This means that changes in other layers will have no effect on domain layer eg. screen UI (presentation layer) or changing database (data layer) will not result in any code change withing domain layer.
Components of domain layer include:
-
Models: Defines the core structure of the data that will be used within the application.
-
Repositories: Interfaces used by the use cases. Implemented in the data layer.
-
Use cases/Interactors: They enclose a single action, like getting data from a database or posting to a service. They use the repositories to resolve the action they are supposed to do. They usually override the operator “invoke”, so they can be called as a function.
The presentation
layer contains components involved in showing information to the user. The main part of this layer are the views and viewModels.
This project uses many of the popular libraries, plugins and tools of the android ecosystem.
-
Observer Pattern - The observer pattern is a software design pattern that defines a one to many dependencies between objects so that one object changes state, all of its dependents are notified and updated automatically.
-
Repository Pattern: The repository pattern is a software design pattern that restricts us from work directly with the data in the application and creates new layers for database operations, business logic, and the application's UI.
-
UseCase Pattern - UseCase pattern is a software design pattern that is used to bridge the gap between business and system models without altering requirements contents nor preempting architectural options.
-
[Koin] - Dependency Injection library.
-
- Android KTX - Provide concise, idiomatic Kotlin to Jetpack and Android platform APIs.
- AndroidX - Major improvement to the original Android Support Library, which is no longer maintained.
- Lifecycle - perform actions in response to a change in the lifecycle status of another component, such as activities and fragments.
- LiveData - lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services.
- ViewModel - designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
- Data Binding - Allows you to bind UI components in your layouts to data sources in your app using a declarative format rather than programmatically.
- Room - Provides an abstraction layer over SQLite used for offline data caching.
- Navigation Component-Component that allows easier implementation of navigation from simple button clicks to more complex patterns.
-
Retrofit - Type-safe http client and supports coroutines out of the box.
-
GSON - JSON Parser,used to parse requests on the data layer for Entities and understands Kotlin non-nullable and default parameters.
-
OkHttp-Logging-Interceptor - Logs HTTP request and response data.
-
Coroutines - Library Support for coroutines.
-
Flow - Flows are built on top of coroutines and can provide multiple values. A flow is conceptually a stream of data that can be computed asynchronously.
-
Timber-Library for easier logging.
-
Material Design - Build awesome beautiful UIs.
-
Glide- Image Library from loading images from the database and cacheing in memory.