Как обрабатывать изменение типа данных поставщика, если на это нельзя повлиять
Что проверяют
Умеете ли вы изолировать систему от внешнего контракта, который меняется без вашего участия. Следом обычно просят конкретику: поставщик прислал цену строкой вместо числа, что упадёт и где вы это поймаете.
Как отвечать
Главная мысль: чужая модель данных не должна проникать дальше слоя интеграции. Отсюда каркас:
- антикоррупционный слой: отдельные DTO ровно под формат поставщика и маппер в доменную модель, руками или через MapStruct. При смене типа меняются DTO и маппер, а домен и база ничего не замечают;
- терпимый читатель: разбирать только нужные поля и игнорировать лишние. В Jackson это
@JsonIgnoreProperties(ignoreUnknown = true)или выключенныйDeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; - приём обоих форматов: собственный
JsonDeserializer, который принимает и число, и строку,@JsonAliasдля переименованного поля,ACCEPT_SINGLE_VALUE_AS_ARRAY, если одиночный объект стал массивом. Строку"123"вintJackson по умолчанию приведёт сам, а"12,50"вBigDecimalуже нет; - валидация на входе: после разбора проверить инварианты (цена положительная, валюта из списка) и отклонить запись, а не пропустить
nullдальше; - изоляция сбоев: сохранять сырой ответ, а неразобранные сообщения отправлять в отдельный топик или очередь для ручного разбора, чтобы одна битая запись не останавливала поток;
- раннее обнаружение: тесты на сохранённых примерах реальных ответов, проверка по JSON Schema, метрика и алерт на ошибки десериализации.
Если о смене формата известно заранее, поддержите оба варианта на переходный период и удалите старую ветку, когда поставщик полностью переедет.
Где спотыкаются
Используют DTO поставщика как доменную сущность или сразу как JPA-entity. Тогда смена типа одного поля расползается по всему коду и схеме базы.
Глотают ошибки разбора и подставляют значение по умолчанию. Цена 0 вместо упавшей десериализации опаснее, чем явный отказ с алертом.
Уходят в Map<String, Object> «для гибкости». Типизация пропадает, а ошибки переезжают из момента разбора в неожиданные места в виде ClassCastException.