Lifecycle: Soft Delete, Restore, Purge
Purpose
The platform must support safe deletion behavior.
Normal users should not accidentally destroy business-critical asset, credential, audit, or relationship history.
Lifecycle States
The baseline lifecycle uses timestamps.
Active: deleted_at IS NULL
Soft-deleted: deleted_at IS NOT NULL
Purged: physically removedFuture states may add archival or legal hold.
Default Rule
Normal delete means soft delete.
UPDATE entities
SET deleted_at = now(), updated_at = now()
WHERE id = $1;Hard delete should be explicit and usually performed by a purge job after retention rules are satisfied.
Soft Delete Scope
Soft-deleting an entity should normally affect its subtree.
Example:
Customer
├── Site
│ └── Building
│ └── Asset
└── SiteSoft-delete customer should soft-delete:
- customer entity,
- descendant entities,
- relevant domain records,
- property values,
- tag assignments,
- group memberships where appropriate,
- relationships touching affected entities.
Soft Delete Query
Find affected subtree:
SELECT descendant.id
FROM entities target
JOIN entities descendant ON descendant.path <@ target.path
WHERE target.id = $1
AND descendant.deleted_at IS NULL;Soft Delete Transaction
Pseudo flow:
begin transaction
assert user can delete target
load target subtree ids
soft-delete relationships touching subtree
soft-delete tag assignments for subtree
soft-delete group memberships for subtree
soft-delete property values for subtree
soft-delete domain records for subtree
soft-delete entities in subtree
write audit events
commitRestore
Restore reverses soft delete.
Important question:
Should restore include all descendants automatically?Recommended behavior:
- restore subtree by default when the original delete was subtree delete,
- require parent/root to be active before restoring a child,
- do not restore records that were independently deleted before the parent delete unless you track delete batches.
Delete Batch ID
For better restore behavior, introduce a delete batch concept later.
Example:
deleted_batch_id uuid nullThis allows restore to distinguish:
Deleted because parent was deletedfrom:
Deleted independently earlierAlpha can start without this if restore requirements are simple.
Purge
Purge means physical deletion.
Purge should be rare, explicit, audited, and protected.
Recommended flow:
Soft Delete
↓
Retention Period
↓
Purge Eligibility Check
↓
Hard Delete TransactionPurge Order
Delete leaf/dependent records before entities.
Recommended order:
1. audit-safe references or event redaction where required
2. property values
3. tag assignments
4. group memberships
5. relationships
6. domain tables
7. entities
8. type/template metadata only if explicitly purging configurationBecause foreign keys use ON DELETE RESTRICT, purge logic must be deliberate.
Relationships During Delete
When an entity is soft-deleted, active relationships touching it should normally be soft-deleted.
Cases:
source in deleted subtree
target in deleted subtree
both in deleted subtreeDefault Alpha rule:
Soft-delete relationships where source or target is in deleted subtree.Groups During Delete
If a group is deleted:
- soft-delete group entity,
- soft-delete group metadata,
- soft-delete memberships.
If a member entity is deleted:
- soft-delete memberships involving that member.
Do not delete other group members.
Tags During Delete
If a tag entity is deleted:
- soft-delete tag metadata,
- soft-delete assignments using that tag.
If a tagged entity is deleted:
- soft-delete assignments for that entity.
Do not delete tag definitions just because an entity using them was deleted.
Properties During Delete
When an entity is deleted:
- soft-delete its property values.
Do not delete property definitions or property groups unless deleting configuration metadata explicitly.
Audit Events
Deletion should emit audit events.
Examples:
entity.deleted
entity.restored
entity.purged
subtree.deleted
subtree.restoredAvoid placing sensitive values in audit payloads.
Common Mistakes
Mistake: Database cascade for business delete
Do not use ON DELETE CASCADE for normal application deletion.
Mistake: Deleting only the selected row
Hierarchy means descendants must be considered.
Mistake: Restoring child under deleted parent
Restoring a child while its parent remains deleted creates confusing UI behavior.
Mistake: Forgetting relationships
Deleted entities should not appear in active graph views through stale relationships.