Как выполняется маппинг объектов в DTO и какие инструменты используются
Что проверяют
Понимаете ли вы, зачем в Android-приложении разделять сетевую модель, модель базы и доменную модель, и где между ними проходят границы. Следом обычно спрашивают, в каком слое живут мапперы и что делать с null от сервера в поле, которое в домене обязательное.
Как отвечать
Опишите типичный набор моделей в Clean Architecture на Kotlin:
- DTO: то, что возвращает Retrofit после разбора JSON через kotlinx.serialization или Moshi. Повторяет контракт API, поля часто nullable;
- Entity: таблица Room со своими аннотациями и типами колонок;
- доменная модель: классы без зависимостей от Android и библиотек, с типами, удобными бизнес-логике;
- UI-модель: готовые к показу строки, форматы дат, флаги состояния экрана.
Маппинг живёт в data-слое: репозиторий получает DTO, превращает его в доменную модель и наружу отдаёт только её. Домен про DTO не знает.
Чаще всего маппинг пишут руками, функциями-расширениями вроде fun UserDto.toDomain(): User. Это явный код без магии: компилятор ловит новое обязательное поле, а тест на такую функцию идёт на JVM за миллисекунды.
Из библиотек стоит назвать две и объяснить, почему на Android их берут редко:
- MapStruct: генерирует код маппинга при компиляции, без рефлексии. Но в Kotlin работает через kapt, а kapt в режиме поддержки, и проекты переезжают на KSP вслед за Room и Hilt;
- ModelMapper и похожие: сопоставляют поля рефлексией в рантайме. Это медленно, а после R8, который переименовывает поля, ломается без правил
-keep.
Gson к маппингу DTO в домен отношения не имеет: это сериализация JSON.
Где спотыкаются
Отдают DTO прямо во ViewModel и на экран. Первое же переименование поля на бэкенде ломает интерфейс, а не только сетевой слой.
Не знают ловушку Gson с Kotlin: если у data-класса нет конструктора без аргументов, Gson создаёт объект в обход конструктора. Значения по умолчанию не применяются, а в non-null поле попадает null, и падает это далеко от места разбора.
Прячут проблему в маппере за !! или молчаливым дефолтом вроде пустой строки. Лучше решить явно: запись с битыми данными отбрасывается с логом, либо поле в домене честно nullable.