При реализации проекта/схемы БД небольшой/тривиальной бухгалтерской программы возникла следующая проблема. Представим себе несколько таблиц в БД (пока не обращайте внимание на типы, можно взять за пример БД Northwind от Microsoft):
+----------------+ +------------------+ +-----------+
| Product | | OrderDetails | | Order |
+--------+-------+ +------+-----------+ +------+----+
| guid | Id |<-+ | guid | OrderId |--->| guid | Id |
+--------+-------+ | +------+-----------+ +------+----+
| string | Name | +-| guid | ProductId |
+--------+-------+ +------+-----------+
| int | Price | | int | Amount |
+--------+-------+ +------+-----------+
Так как предполагается генерация документов на основе данных из БД, то возникает логичный вопрос: а как повторно сгенерировать документ, если информация о продукте изменилась? В этом случае (как мне представляется) есть пара вариантов:
а) после генерации документа сохранять его в файловой системе (в этом случае теряется вся информация, т.е. мы не можем гарантировать, что данные не изменились со времени генерации документа);
б) сохранять всю информацию непосредственно в БД. Не будет ли в этом случае трудно поддерживать и сопровождать БД?
Как принято поступать в таких случаях?
Сейчас в голову пришёл ещё один вариант. Во все сущности, которые должны сохранять своё состояние добавляем свойство указывающее на текущее состояние ItemState (например: Draft, Active, Inactive, Deleted). Также добавляем свойство ItemCode, которое указывает на конкретную сущность (не путать с Id). Далее это можно было бы использовать так:
а) пользователь создаёт счёт-фактуру, которая ссылается на конкретные записи в БД;
б) если пользователь в дальнейшем изменяет цену товара, то в БД выполняется следующая транзакция:
В приведённом примере все используемые "старые" ссылки (например в существующих счетах-фактурах) будут ссылаться на "старые" данные. То есть основная идея в том, что сущности не изменяются в БД, а копируются. Можно ли использовать такой подход? Может быть у этого подхода есть своё название (даже не знаю как это "нагуглить")?
P.S.: не знаю поможет ли эта информация, но используется EF Core 3.1, MS SQL.
Подходы, которые упомянули в комментариях могут сработать, но не все и всегда практичны, когда нужно поддерживать именно доступ к данным в какой-то момент времени в прошлом. Т.е. да, по истории или аудиту, можно восстановить состояние, на тот момент. Но если уже какая-то часть кода реализована, то это потребует дополнительной поддержки по восстановлению этого состояния, нужно все равно идентифицировать версии и т.д.
Event sourcing выглядит хорошо, но его добавление в готовую систему, требует еще больше усилий. Без опыта использования event sourcing внедрить непросто.
Есть еще один подход, который стоит рассмотреть, и вы его частично описали в вопросе. Суть его в том, что вместо записи Order
вы храните записи Order_Version
(и так для всех сущностей). Каждая запись - неизменяемая, это может проверять триггер и просто не давать этого делать. Каждое изменение - это добавление новой записи для новой версии сущности. Это немного похоже на подход с историей, но тут настоящая неизменность (immutability) записей и все версии одинаковы, т.е. нет различия между текущей и исторической.
Обычно не требуется большого изменения кода, который использует сущности, так как в большинстве кода все равно работаем ли мы с последней или с какой-то другой версией сущности. Мы по сути всегда используем какую-то потенциально устаревшую версию (так как ее могли поменять после того, как ее прочитали). И выделение этого аспекта явно идет на пользу коду. Обычно изменения требует небольшое количество кода, который собственно меняет сущности или которому нужно явно работать с последней версией, т.е. где были какие-то уже блокировки, чтобы синхронизировать конкурентный доступ.
При таком подходе отчет будет явно привязан к конкретной версии и даже если сущность изменилась, то это никак не влияет на отчет.
Конечно есть определенные накладные расходы и плата за гибкость, а именно, при создании отчета для какой-то версии, нужно выбирать связанные сущности по дате, т.е. нужно выбирать правильные версии связанных сущностей.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Имеется ComboBox, из которого пользователь выбирает строки, которые после этого отображаются на ListBoxХочу эти выбранные элементы вывести через...
Как из MySql phpmyadmin извлечь информацию из таблицы и перенести в Label?
Кароче, у меня есть панель с паролем и я хочу в случае чего убирать вбитые цифры, но не знаю как это реализовать, пробовал через Remove, не выходило