Skip to content

Marker Interface

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"
InterfaceFileDetected by
ISoftDeletablesrc/Granit.Core/Domain/ISoftDeletable.csSoftDeleteInterceptor, ApplyGranitConventions()
IMultiTenantsrc/Granit.Core/Domain/IMultiTenant.csAuditedEntityInterceptor, ApplyGranitConventions()
IActivesrc/Granit.Core/Domain/IActive.csApplyGranitConventions()
InterfaceFileDetected by
IDomainEventsrc/Granit.Core/Events/IDomainEvent.csWolverine routing — local queue
IIntegrationEventsrc/Granit.Core/Events/IIntegrationEvent.csWolverine routing — transport/Outbox
InterfaceFileDetected by
IUserFriendlyExceptionsrc/Granit.Core/Exceptions/IUserFriendlyException.csGranitExceptionHandler — message exposed to client
IHasErrorCodesrc/Granit.Core/Exceptions/IHasErrorCode.csGranitExceptionHandler — error code in ProblemDetails
IHasValidationErrorssrc/Granit.Core/Exceptions/IHasValidationErrors.csGranitExceptionHandler — field errors in extensions
InterfaceFileDetected 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