The architecture has a lot of mapping between different model objects. Let’s look at this visually first:
Let’s start from the middle. Our business logic is performed by Interactors, and they’ll perform this using domain models. These are pure Kotlin data classes for the most part, preferably with all immutable properties. Going both upward and downwards from this layer, we’ll be mapping to other models, and here’s why.
Data sources get their own model objects to use internally. Their interfaces towards Interactors don’t expose these models, only domain models and primitives, but they’ll map to and from their own data models internally. These might be similar to the domain models a lot of the time, but they might occasionally have the data in a different format, or for example, be annotated with various library specific annotations (
@Json), or inherit from library specific base classes (
RealmObject). Having separate models here ensures that our business logic doesn’t directly depend on these libraries.
In the other direction, we have Presenters. These map the domain models to screen specific presentation models. Why are these necessary then? Again, this isn’t just mapping for the sake of mapping. Domain models might be in different formats than what we need to show on screen, and there may not even be a 1-to-1 mapping between the domain models and items that show up on the UI. This additional mapping is also an opportunity to, for example, perform localization on data before presenting it.
Presentation models should be in a format that is readily displayable on the UI, without any more formatting or data processing required. Essentially, we want all of UI manipulation code to be as simple as this:
authorNameText.text = viewState.author.name
Finally, why do this in the Presenter? For one, they’re screen specific, and different presentation models are often needed per screen, as there’s little chance that two screens display the exact same set of data in the exact same format. Plus, as we’ve seen before, all Presenter methods run on the IO threadpool, so this mapping isn’t blocking the UI thread (like it would if we did this in ViewModels).
For more why mapping is important, read Do you even map though? Data model mapping in Android Apps by Joe Birch, which covers these ideas really well.
You may also want to take a look at the mapping code style section.