Provider Pattern (simple)
Abstrae el acceso a un recurso o servicio externo intercambiable, configurable en runtime.
#📋 Contexto
Tu aplicación necesita un servicio de almacenamiento de blobs (Azure Blob Storage, AWS S3, u otro) cuya implementación cambia por entorno (local, dev, prod) o por cliente (multi-tenant). El código de dominio no debería conocer los detalles de cada proveedor específico.
#🚨 Problema
- El cliente (dominio) no debería depender de implementaciones concretas de proveedores.
- Cambiar de proveedor (ej: de AWS S3 a Azure) no debe requerir cambios en la lógica de negocio.
- La configuración del proveedor debe ser flexible y centralizada.
- Tests deben poder usar fácilmente proveedores fake o mock.
#✅ Solución
Define una interfaz común (IBlobStorageProvider), implementaciones para cada proveedor, y resuelve la inyección mediante un builder fluido y DI nativo.
#Arquitectura en esta solución
ProviderPattern.Core
├── IBlobStorageProvider (interfaz base)
├── BlobStorageBuilder (builder fluido)
└── ServiceCollectionExtensions (registro en DI)
ProviderPattern.Core.AwsS3
├── AwsS3BlobStorageProvider
├── AwsS3BlobStorageOptions
└── AwsS3BuilderExtensions
ProviderPattern.Core.AzureBlobStorage
├── AzureBlobStorageProvider
├── AzureBlobStorageOptions
└── AzureBlobStorageBuilderExtensions
#🔧 Componentes Clave
#1. Interfaz Base — IBlobStorageProvider
public interface IBlobStorageProvider
{
Task<string> UploadAsync(string containerName, string blobName, Stream stream);
Task<Stream> DownloadAsync(string containerName, string blobName);
Task<bool> DeleteAsync(string containerName, string blobName);
Task<bool> ExistsAsync(string containerName, string blobName);
Task<IEnumerable<string>> ListBlobsAsync(string containerName);
}
Características:
- Define el contrato común que todos los proveedores deben cumplir.
- Métodos asincronos (operaciones I/O).
- Agnóstico de la plataforma: no menciona Azure, AWS, local, etc.
#2. Implementación Concreta — AwsS3BlobStorageProvider
public class AwsS3BlobStorageProvider : IBlobStorageProvider
{
private readonly AwsS3BlobStorageOptions _options;
public AwsS3BlobStorageProvider(AwsS3BlobStorageOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
if (string.IsNullOrWhiteSpace(options.Bucket))
throw new ArgumentException("Bucket es requerido para AWS S3");
if (string.IsNullOrWhiteSpace(options.Region))
throw new ArgumentException("Region es requerida para AWS S3");
}
public async Task<string> UploadAsync(string containerName, string blobName, Stream stream)
{
// Lógica específica de AWS S3
return $"https://{_options.Bucket}.s3.{_options.Region}.amazonaws.com/{blobName}";
}
// Resto de métodos...
}