Как изменить DTO при добавлении нового поля по требованию бизнеса
Что проверяют
Понимаете ли вы, что новое поле в DTO это изменение контракта, и кого оно может сломать. Следом обычно спрашивают, когда нужна новая версия API, а когда хватит обратно совместимого добавления.
Как отвечать
Начните с вопроса, где живёт DTO: ответ REST, тело запроса, событие в Kafka или класс в общей библиотеке. От этого зависит риск.
- поле в ответе: изменение аддитивное и обычно безопасное, если клиенты игнорируют незнакомые поля. У голого
ObjectMapperвключёнFAIL_ON_UNKNOWN_PROPERTIES, Spring Boot в своей конфигурации его выключает, но клиент может жить с другим маппером, так что это проверяют, а не предполагают; - поле в запросе: старые клиенты его не пришлют. Значит, поле необязательное или с разумным значением по умолчанию, а
@NotNullпоявляется только после того, как обновились все клиенты; - событие в брокере: при Avro и Schema Registry новое поле добавляют с
default, иначе ломается совместимость схемы; - общая библиотека: если DTO это
record, новый компонент меняет канонический конструктор, и все вызовыnewперестают компилироваться. Билдер или дополнительный конструктор смягчают удар.
Дальше по цепочке: колонка в базе миграцией Flyway или Liquibase (nullable, без тяжёлого бэкфилла), маппинг в MapStruct (с unmappedTargetPolicy = ReportingPolicy.ERROR забытое поле ловится на компиляции), спецификация OpenAPI и контрактные тесты. Новую версию /v2 заводят только под ломающие изменения: переименование, смену типа, удаление поля.
Где спотыкаются
Сразу делают новое поле обязательным во входящем DTO. Все клиенты, которые ещё не обновились, начинают получать 400.
Заводят UserDtoV2 ради одного добавленного поля. Вместо простого изменения появляются две параллельные ветки кода, которые придётся поддерживать, а потом выпиливать.
Забывают про сериализованные копии: объекты в Redis и сообщения в очереди, записанные до релиза. Новый код читает старые данные без нового поля и должен спокойно переживать там null.