A Marker Interface is an interface with no methods (or minimal properties) that
signals that a type possesses a specific behavior or characteristic. Framework
components detect these interfaces via reflection and apply the associated
behavior.
classDiagram
class ISoftDeletable {
<<marker>>
+IsDeleted : bool
+DeletedAt : DateTimeOffset?
+DeletedBy : string?
}
class IMultiTenant {
<<marker>>
+TenantId : Guid?
}
class IActive {
<<marker>>
+IsActive : bool
}
class IDomainEvent {
<<marker>>
}
class IIntegrationEvent {
<<marker>>
}
class IUserFriendlyException {
<<marker>>
}
class IHasErrorCode {
<<marker>>
+ErrorCode : string
}
note for ISoftDeletable "SoftDeleteInterceptor<br/>Query filter"
note for IMultiTenant "AuditedEntityInterceptor<br/>Query filter"
note for IDomainEvent "Local queue Wolverine"
note for IIntegrationEvent "Outbox Wolverine"
note for IUserFriendlyException "Message exposed to client"
Interface File Detected by ISoftDeletablesrc/Granit.Core/Domain/ISoftDeletable.csSoftDeleteInterceptor, ApplyGranitConventions()IMultiTenantsrc/Granit.Core/Domain/IMultiTenant.csAuditedEntityInterceptor, ApplyGranitConventions()IActivesrc/Granit.Core/Domain/IActive.csApplyGranitConventions()
Interface File Detected by IDomainEventsrc/Granit.Core/Events/IDomainEvent.csWolverine routing — local queue IIntegrationEventsrc/Granit.Core/Events/IIntegrationEvent.csWolverine routing — transport/Outbox
Interface File Detected by IUserFriendlyExceptionsrc/Granit.Core/Exceptions/IUserFriendlyException.csGranitExceptionHandler — message exposed to clientIHasErrorCodesrc/Granit.Core/Exceptions/IHasErrorCode.csGranitExceptionHandler — error code in ProblemDetailsIHasValidationErrorssrc/Granit.Core/Exceptions/IHasValidationErrors.csGranitExceptionHandler — field errors in extensions
Interface File Detected by IIdempotencyMetadatasrc/Granit.Idempotency/Abstractions/IIdempotencyMetadata.csIdempotencyMiddleware — enables idempotency on the endpoint
Markers allow applying cross-cutting behaviors (audit, filtering, routing)
declaratively, without coupling entities to infrastructure frameworks. An entity
implementing ISoftDeletable automatically gets soft delete and the query
filter — no additional code required.
// The entity declares its characteristics via markers
public sealed class MedicalRecord : FullAuditedEntity, IMultiTenant, IActive
public Guid? TenantId { get ; set ; } // <- IMultiTenant
public bool IsActive { get ; set ; } = true ; // <- IActive
// ISoftDeletable is inherited from FullAuditedEntity
public string Diagnosis { get ; set ; } = string . Empty ;
// The framework detects markers and automatically applies:
// - Query filter: WHERE IsDeleted=false AND IsActive=true AND TenantId=@tid
// - Audit interceptor: CreatedAt/By, ModifiedAt/By, TenantId
// - Soft delete interceptor: DELETE -> UPDATE IsDeleted=true