Composite
Definition
Section titled “Definition”The Composite pattern allows treating individual objects and compositions of objects uniformly. In Granit, this pattern manifests in the auditable entity hierarchy where each level adds capabilities while remaining uniformly manipulable.
Diagram
Section titled “Diagram”classDiagram
class Entity {
+Id : Guid
}
class CreationAuditedEntity {
+CreatedAt : DateTimeOffset
+CreatedBy : string
}
class AuditedEntity {
+ModifiedAt : DateTimeOffset?
+ModifiedBy : string?
}
class FullAuditedEntity {
+IsDeleted : bool
+DeletedAt : DateTimeOffset?
+DeletedBy : string?
}
class ISoftDeletable {
<<interface>>
}
class IMultiTenant {
+TenantId : Guid?
}
class IActive {
+IsActive : bool
}
Entity <|-- CreationAuditedEntity
CreationAuditedEntity <|-- AuditedEntity
AuditedEntity <|-- FullAuditedEntity
FullAuditedEntity ..|> ISoftDeletable
Implementation in Granit
Section titled “Implementation in Granit”| Class | File | Added capabilities |
|---|---|---|
Entity | src/Granit.Core/Domain/Entity.cs | Id (Guid) |
CreationAuditedEntity | src/Granit.Core/Domain/CreationAuditedEntity.cs | CreatedAt, CreatedBy |
AuditedEntity | src/Granit.Core/Domain/AuditedEntity.cs | ModifiedAt, ModifiedBy |
FullAuditedEntity | src/Granit.Core/Domain/FullAuditedEntity.cs | IsDeleted, DeletedAt, DeletedBy (ISoftDeletable) |
ISoftDeletable | src/Granit.Core/Domain/ISoftDeletable.cs | Soft delete marker |
IMultiTenant | src/Granit.Core/Domain/IMultiTenant.cs | TenantId isolation |
IActive | src/Granit.Core/Domain/IActive.cs | IsActive filtering |
The marker interfaces (ISoftDeletable, IMultiTenant, IActive) are
composable with the inheritance hierarchy. EF Core interceptors and query
filters detect these interfaces via reflection and apply the appropriate
behavior.
Rationale
Section titled “Rationale”The progressive hierarchy allows choosing the required audit level per
entity. A reference entity (postal code) only needs Entity. A medical
entity under ISO 27001 needs FullAuditedEntity + IMultiTenant. The
interceptors treat all entities uniformly.
Usage example
Section titled “Usage example”// Simple entity -- identity onlypublic sealed class Country : Entity { }
// Entity with creation auditpublic sealed class Invitation : CreationAuditedEntity { }
// Entity with full ISO 27001 audit + tenant isolation + GDPR soft deletepublic sealed class MedicalRecord : FullAuditedEntity, IMultiTenant{ public Guid? TenantId { get; set; } public string Diagnosis { get; set; } = string.Empty;}// Interceptors automatically populate all audit fields