/MercariApp

A modularised Kotlin based android app with an MVVM architecture. Using databinding, Retrofit and coroutines.

Primary LanguageKotlin

Mini Mercari App

Tech Stack

Demo

Download the apk

Demo video

Architecture

The proposed solution uses a modularised approach in order to promote separation of concerns throughout the app. It uses a mixture of separation by layer and separation by feature. The following diagram shows each of the main modules in the app, and how they are connected:

App module

The main App module at the top is responsible for:

Core module

The Core module simply contains the high level business rules that are common throughout the app such as:

CoreImplementation module

The CoreImplementation module is responsible for:

  • Defining the implementations of repositories from the Core module, currently only ProductRepositoryImpl
  • Isolating dependencies on frameworks that are required to provide data, such as:
    • which http client to use (if any)
    • which database to use (if any)
    • which serialisation library to (if any)

The current implementation uses Retrofit for retrieving the product categories and subsequently products from the provided endpoint (https://s3-ap-northeast-1.amazonaws.com/m-et/Android/json/master.json). The main benefit of this approach is the ability to swap-out implementations in future if necessary, such as:

  • Switching the networking implementation to use GraphQL, GRPC, Firebase, FireStore, etc..
  • Switching the database implementation to use Realm, ObjectBox, Room, etc..

As the app would become more complex over time, both Core and CoreImplementation could be subdivided further.

FeatureCommon

The FeatureCommon module is responsible for:

  • Providing a basic ProductViewModel for a Product that may be used in any feature module
  • Providing commonly used binding adapters to other feature modules for handling:
    • Visibility of views (ViewVisibilityBindingAdapters)
    • Integration with the standard SwipeRefreshLayout (SwipeRefreshLayoutBindingAdapters) for:
      • Changing it's refreshing state
      • Styling the loading spinner
    • Loading of images into an ImageView via an abstract (non-static) binding adapter (ImageViewBindingAdapters)
      • This is abstract to keep dependencies on image loading frameworks like Glide or Picasso away from feature modules
    • Click events via an abstract binding adapter, and passing data through them such as which product was clicked.
      • This is abstract to allow the App module to decide how to handle the event, in this case to navigate to another feature screen

FeatureBrowseProducts module

The FeatureBrowseProducts module is responsible for providing the BrowseProductsFragment that hosts the UI and associated view models required to browse through products in each category.

The main view model, ProductCategoriesViewModel is responsible for:

  • Retrieving product categories via use cases
  • Exposing the loading state of retrieving categories to a ProgressBar
  • Exposing the product categories to a ViewPager

It is a Architecture components ViewModel that is scoped to the instance of the fragment's host activity. It doesn't use the API for retrieving view models directly, instead it uses Koin's ViewModel extension, which uses the standard ViewModelProvider api under the hood. The reasoning for using this is because it vastly simplifies retrieval of view models from a ViewModelProvider, and more importantly allows view model instances and their dependencies to be resolved through Koin.

The second view model, ProductCategoryViewModel is responsible for:

  • Retrieving products for a given product category via use cases
  • Exposing the loading state of retrieving a category to a SwipeRefreshLayout
  • Exposing placeholder items to a RecyclerView while loading
  • Exposing the product categories to a RecyclerView once loaded
  • Exposing a refresh mechanism used by the SwipeRefreshLayout

It is very similar in implementation to ProductCategoriesViewModel, however it is not an Architecture components ViewModel. This is because these view models are owned by the parent ProductCategoriesViewModel. Instances of ProductCategoryViewModel are also created with Koin.

Both of these view models require a repository interface implementation which is provided by the App module via Koin.

FeatureViewProduct module (bonus feature)

The FeatureViewProduct module was not part of the requirements, but simply a test of how subsequent features might interact with each other. In this case, to test navigation to another 'feature'. There is nothing special in this screen, it simply displays the product photo enlarged. Navigation to this feature screen is done through a binding adapter.

FeatureCommonImplementation module

The FeatureCommonImplementation module is responsible for for 'filling in the gaps' in the FeatureCommon module, such as providing concrete implementations of abstractions. Currently this is only providing a concrete implementation of ImageViewBindingAdapters that uses Glide (GlideImageViewBindingAdapters).

Similarly to CoreImplementation, this module could be subdivided further as the app grows over time.