Comments 5
Я правильно понял, что теперь все зависимости на внешние типы (типа MonetaryAmount или CommandResult) фактически инлайнятся в спецификацию? А как тогда генерируются package names для них: если он одинаковый (типа ru.yandex.money.domain), то как решаете конфликты с разными версиями этого класса (например разные несовместимые версии ru.yandex.money.domain.PhoneNumber в API Notifier и API Кошелька)? Если package names разные (например ru.yandex.money.notifier.PhoneNumber и ru.yandex.money.kassa.PhoneNumber), то как решили проблему конвертации одного в другое?
Я правильно понял, что теперь все зависимости на внешние типы (типа MonetaryAmount или CommandResult) фактически инлайнятся в спецификацию ?
Если тебе хочется что бы твой тип не генерировался, а использовался уже имеющийся у тебя в classpath, то тебе достаточно указать в спецификации расширение
x-java-import-mapping: ru.yoomoney.model.domain.phone.PhoneNumber
x-java-dependency: ru.yoomoney.domain:phone
Это скажет генератору не создавай новый файлик с именем PhoneNumber.java а по всем местам использования этой схемы (PhoneNumber) возьми ru.yoomoney.model.domain.phone.PhoneNumber
Вот сгенерированного объекта запроса
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import ru.yoomoney.domain.phone.PhoneNumber;
import ru.yoomoney.codegen.platform.ApiToStringBuilder;
import ru.yoomoney.codegen.platform.Jsr303Validator;
import javax.annotation.Nonnull;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
@ApiModel(description = "Запрос отправки ОТП на номер")
public class OtpSendRequest {
@ApiModelProperty(value = "Номер телефона в формате ITU-T E.164", required = true)
@NotNull
private final PhoneNumber phone;
@ApiModelProperty(
value = "Секретный код, который получит пользователь",
example = "1234",
required = true)
@NotNull
@Pattern(regexp = "^[0-9]{4}$")
private final String secret;
@ApiModelProperty(
value = "Уникальный ID запроса в рамках приложения прикладного процесса",
example = "86463266-f8ce-4a61-a3c6-62647028c111",
required = true)
@NotNull
@Size(max = 40)
private final String requestId;
@JsonCreator
private OtpSendRequest(
@Nonnull @JsonProperty("phone") PhoneNumber phone,
@Nonnull @JsonProperty("secret") String secret,
@Nonnull @JsonProperty("requestId") String requestId) {
this.phone = phone;
this.secret = secret;
this.requestId = requestId;
}
@Nonnull
@JsonProperty("phone")
public PhoneNumber getPhone() {
return phone;
}
@Nonnull
@JsonProperty("secret")
public String getSecret() {
return secret;
}
@Nonnull
@JsonProperty("requestId")
public String getRequestId() {
return requestId;
}
@Override
public String toString() {
return ApiToStringBuilder.toStringHelper(this)
.add("phone", phone)
.add("secret", secret)
.add("requestId", requestId)
.toString();
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private PhoneNumber phone;
private String secret;
private String requestId;
public Builder withPhone(PhoneNumber phone) {
this.phone = phone;
return this;
}
public Builder withSecret(String secret) {
this.secret = secret;
return this;
}
public Builder withRequestId(String requestId) {
this.requestId = requestId;
return this;
}
public OtpSendRequest build() {
return Jsr303Validator.validate(new OtpSendRequest(phone, secret, requestId));
}
}
}
Класс ru.yoomoney.domain.phone.PhoneNumber находится в ru.yoomoney.domain:phone:x.y.z
разработчик самостоятельно должен добавить зависимость на библиотеку ru.yoomoney.domain:phone:x.y.z в build.gradle
Расширение x-java-dependency - это такая справочная информация которая поможет понять разработчику где взять недостающий класс.
Если же он забыл добавить ru.yoomoney.domain:phone:x.y.z в build.gradle то при компиляции java кода будет ошибка с тем что класс ru.yoomoney.domain.phone.PhoneNumber не найден.
Это что касается шаринга общих типов между спеками.
Если говорить про CommandResult или ApiToStringBuilder, Jsr303Validator из примера выше.
Эти классы не описаны в спецификациях, т.к. не участвуют в транспорте.
Эти классы в отличи от PhoneNumber нужны абсолютно всем т.к. сгенерированный код зависит от них.
Такие классы генератор несет с собой в ресурсах и инлайнит всегда в одно и тоже место ru.yoomoney.codegen.platform.*
Проблем их конфликтов не возникает т.к. в одном приложении подключена одна версия генератора.
Описание контракта в виде человеко-понятных схем и кодогенерация по ним — в моем представлении — это очень похоже на WSDL. Т.е. история вернулась на круги своя, или я упускаю какую-то принципиальную разницу?
В целом вы правы, описать контракт в понятном для человека языке и сгенерировать по нему код - это не новая идея. Черт кроется в деталях. Основное от WSD по моему мнению - это возможность очень точно управлять тем кодом который вы генерируется. Это позволяет вам использовать именно те технологии которые у вас есть в компании. Для маленьких проектов или стартапов все это наверное излишне. Но если вы большая компания с кучей сервисов и штатом разработчиков, такой подход сильно помогает в разработке.
Как улучшить межсерверное взаимодействие и сэкономить время разработчика