Skip to content

Facade

The Facade pattern provides a simplified interface to a complex subsystem. It hides the complexity of interactions between multiple components behind a single, cohesive entry point.

classDiagram
    class IBlobStorage {
        +InitiateUploadAsync()
        +CreateDownloadUrlAsync()
        +DeleteAsync()
    }

    class DefaultBlobStorage {
        -currentTenant : ICurrentTenant
        -store : IBlobDescriptorStore
        -keyStrategy : IBlobKeyStrategy
        -storageClient : IBlobStorageClient
        -urlGenerator : IBlobPresignedUrlGenerator
        -validators : IBlobValidator[]
        -clock : IClock
    }

    class GranitExceptionHandler {
        -mappers : IExceptionStatusCodeMapper[]
        -logger : ILogger
    }

    DefaultBlobStorage ..|> IBlobStorage
    DefaultBlobStorage --> IBlobDescriptorStore
    DefaultBlobStorage --> IBlobKeyStrategy
    DefaultBlobStorage --> IBlobStorageClient
    DefaultBlobStorage --> IBlobPresignedUrlGenerator
    DefaultBlobStorage --> IBlobValidator
FacadeFileOrchestrated sub-components
DefaultBlobStoragesrc/Granit.BlobStorage/Internal/DefaultBlobStorage.csICurrentTenant, IBlobDescriptorStore, IBlobKeyStrategy, IBlobStorageClient, IBlobPresignedUrlGenerator, IBlobValidator[], IClock
GranitExceptionHandlersrc/Granit.ExceptionHandling/GranitExceptionHandler.csIExceptionStatusCodeMapper[], ILogger, ExceptionHandlingOptions

Without the DefaultBlobStorage facade, application code would have to manually orchestrate tenant resolution, S3 key generation, descriptor creation, presigned URL generation, and validation — on every operation. The facade encapsulates this complexity behind 3 public methods.

GranitExceptionHandler centralizes exception-to-ProblemDetails conversion (RFC 7807), hiding the mapper chain and ISO 27001 rules (masking internal details in production).

// The caller interacts with a simple API -- complexity is hidden
IBlobStorage blobStorage = serviceProvider.GetRequiredService<IBlobStorage>();
// Behind this call: tenant resolution, descriptor creation,
// S3 key generation, presigned URL, database persistence
PresignedUploadTicket ticket = await blobStorage.InitiateUploadAsync(
"avatars",
new BlobUploadRequest("photo.jpg", "image/jpeg", MaxAllowedBytes: 5_000_000),
cancellationToken);