Skip to content

Repository files navigation

ProductStore

Android-приложение магазина товаров, разработанное на Kotlin с использованием Jetpack Compose.

Проект реализован с разделением на presentation, domain и data слои. Presentation-слой построен по архитектуре TEA с использованием библиотеки KoTEA. Для работы с сетью используется Retrofit, для локального хранения и кэширования данных — Room.

Основной акцент проекта сделан на реактивной работе с данными, offline-сценариях, сохранении состояния приложения и тестировании бизнес-логики и UI.

Возможности приложения

  • просмотр каталога товаров;
  • просмотр детальной информации о товаре;
  • добавление товаров в корзину;
  • отображение количества товаров в корзине;
  • очистка корзины;
  • загрузка изображений товаров;
  • локальное кэширование каталога и детальной информации;
  • работа с ранее загруженными данными при отсутствии сети;
  • отображение состояния подключения к интернету;
  • создание напоминаний о товарах в корзине;
  • восстановление активных напоминаний после перезагрузки устройства.

Технологический стек

  • Kotlin
  • Jetpack Compose
  • Navigation Compose
  • KoTEA
  • Kotlin Coroutines и Flow
  • Hilt
  • Retrofit
  • Room
  • Kotlinx Serialization
  • Glide
  • AlarmManager
  • BroadcastReceiver
  • JUnit
  • Kaspresso
  • Compose UI Testing

Архитектура

Приложение разделено на три основных слоя:

Presentation

UI реализован на Jetpack Compose.

Presentation-слой построен по паттерну TEA (The Elm Architecture) с использованием библиотеки KoTEA.

Состояние экрана изменяется через однонаправленный поток данных:

UI Event
    ↓
ELM Update
    ↓
Command Use Case
    ↓
Repository
    ↓
Room или Retrofit
    ↓
Flow
    ↓
новое состояние UI

Такой подход позволяет явно разделять:

  • состояние экрана;
  • пользовательские события;
  • изменение состояния;
  • побочные эффекты;
  • работу с domain- и data-слоями.

Domain

Domain-слой содержит:

  • модели предметной области;
  • интерфейсы репозиториев;
  • use case'ы.

Presentation-слой не работает непосредственно с Room или Retrofit — доступ к данным осуществляется через domain-контракты.

Data

Data-слой отвечает за:

  • получение данных из сети через Retrofit;
  • работу с локальной базой Room;
  • преобразование DTO и Entity в domain-модели;
  • реализацию репозиториев;
  • кэширование данных.

Работа с сетью

Для получения данных используется Retrofit и публичный DummyJSON API.

Реализованы запросы:

  • списка товаров с пагинацией;
  • детальной информации о конкретном товаре.

Полученные сетевые DTO преобразуются в domain-модели и сохраняются в Room.

Room и кэширование

Room используется как локальное хранилище данных и основной источник данных для UI.

Для каталога используется схема:

Retrofit
   ↓
Repository
   ↓
Room
   ↓
Flow
   ↓
UI

После загрузки данных из сети они сохраняются в Room. UI получает изменения через Flow, поэтому обновляется реактивно при изменении базы.

Для детальной информации о товаре реализована проверка актуальности кэша.

Если сохранённые данные ещё актуальны, приложение использует их без дополнительного сетевого запроса.

Если кэш устарел:

  1. выполняется запрос к API;
  2. новые данные сохраняются в Room;
  3. UI автоматически получает обновлённые данные.

Если сеть недоступна, но в базе остались ранее загруженные данные, приложение продолжает отображать их с признаком устаревшего кэша.

Таким образом приложение не становится полностью непригодным при временном отсутствии сети.

Корзина

Состояние корзины также хранится в Room.

Для товаров сохраняются, в частности:

  • идентификатор;
  • название;
  • цена;
  • бренд;
  • количество;
  • состояние напоминания.

Изменения корзины поступают в presentation-слой реактивно через Flow.

Напоминания о товарах

Для товаров в корзине можно включать напоминания.

Планирование реализовано через AlarmManager.

Для каждого товара создаётся отдельный PendingIntent, после срабатывания которого BroadcastReceiver показывает соответствующее уведомление.

Информация о включённых напоминаниях сохраняется в Room.

Восстановление после перезагрузки

AlarmManager не сохраняет запланированные события после перезагрузки устройства, поэтому в приложении реализован BootCompletedReceiver.

После получения:

Intent.ACTION_BOOT_COMPLETED

receiver:

  1. получает из Room товары с активными напоминаниями;
  2. повторно планирует напоминание для каждого товара.

Таким образом активные пользовательские напоминания восстанавливаются после перезагрузки устройства.

Отслеживание состояния сети

Приложение отслеживает изменение подключения к интернету.

Для этого используется BroadcastReceiver, реагирующий на изменение connectivity state.

Текущее состояние сети передаётся в UI, где пользователь видит индикатор offline-режима.

Offline-состояние также учитывается при работе с локальным кэшем.

Dependency Injection

Для внедрения зависимостей используется Hilt.

Через DI предоставляются:

  • Retrofit API;
  • Room database и DAO;
  • реализации репозиториев;
  • use case'ы;
  • компоненты работы с напоминаниями;
  • вспомогательные зависимости.

Это позволяет отделить создание объектов от их использования и упрощает тестирование компонентов приложения.

Изображения

Изображения товаров загружаются с помощью Glide и интеграции с Jetpack Compose.

Предусмотрено отдельное состояние для случаев, когда URL изображения отсутствует.

Тестирование

Проект содержит unit- и UI-тесты.

Unit-тесты

Unit-тестами покрывается логика, не требующая запуска Android UI, включая:

  • repository-логику;
  • преобразование моделей;
  • ELM Update;
  • обработку состояний и событий.

Для тестирования coroutine-кода используется kotlinx-coroutines-test.

UI-тесты

UI-тесты написаны с использованием:

  • Kaspresso;
  • Kakao Compose;
  • Compose UI Test;
  • JUnit4.

Среди проверяемых сценариев:

  • отображение кнопки очистки для непустой корзины;
  • отсутствие кнопки очистки для пустой корзины;
  • корректное отображение количества товара;
  • отображение изображения товара при наличии URL;
  • отображение placeholder при отсутствии изображения.

Структура проекта

com.example.productsStore
│
├── data
│   ├── local
│   │   ├── dao
│   │   ├── entity
│   │   └── mapper
│   ├── remote
│   │   ├── api
│   │   ├── dto
│   │   └── mapper
│   └── repositoryImpl
│
├── domain
│   ├── model
│   ├── repository
│   └── useCase
│
├── presentation
│   ├── elm
│   │   ├── cart
│   │   ├── productDetails
│   │   └── productsList
│   └── ui
│
├── di
├── network
└── reminder

Основные технические решения

В проекте на практике реализованы:

  • однонаправленный поток данных посредством KoTEA;
  • чистая архитектура: разделение presentation-domain-data;
  • dependency injection через Hilt;
  • реактивная работа с Room через Flow;
  • Room как локальный источник данных для UI;
  • синхронизация сетевых и локальных данных;
  • проверка актуальности кэша;
  • fallback на устаревший кэш при недоступной сети;
  • обработка online и offline состояния;
  • фоновые напоминания через AlarmManager;
  • восстановление напоминаний после перезагрузки Android;
  • unit-тестирование бизнес-логики;
  • UI-тестирование Jetpack Compose через Kaspresso.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages