Введение

Почти любое мобильное приложение общается с бэкендом, а значит живёт по контракту — чаще всего в виде OpenAPI схемы, которую удобно смотреть в Swagger.

image.png

Пока проект маленький, изменения в API можно отслеживать вручную. Но по мере роста схемы и команды ручная проверка становится дорогой и начинает регулярно ломаться.

Автогенерация из OpenAPI решает проблему, но в многомодульном Android-проекте всплывают нюансы: где хранить код, как не тянуть лишнее, как вписать сгенерённый код в архитектуру.

Привет! Меня зовут Дима Максимов, я Android-разработчик в Дринкит. В этом цикле статей из 2 частей я расскажу о том, как настроить генерацию из Swagger в Kotlin-код, и о том, как обуздать автогенерацию в условиях многомодульного проекта.

Схема генерации. The best way

Сгенерировать код из Swagger просто в том случае, если:

С другой стороны - есть наш проект Дринкит. И тут мы сталкиваемся с несколькими сложностями одновременно:

  1. Отдельные модули под каждую фичу: По принципам Clean Architecture, каждый наш модуль имеет разделение на -api (domain), -impl (data) и presentation. В итоге проект имеет следующую структуру:
root
├── infra
│   ├── lib_1
│   └── ...
└── contexts
    ├── common
    │   ├── cart
    │   │   ├── domain-api
    │   │   └── domain-impl
    │   └── ...
    ├── cart
    │   ├── domain-api
    │   ├── domain-impl
    │   └── presentation
    ├── product
    │   ├── domain-api
    │   ├── domain-impl
    │   └── presentation
    └── ...

И в каждом модуле примерно такая структура:

cart
├── cart-api
│   ├── build.gradle
│   └── src/main/kotlin/ru/drinkit/common/cart
│       ├── Cart.kt
│       ├── ConsumeCartRepository.kt
│       └── ... other cart related domain classes
└── cart-impl
    ├── build.gradle
    └── src/main/kotlin/ru/drinkit/common/cart
		    ├── ConsumeCartRepositoryImpl.kt
        ├── api
        │   └── CartApi.kt
        └── dto
            ├── CartDto.kt
            └── ... other cart related DTOs

То есть в конкретном модули лежат DTO модели и сетевой интерфейс только конкретного домена. Для корзины — про корзину, для карточки продукта — про карточку.

И для соблюдения нашей архитектуры, необходимо найти способ генерировать код из Swagger в модули -impl

  1. Вторая проблема — разделение ответственности даже в условиях одинаковых контекстов

Например, в блоке со структурой проекта можно заметить два одинаковых модуля cart: в common и в contexts. И это не ошибка!