Skip to content

Facade Pattern — Simplified Module API

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
        -reader : IBlobDescriptorReader
        -writer : IBlobDescriptorWriter
        -keyStrategy : IBlobKeyStrategy
        -storeProvider : IBlobStoreProvider
        -presignedUrlProvider : IPresignedUrlProvider
        -validators : IBlobValidator[]
        -clock : IClock
    }

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

    DefaultBlobStorage ..|> IBlobStorage
    DefaultBlobStorage --> IBlobDescriptorReader
    DefaultBlobStorage --> IBlobDescriptorWriter
    DefaultBlobStorage --> IBlobKeyStrategy
    DefaultBlobStorage --> IBlobStoreProvider
    DefaultBlobStorage --> IPresignedUrlProvider
    DefaultBlobStorage --> IBlobValidator
FacadeFileOrchestrated sub-components
DefaultBlobStoragesrc/Granit.BlobStorage/Internal/DefaultBlobStorage.csICurrentTenant, IBlobDescriptorReader, IBlobDescriptorWriter, IBlobKeyStrategy, IBlobStoreProvider, IPresignedUrlProvider, IBlobValidator[], IGuidGenerator, IClock
GranitExceptionHandlersrc/Granit.Http.ExceptionHandling/Internal/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);