TGViewer
Библиотека Go-разработчика | Golang Библиотека Go-разработчика | Golang @goproglib · 24.1K subscribers
Post #7334 2.97K
🧑‍💻 Сравнение двух Go-структур без хардкода полей. Часть 1, проблема

Почти в каждом бэкенде есть скучная рутина. Вы тянете данные из внешнего источника, у вас уже лежит их копия в своей базе, и нужно обновить только те строки, которые реально изменились. Звучит просто, но на практике это превращается в стену почти одинакового кода сравнения, который никто не хочет трогать.

В этой серии разберём приём на рефлексии, который убирает эту стену целиком. Начнём с того, откуда вообще берётся боль.
Допустим, приходит событие «водитель принял заказ», и вы подтягиваете свежий профиль водителя из внешнего сервиса.

Он отдаёт маленькую структуру на три поля:
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
  • 👍 10
  • 🔥 3
  • ❤ 1
More from @goproglib
  1. Sep 25, 2026🤡🤡 📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека Go-разработчика #GoGiggle
  2. Sep 25, 2026💡 Код работает. А data race уже есть В Go можно записать значение в одной горутине, прочи…
  3. Sep 23, 2026💥 TCP/IP: что происходит с данными в сети Когда Go-приложение отправляет данные по сети,…
  4. Sep 22, 2026🔴 Встроенные функции len, make, panic — встроенные функции Go, которые можно использовать…
  5. Sep 21, 2026🤩 Go с нуля: серия базовых шпаргалок Собрали путь от Hello, World! до goroutines, channel…
  6. Sep 20, 2026🤔 Вопрос с собеседования по Go Что выведет программа? ❤️ — [1 2 3 4 5] / [1 2 10] 🔥 — [1…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →