ADR 0003: Implement Providers via BaseStorage<TClient, TOptions> (Template Method)

Status

Accepted — 2025-12-15

Context

Providers share the same high-level capabilities (upload/download/streaming, metadata, deletion, and container lifecycle), and we want these behaviours to be consistent across all implementations.

Duplicating this logic in every provider increases drift and makes behaviour inconsistent.

Problem

Without a shared provider base:

Decision

Provider implementations inherit BaseStorage<TClient, TOptions>:

flowchart TD
  App[Application code] --> I[IStorage]
  I --> Base[BaseStorage&lt;TClient, TOptions&gt;]
  Base --> Impl[Provider overrides<br/>UploadInternalAsync / DownloadInternalAsync / ...]
  Impl --> SDK[Vendor SDK / API]
  SDK --> Impl --> Base --> I --> App

Alternatives Considered

  1. Implement IStorage fully per provider
    • Pros: no inheritance; explicit implementation.
    • Cons: large duplication; inconsistent behaviour across providers.
  2. Composition-based helper services
    • Pros: avoids inheritance and virtual calls in constructors.
    • Cons: more moving pieces; harder for contributors to implement new providers quickly.
  3. Source generators / partial templates
    • Pros: could reduce boilerplate further.
    • Cons: complexity and tooling overhead.

Consequences

Positive

Negative

References (Internal)