Pull to refresh

Comments 9

В VK используете sqlx ? A jackc/pgx рассматривали?

VK большой и проектов много, я думаю в зависимости от задач используют разные библиотеки :)

В качестве драйвера используется pgx и еще заполнение таблиц большими объемами данных делается напрямую через pgx CopyFromSource.

Была идея везде использовать напрямую pgx но что-то не дошли руки, так как довольно много кода пришлось бы переписать.

Так толком и непонятно, почему автор так сильно против Query Имхо, ORM - лютое дерьмо для тех, кто не осилил чистый SQL.

Есть люди вполне владеющие чистым SQL и пишущие код с ORM, потому что кода может быть сильно меньше, а импакт по производительности может быть не настолько значимым, особенно если проект не хайлоад.

Я против Query, потому что такой код очень тяжело читать и ревьювить и в нем легко ошибиться. Вместо Query я предлагаю использовать Select или Get - так код будет гораздо чище а SQL мы будем так же как и для Query писать руками.

Очень вредный совет по "Обёрткам по запуску транзакций"

Пример некорректен, тк я сильно сомневаюсь что у вас это просто функции, скорее всего в реальном коде - это методы объявленные на структуре с названием типа repository.

Но в любом случае, из-за этих функций обёрток начинают лезть проблемы - а если требуется и юзера обновить и в user_history записать изменения, а если еще в аутбокс событие положить? Еще одна/две/три, но на 90% копипастные обёртки с доп параметрами в виде model.Outbox, model.UserHistory?

Транзакция - это частный случай unitOfWork, реализуйте его нормально на уровне сервисных классов/бизнес логики, а не на уровне репозитория - это не верный слой абстракции для этого паттерна.

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

По поводу транзакций - да, можно конечно только на уровне бизнес логики запускать транзакции если есть вообще деление на репозиторий и бизнес логику. Просто в последнее время же в моде микросервисный подход. Если весь смысл микросервиса только в том, чтобы строчку в базе сохранить и потом выдать список всех объектов, то и нет необходимости сильно на слои делить.

Пример не самый удачный.
Давайте попробую привести свой. Возможно, тоже не самый удачный, но смысл в сочетании действий.

func updateOrderStatusTx(ctx context.Context, tx *sqlx.DB, orderID int64, status Status) (Order, error) {
  var order Order
  err := dbutils.Get(ctx, tx, &order, `UPDATE orders SET status = ? WHERE id = ?`, status, orderID)
  return order, err
}

func savePaymentTx(ctx context.Context, tx *sqlx.DB, ...) (..., error) {
  // ...
}

func saveDeliveryTx(ctx context.Context, tx *sqlx.DB, ...) (..., error) {
  // ...
}

func saveOutboxMsgTx(ctx context.Context, tx *sqlx.DB, ...) (..., error) {
  // ...
}

// и тут начинается

func pay(ctx context.Context, dbh *sqlx.DB, orderID int64, payment ...) (..., err error) {
	err = dbutils.RunTx(ctx, dbh, func(tx *sqlx.Tx) error {
		_, err = savePaymentTx(ctx, tx, ...)
        _, err = updateOrderStatusTx(ctx, tx, ...)
        _, err = saveOutboxMsgTx(ctx, tx, ...)
		return err
	})

	return u, err
}

func deliver(ctx context.Context, dbh *sqlx.DB, orderID int64, payment ...) (..., err error) {
	err = dbutils.RunTx(ctx, dbh, func(tx *sqlx.Tx) error {
		_, err = saveDeliveryTx(ctx, tx, ...)
        _, err = updateOrderStatusTx(ctx, tx, ...)
        _, err = saveOutboxMsgTx(ctx, tx, ...)
		return err
	})

	return u, err
}

func payAndDeliver(ctx context.Context, dbh *sqlx.DB, ...) (..., error) {
  	err = dbutils.RunTx(ctx, dbh, func(tx *sqlx.Tx) error {
		_, err = savePaymentTx(ctx, tx, ...)
        _, err = saveDeliveryTx(ctx, tx, ...)
        _, err = updateOrderStatusTx(ctx, tx, ...)
        _, err = saveOutboxMsgTx(ctx, tx, ...)
		return err
	})

	return u, err
}

В итоге получится что под каждый вариант использования (pay, deliver, payAndDeliver, etc) будет создано по функции с около нулевой переиспользуемостью.

А если выносить транзакции в виде unitOfWork на уровень бизнес-логики/useCase, а сами запросы на уровень infrastructure/repositry, то всё очень удачно композируется.

Просто в последнее время же в моде микросервисный подход. Если весь смысл микросервиса только в том, чтобы строчку в базе сохранить и потом выдать список всех объектов, то и нет необходимости сильно на слои делить.

Поправьте если не прав, но звучит так как будто вам такой подход симпатизирует, но вы сами его не использовали.
Просто это уже нано-сервисный подход, а ля распределённый монолит.
Хочется адептам такого подхода пожелать удачи обеспечивать транзакционную целостность и тратить своё время на что-то полезное, а не на написание двух фазных коммитов и саг.

Sign up to leave a comment.