Посібник по м'якому видаленню та веденню журналу аудиту в Rails
У міру того як фінансові додатки стають дедалі складнішими, цілісність даних стає критично важливою. Сьогодні я поділюсь досвідом впровадження надійної системи м'якого видалення з комплексним веденням журналу аудиту у фінансовому додатку на Rails. Давайте дослідимо, як ми можемо зберігати трасування даних, при цьому забезпечуючи, що нічого не буде втрачено назавжди.
Виклик
У фінансових додатках просто видаляти записи не можна. Ми повинні:
- Відстежувати всі зміни фінансових записів
- Зберігати зв'язки даних
- Забезпечувати відновлення записів
- Гарантувати відповідність вимогам аудиту
Реалізація м'якого видалення за допомогою actsasparanoid
Гем acts_as_paranoid
забезпечує надійну основу для м'якого видалення. Ось як ми реалізували це в нашій моделі Account:
class Account < ApplicationRecord
acts_as_paranoid
belongs_to :currency, optional: true
belongs_to :user
has_many :transactions, dependent: :destroy
validates :name, presence: true
validates :balance, presence: true
end
Ми додали стовпець deleted_at
типу timestamp, який, коли він встановлений, ефективно "приховує" запис від звичайних запитів, але зберігає його в базі даних.
Розширена система ведення журналу аудиту
Ми створили комплексну систему ведення журналу аудиту, яка відстежує кожну зміну у фінансових записах:
class AuditLog < ApplicationRecord
acts_as_paranoid
belongs_to :user, optional: true
validates :class_name, presence: true
end
Журнал аудиту інтегровано в нашому сервісному шарі за допомогою базового класу сервісу:
class ApplicationService
private
def log_event(user:, data: {})
event_data = {
user: user,
data: data,
class_name: self.class.to_s
}.compact
AuditLog.create(event_data)
end
end
Відстеження транзакцій під час видалення
Для фінансових транзакцій ми зберігаємо повну трасу аудиту навіть після видалення. Ось як ми обробляємо видалення транзакції:
class Transactions::DestroyService < ApplicationService
def call
return failure([TRANSACTION_NOT_FOUND_MESSAGE]) unless transaction
return failure([USER_NOT_FOUND_MESSAGE]) unless user
ActiveRecord::Base.transaction do
transaction.destroy
update_account_balance
log_event(user: user, data: { transaction: transaction })
success(TRANSACTION_DELETED_MESSAGE)
rescue ActiveRecord::RecordInvalid => e
failure(e.record.errors.full_messages)
end
end
private
def update_account_balance
factor = transaction.transaction_type == 'expense' ? 1 : -1
transaction.account.update!(
balance: transaction.account.balance + (transaction.amount * factor)
)
end
end
Забезпечення цілісності даних
Для забезпечення цілісності даних ми реалізували кілька ключових функцій:
- Атомарні транзакції: Всі пов'язані операції обгорнуті в транзакції бази даних:
ActiveRecord::Base.transaction do
transaction.destroy
update_account_balance
log_event(user: user, data: { transaction: transaction })
end
2. Збереження зв'язків: Ми зберігаємо зв'язки між записами навіть після видалення:
class User < ApplicationRecord
acts_as_paranoid
has_many :audit_logs, dependent: :nullify
has_many :accounts, dependent: :destroy
has_many :transactions, dependent: :destroy
end
3. Автоматизоване очищення: Ми обробляємо старі м'яко видалені записи за допомогою фону роботи:
class RemoveSoftDeletedUsersJob < ApplicationJob
def perform
return unless Settings.jobs.remove_soft_deleted_users.enabled
User.deleted_before_time(eval(Settings.jobs.remove_soft_deleted_users.time).ago)
.each(&:destroy_fully!)
end
end
Реальні переваги
Ця реалізація принесла кілька переваг:
- Відповідність вимогам: Повні журнали аудиту для фінансових транзакцій
- Відновлення даних: Легке відновлення випадково видалених записів
3.
Продуктивність: Мінімальний вплив на продуктивність бази даних при збереженні цілісності даних - Обслуговування: Автоматизоване очищення старих м'яко видалених записів
Результати навчання
Створення цієї системи дало мені кілька важливих уроків:
- Завжди враховуйте ширші наслідки видалення даних у фінансових системах
- Проектуйте з урахуванням можливості аудиту з самого початку
- Баланс між збереженням даних та продуктивністю системи
- Важливість атомарних операцій для збереження консистентності даних
Кращі практики
При впровадженні подібної системи варто враховувати наступні рекомендації:
- Завжди використовуйте транзакції бази даних для пов'язаних операцій
- Логуйте як саму дію, так і стан даних
- Реалізуйте стратегії очищення старих м'яко видалених записів
- Зберігайте зв'язки між пов'язаними записами навіть після їх видалення
- Проектуйте систему аудиту так, щоб вона була зручна для запитів і легко підтримувана
Ця реалізація виявилася надійною в умовах продуктивного середовища, обробляючи мільйони фінансових транзакцій при збереженні повної трасованості та цілісності даних. Поєднання м'якого видалення та комплексного ведення журналу аудиту забезпечує безпеку та прозорість, необхідні для фінансових додатків.
Пам'ятайте, що у фінансових додатках справа не тільки у збереженні даних — важливо також зберігати перевірену історію кожної зміни при забезпеченні цілісності даних на кожному етапі.
Щасливого кодування!
Оригінал опубліковано на https://sulmanweb.com.
Перекладено з: Rails Soft Delete & Audit Logging Guide