Comments 9
Почему нельзя хранить версии агрегатов в отдельной таблице: тип агрегата | ид агрегата | версия? Можно даже сделать эту таблицу нежурналируемой. Агрегаты ведь могут быть не персистентными и собираться из журнала действий. А команда на изменеие агрегата как раз должна хранить версию агрегата, котопую пытается изменить, чтобы сразу предупредить клиента о конфликте.
Вы пытаетесь модель предметной области материализовать один в один в инфраструктуре, что изначально выглядит порочно
Почему нельзя хранить версии агрегатов в отдельной таблице: тип агрегата | ид агрегата | версия? Можно даже сделать эту таблицу нежурналируемой. Агрегаты ведь могут быть не персистентными и собираться из журнала действий. А команда на изменеие агрегата как раз должна хранить версию агрегата, котопую пытается изменить, чтобы сразу предупредить клиента о конфликте.
Возможно я вас не так понял, но что нам это даёт? Изменение любой сущности внутри агрегата должно происходить через сам агрегат. Единственный теоретический плюс это то что в доменном объекте не будет инфраструктурного поля version. Зато за этот плюс придётся заплатить кастомной реализацией, тк из коробки оно работать не будет. Задача же была в том, чтобы автоматизировать версионность и исключить любое мануальное изменение версий.
Вы пытаетесь модель предметной области материализовать один в один в инфраструктуре, что изначально выглядит порочно
Концептуально согласен на счёт порочности, но это более широкое обсуждение, тк если использовать ORM коим Hibernate является, то ты в принципе связываешь доменную модель с моделью базы данных и инфраструктурой, иначе теряешь многие преимущества.
Тоже заметил коллизию. В домене вложенная сущность не должна знать о родителе, да. Однако ничто не мешает строить инфраструктуру способом, принятым именно для этой инфраструктуры. Аналогично, для приложения, вложенный контекст API должен иметь доступ к контексту родителя, но не родитель иметь доступ ко вложенному контексту, и дальше вглубь.
А нельзя использовать хеш от версий от частей агрегата, включая корень? Аля вычисляемое поле.
Простите, если туплю, но какой смысл от оптимистической блокировки, если используется @Transactional
?
@Transactional
, как и наличие транзакции в принципе, не решает проблему параллельных изменений данных, если только у вас не используются сериализуемые транзакции (а они, скорее всего, не используется, и не все базы данных их поддерживают) - по факту последовательное выполнение транзакций. Транзакция лишь гарантирует атомарность — либо все изменения будут сохранены, либо ни одно из них. Поэтому для любого уровня изолированности ниже сериализуемых транзакций возникают различные проблемы, которые решаются в том числе с помощью блокировок - оптимистичных или пессимистичных.
Приведу пример для уровня изолированности READ_COMMITTED (дефолтного для подавляющего большинства проектов с реляционными БД):
Предположим, у вас есть два аккаунта:
Account 1: balance 1000
Account 2: balance 1000
Два процесса параллельно пытаются изменить эти данные. Первый процесс читает баланс аккаунта 1, который равен 1000, снимает с него 100 и добавляет 100 на баланс аккаунта 2. Второй процесс также читает баланс аккаунта 1, который равен 1000, снимает с него 200 и добавляет 200 на баланс аккаунта 2 (будем считать что мы не используем атомарный инкремент/декремент).
Правильным результатом было бы:
Account 1: balance 700
Account 2: balance 1300
Но в зависимости от того, какой из процессов завершится первым, мы получим либо:
Account 1: balance 900
Account 2: balance 1100
Либо:
Account 1: balance 800
Account 2: balance 1200
Т.е. результаты одной из транзакций просто проигнорируются.
При этом с точки зрения каждой отдельной транзакции всё атомарно и правильно. Поэтому тут и нужны блокировки - либо пессимистичная (которая по факту просто не запустит вторую транзакцию пока не завершится первая), либо оптимистичная (которая отклонит вторую транзакцию и заставит её перевычитать данные, обновлённые первой транзакцией).
Причём в данном примере я описал проблему "потерянного обновления", тогда как в статье мы боремся с проблемой "ассиметрии записи", которая сложнее - она не игнорирует обновление одних и тех же объектов, но обновляя хоть и разные объекты приводит систему в невалидное состояние.
Такого количества, зачастую ненужных, англицизмов в статье я давненько не видел.
я бы предпочел увеличивать версию агрегата вручную, не люблю я магию под капотом
Оптимистичная блокировка в Hibernate если у вас DDD (и не только)