التدقيق والحذف الناعم
ما هو
نمطا بيانات عابران للقطع مبنيان على أصناف الكيان الأساسية:
- تسجيل التدقيق — تُسجَّل لحظات العمل المهمة كصفوف
audit_eventsغير قابلة للتغيير. هذا انتقائي ويُستدعى يدويًا، لا ظلٌّ تلقائي لكل كتابة. - الحذف الناعم — لا تُزال معظم الكيانات فيزيائيًا أبدًا؛ بل تحمل علم
IsDeleted، ويخفي مرشّح الاستعلام العام الصفوف المحذوفة.
حالة الصفحة partial بسبب نصف التدقيق: فهو حيّ لكنه يغطّي فقط الكتابات التي تستدعيه صراحةً.
لماذا بُني بهذه الطريقة
التدقيق اشتراكي بالتصميم. بدلًا من معترض EF SaveChanges يعكس كل تغيير، تستدعي الخدمات IAuditLogger.LogAsync عند النقاط المهمة (نشر، اعتماد، تغيير إعداد). يُبقي هذا صفوف التدقيق ذات معنى — حقائق عمل، لا سجل تغييرات على مستوى الصف — وكان القرار المتعمَّد (الورقة 01 §how-it-works، MIQ005_Report.md §1). المقايضة، والشيء الذي يجب ألّا يُخطأ فيه: كتابة بلا استدعاء صريح لا تُدقَّق.
AuditEvent ملحَق-فقط وعمدًا ليس AuditableEntity — id فيه bigint للحجم، ولا تحديث/حذف فيه، ولا حذف ناعم، ولا RowVersion. صفوف التدقيق حقائق غير قابلة للتغيير (الورقة 01 §decisions، AuditEvent.cs، MIQ005_Report.md §1). الملحَق-فقط حاليًا مُنفَّذ على مستوى التطبيق فقط — لا يوجد بعد دور INSERT-only على مستوى قاعدة البيانات (مؤجّل لما بعد MVP، MIQ005_Report.md §6.3).
الحذف الناعم يحفظ التاريخ والسلامة المرجعية: حذف مستخدم، مثلًا، لا يمزّق مسار تدقيقه (مفتاح actor_user_id الأجنبي هو ON DELETE SET NULL، مع actor_username مُزال التطبيع ليبقى) (الورقة 01 edge-cases، MIQ105…cs:63-69).
كيف يعمل
التدقيق
- يكتب
AuditLogger.LogAsyncصفّaudit_eventsواحدًا ويبتلع الإخفاقات — لا يحجب التدقيق عملية العمل أبدًا (الورقة 01 §rules،AuditLogger.cs:30-65). - يُستدعى يدويًا من خدمات فردية؛ ~30 خدمة تحقن
IAuditLogger. لا يوجد معترض SaveChanges للتدقيق (الورقة 01 §build-status). - عقد الكتابة: BU =
tenant.BusinessUnitId؛ يُعيَّن المُنفِّذ افتراضيًا إلىtenant.UserId/tenant.Username ?? "system"؛ IP + الارتباط منHttpContext؛detailsمُسلسَل إلى JSON (AuditLogger.cs:30-65). - تتضمّن حقول
AuditEvent:event_type،entity_type،entity_id،action،actor_user_id?(FK→users،ON DELETE SET NULL)،actor_username،occurred_at،correlation_id?،reason?،details jsonb?،severity(varchar(20)حر، افتراضيًا "Info") (الورقة 01 §entities،AuditEvent.cs:3-20).
الحذف الناعم
- يحمل
AuditableEntityالأساسيIsDeleted،DeletedAt?،DeletedBy?، إضافةً إلىRowVersion(التزامن المتفائل) وIsActive(الورقة 01 §entities،AuditableEntity.cs:3-15). - مرشّح الاستعلام العام هو
!IsDeleted && (BusinessUnitId == t.BusinessUnitId || t.IsSuperAdmin)— فتكون الصفوف المحذوفة ناعمًا غير مرئية للقراءات العادية (الورقة 01 §rules،ManpowerIQDbContext.cs:124-319). الحذف الناعم هو نصفIsDeleted؛ وعزل المستأجر هو الآخر (انظر تعدّد المستأجرين). - قيود الفرادة مرشَّحة على
is_deleted = false، فيمكن إعادة استخدام رمز بعد حذف حامله ناعمًا (الورقة 01 §entities، مثلًاPermission.codeUNIQUE مرشَّح).
مزالق / قيود
- التدقيق انتقائي/يدوي — لا تدّعِ "كل تغيير يُدقَّق." لا يوجد معترض؛ كتابة غير مُدقَّقة صامتة (الورقة 01 MUST-NOT #3). كتابة جديدة ينبغي تدقيقها يجب أن تستدعي
IAuditLoggerصراحةً. AuditEventلا يرثAuditableEntity— لاIsDeleted، ولاRowVersion، ولاIsActive، ولاCreatedBy؛idهوbigint(الورقة 01 MUST-NOT #5).- الحذف الناعم والحالة مختلفان. إلغاء
LeaveRequestيضبطStatus = Cancelled، لاIsDeleted؛ الحذف الناعم محجوز لإزالة المسؤول/البيانات الشخصية PII (الورقة 01 edge-cases،ManpowerIQDbContext.cs:296-300). severityنص حر، لا تعداد. تُدرِج الوثائق "Info/Warning/Critical" لكن "Error" مستخدمة في الممارسة (الورقة 01 discrepancies،AllocationRuleEngine.cs:17).RowVersion(bytea) يحتاج افتراضيًا من جهة Postgres'\x'::bytea؛ توجد سلسلة كاملة من هجرات "RowVersionDefault" لأن مسارات الإدراج الأقدم فشلت دونه (الورقة 01 edge-cases،UserConfiguration.cs:29-31).
حالة البناء
- الحذف الناعم + نمط الفهرس الفريد الجزئي — Available، عام عبر صفوف
AuditableEntity(الورقة 01 §build-status). - تسجيل التدقيق — Partial: حيّ لكنه انتقائي/يدوي (لا معترض لكل الكتابات)؛ الملحَق-فقط مُنفَّذ على مستوى التطبيق فقط، لا دور INSERT-only على مستوى قاعدة البيانات بعد (الورقة 01 §build-status،
MIQ005_Report.md §6.3).
ذات صلة
- تعدّد المستأجرين — علم الحذف الناعم هو نصف مرشّح الاستعلام نفسه.
- Clean Architecture — لماذا التدقيق استدعاء صريح، لا سلوك خطّ أنابيب.
- ورقة الحقائق: 01 (الأساس).