Adding a domain layer. In this article, I’ll explain how we… | by Don Turner | Android Developers | Dec, 2022

In this article, I’ll explain how we added a domain layer to the Now in Android app for improved readability, scalability and portability.

The domain layer sits between the UI and data layers

In Android app architecture, the domain layer holds business logic — the rules that dictate how the app works. For the remainder of this article, I’ll use the term “logic” to refer to “business logic”, as opposed to “UI logic” or any other form of logic.

It’s common to introduce the domain layer as an app grows in complexity, and use it to encapsulate complex logic or logic that is reused by many screen level state holders, like ViewModels.

The pull request which this article is based on is here. I’ve simplified and renamed some class names to focus on the core concepts.

The domain layer is created by moving logic, typically from the UI layer, into use cases. Use cases are functions (or classes with a single public method) which contain logic. They perform a single operation which typically combines or transforms data from repositories or other use cases.

The naming convention for use cases in this article (and in the Now in Android app) follows the official guidance of:

  • verb in present tense e.g. Get
  • noun/what e.g. FollowableTopic
  • UseCase suffix.

Example: GetFollowableTopicUseCase

Here’s an overview of the process we used:

  • Identify duplicate and complex logic inside ViewModels
  • Create appropriately named use cases
  • Move logic inside use cases
  • Refactor ViewModels to depend on use cases instead of repositories
  • Add tests for use cases

The following diagram shows the data which is observed by each ViewModel. Each box in the Observes column represents logic, typically combining data from multiple streams. Each box represents a candidate for a use case, and those with the same color indicate duplicate logic.

Diagram showing the logic contained by each ViewModel and use case candidates for that logic

The “UseCase” suffix is omitted on the diagram for readability.

Now that the hard part of “naming things” is done, we just need to move the logic from each ViewModel into its relevant UseCase. Let’s take a look at an example.

Logic to observe news articles is used in three different ViewModels.

Diagram showing duplicate logic in three ViewModels being moved into a single use case

Let’s take a look at this logic inside BookmarksViewModel:

Here’s what’s going on:

  1. Bookmarks are obtained from UserDataRepository
  2. News resources (aka articles) are obtained from NewsRepository
  3. These two are combined to create a list of bookmarked news resources
  4. The list is filtered to only show bookmarked news resources

The logic for steps 1–3 is common to all other ViewModels, so can be moved into a new use case named GetSaveableNewsResourcesUseCase. Here’s the code:

Use cases live in the domain layer, and to clearly separate this layer from the rest of our codebase we created a :domain module. Classes created by use cases, usually due to combining data models from more than one repository, were also moved into the :domain module.

A good example of this is the SaveableNewsResource data class which is a result of combining a NewsResource supplied from the NewsRepository and its isSaved property which is calculated using the list of bookmarks from the UserDataRepository.

Diagram showing data layer classes (NewsResource and UserData) being combined into a domain layer class (SaveableNewsResource).

Now we’ve created GetSaveableNewsResourcesUseCase we can refactor BookmarksViewModel to call it.

The ViewModel is now simpler, easier to read. It’s clear from the constructor what this class is doing — getting saveable news resources, and there’s no need for an intermediate bookmarks variable to store data from the user data repository.

There’s still some logic to filter out the non-bookmarked news articles. We could have moved this filtering logic to another use case (perhaps named GetSavedNewsResourcesUseCase) but creating a whole new class for one call to filter probably doesn’t justify the increased complexity of an extra use case. Ultimately, it’s up to you to decide how much logic to move into your use cases.

The other ViewModels can now be refactored to depend on our use case. Adding in any common logic, such as the ability to return news resources for a given topic or author, as we go.

We repeated this process for each area of duplicate logic:

  • pick a ViewModel
  • move the logic into a use case
  • refactor the other ViewModels to use the use case

Of course, we also added tests for every use cas, and because use cases are simple functions they’re very easy to test! Ultimately, by adding a domain layer, it’s made our codebase easier to maintain and scale.

You can read more about the domain layer in Now in Android in the Architecture learning journey, and more in depth guidance on the official Android developer website.

Next Post

Cathie Wood's ARK Joins The 2023 Cryptocurrency Market With A $5.7M Coinbase Stock Buy

Cathie Wood’s Ark Financial investment Administration has made a $5.7 million order of Coinbase inventory, according to a recent submitting with the Securities and Exchange Commission. Ark is a nicely-known investment agency that focuses on disruptive technologies, like cryptocurrency. The Coinbase order is in line with Ark’s concentration on the […]
Cathie Wood’s ARK Joins The 2023 Cryptocurrency Market With A .7M Coinbase Stock Buy

You May Like