Почти в каждом бэкенде есть скучная рутина. Вы тянете данные из внешнего источника, у вас уже лежит их копия в своей базе, и нужно обновить только те строки, которые реально изменились. Звучит просто, но на практике это превращается в стену почти одинакового кода сравнения, который никто не хочет трогать.
В этой серии разберём приём на рефлексии, который убирает эту стену целиком. Начнём с того, откуда вообще берётся боль.
Допустим, приходит событие «водитель принял заказ», и вы подтягиваете свежий профиль водителя из внешнего сервиса.
Он отдаёт маленькую структуру на три поля:
type DriverDriver struct {
Name string `json:"name"`
Number string `json:"number"`
ProfilePic *string `json:"profile_pic"`
}А ваша модель базы выглядит куда менее мило, в ней около восьмидесяти колонок:
type User struct {
ID int64 `gorm:"column:id"`
Name string `gorm:"column:name"`
Email string `gorm:"column:email"`
Number string `gorm:"column:number"`
ProfilePic *string `gorm:"column:profile_pic"`
// ... и ещё около восьмидесяти колонок
}Перезаписывать всю строку нельзя. Нужно отправить в
UPDATE только те колонки, которые отличаются, чтобы не затереть поля, о которых внешний сервис вообще ничего не знает. Первый вариант, который пишут все, выглядит так:
updates := map[string]any{}
if driver.Name != user.Name {
updates["name"] = driver.Name
}
if driver.Number != user.Number {
updates["number"] = user.Number // упс, опечатка, скопировали не ту сторону
}
if !ptrEqual(driver.ProfilePic, user.ProfilePic) {
updates["profile_pic"] = driver.ProfilePic
}Этот код работает, но плохо стареет. Каждое новое поле это ещё три строки. Указатели требуют отдельного хелпера. Кто-то по невнимательности пишет
user.Number вместо driver.Number, и вы получаете баг, который всплывёт только когда водитель сменит телефон. Помножьте это на три разных DTO и три таблицы, и поддерживать такое становится больно.В следующей части посмотрим, как переписать сравнение так, чтобы оно вообще не упоминало ни одного имени поля.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoDeep