Gradle: Api vs Implementation на Kotlin

Переходим на стек Gradle/Kotlin

В этой статье рассмотрим конфигурации Gradle: api и implementation.

Напишем три библиотеки. Первая — animal-api. А другие две (api-animalclient  и impl-animalclient) абсолютно одинаковы и содержат animal-api как зависимость, но посредством разных конфигураций: api и implemenation.

В главном приложении будем использовать два клиента по очереди.

Разница

api("ru.sysout:animal-api:0.0.1-SNAPSHOT")
implementation("ru.sysout:animal-api:0.0.1-SNAPSHOT")

Проверим это.

Библиотека ru.sysout:animal-api

Содержит модель данных, которую мы хотим использовать в других местах, AnimalsDto:

data class AnimalsDto(val  description: String, val animals: List<AnimalDto>)
data class AnimalDto(val id: String, val name: String)

 Библиотеки ru.sysout:api-animalclient и ru.sysout:impl-animalclient

Они абсолютно одинаковые, кроме способа включения зависимости animal-api в файлах build.gradle.kts.

api-animalclient подключает зависимость animal-api с помощью api:

api("ru.sysout:animal-api:0.0.1-SNAPSHOT")

а impl-animalclient с помощью implementation:

implementation("ru.sysout:animal-api:0.0.1-SNAPSHOT")

Обе библиотеки-клиенты содержат простой код для получения AnimalsDto (через REST).

Использование

Теперь создадим главное приложение на Kotlin/Gradle и добавим в него одну из наших библиотек-клиентов.

build.gradle.kts (главного приложения):

// Вариант 1: используем клиент, который добавил зависимость через API
 implementation("ru.sysout:api-animalclient:0.0.1-SNAPSHOT")

// Вариант 2: используем клиент, который добавил зависимость через Implementation
//implementation("ru.sysout:impl-animalclient:0.0.1-SNAPSHOT")

Попробуем использовать клиент и модель данных в сервисе AnimalService:

import org.springframework.stereotype.Service
import ru.sysout.spring_multimodule.animalapi.AnimalsDto
import ru.sysout.spring_multimodule.api.animalclient.AnimalClient
//import ru.sysout.spring_multimodule.impl.animalclient.AnimalClient либо этот

@Service
class AnimalService(val animalClient: AnimalClient) {
    fun getAnimals(): AnimalsDto?{
        return animalClient.getAnimals()
    }
}

В чем разница?

Здесь и проявляется ключевое различие:

  • С api-animalclient: Код скомпилируется успешно. Так как зависимость animal-api была объявлена через api, она транзитивно «протекает» в главное приложение. Главное приложение «видит» и может использовать класс AnimalsDto.
  • С impl-animalclient: Компилятор выдаст ошибку на строке импорта AnimalsDto. Он просто не найдет этот класс. Зависимость animal-api была объявлена через implementation и надежно скрыта внутри библиотеки impl-animalclient. Главное приложение ее не видит.

Как это исправить?

Чтобы код с impl-animalclient скомпилировался, нам пришлось бы вручную и явно добавить зависимость animal-api уже в главное приложение:

dependencies {
    implementation("ru.sysout:impl-animalclient:0.0.1-SNAPSHOT")
    // Вынуждены явно дополнительно добавить зависимость
    implementation("ru.sysout:animal-api:0.0.1-SNAPSHOT")
}

Итоги

Мы рассмотрели разницу между implementation и api в Gradle.  Рекомендуется начинать с implementation. Переходить на api нужно только тогда, когда вы уверены, что классы из этой зависимости должны быть частью публичного контракта вашей библиотеки и использоваться теми, кто ее подключает. Это правило делает сборки быстрее и предотвращает «загрязнение» classpath потребителя ненужными зависимостями.

Код трех библиотек доступен на Github (главное приложение там отсутствует, но оно может быть любым).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *