¿Qué parte del modelo debe manejar las inserciones de la base de datos?

El título probablemente no describe muy bien mi problema, me alegraría que alguien pudiera editarlo a algo más apropiado. De todos modos:

Tengo un componente que se supone que devuelve el precio del producto dado su id. Implementa una interfaz como esta:

interface IProductPriceFetcher
{
    double GetPrice(int id);
}

Ahora, el precio se puede obtener de 3 diferente fuentes:

  • servicio web
  • directamente desde el código fuente del sitio web (desguace)
  • como alternativa final (tanto el servicio web como el sitio web son inaccesibles), se devuelve el precio más reciente de la base de datos local

Para jugar con este problema de 3 fuentes diferentes, he implementado una clase como esta:

class MainFetcher : IProductPriceFetcher
{
    public double GetPrice(int id)
    {
        var priceFetcher = this.factory.GetWebServiceFetcher()
                        ?? this.factory.GetWebsiteFetcher()
                        ?? this.factory.GetLocalDatabaseFetcher();
        return priceFetcher.GetPrice(id);
    }
}

Cada método de devolución de fábrica IProductPriceFetcher por supuesto, con una nota al margen de que los dos primeros pueden fallar y regresar null; supuse GetLocalDatabaseFetcher aunque siempre devolverá un objeto significativo.

Mi "pregunta ... general"

Después de una llamada exitosa al servicio web / sitio web, quiero que el precio obtenido se inserte en la base de datos local, como un caso de respaldo futuro. Ahora mi pregunta es: ¿qué parte del código anterior debería ser responsable de eso? ¿Debería ser uno de los buscadores web concretos que devuelva el precio? O buscador de "agregador" (MainFetcher), ya que también tiene conocimiento sobre cuál es la fuente del precio. ¿Debería plantear algún evento? ¿Inyectar otra interfaz con llamadas DB? ¿Cambiar el diseño a mejor?

¿Por qué se me ocurrió siquiera como un problema? Bueno, traté de mantener el código limpio (no se preocupe, es solo un proyecto favorito para mi tiempo libre, exactamente para resolver problemas como este) posiblemente con SRP / SoC en mente. Ahora parece que tengo problemas para cambiar de esta forma de pensar, quiero decir, ¿cómo es posible que algo que busca páginas web también esté insertando bases de datos? ¡Oh vamos! :)

preguntado el 16 de mayo de 11 a las 16:05

2 Respuestas

Si desea tener un diseño súper desacoplado, implementaría un Decorador class como la siguiente y úsela para envolver tanto WebServiceFetcher como WebsiteFetcher:

class DatabaseCachingFetcherDecorator : IProductPriceFetcher
{
    private readonly IProductPriceFetcher innerFetcher;

    public DatabaseCachingFetcherDecorator(IProductPriceFetcher fetcher)
    {
        this.innerFetcher = fetcher;
    }

    public double GetPrice(int id)
    {
        double price = this.innerFetcher.GetPrice(id);

        if (price != 0) // or some other value representing "price not found"
        {
            SavePriceToDatabase(id, price);
        }

        return price;
    }

    private SavePriceToDatabase(int id, double price)
    {
        // TODO: Implement...
    }
}

Entonces su fábrica implementaría los siguientes métodos:

public IProductPriceFetcher GetWebServiceFetcher()
{
    return new DatabaseCachingFetcherDecorator(new WebServiceFetcher());
}

public IProductPriceFetcher GetWebsiteFetcher()
{
    return new DatabaseCachingFetcherDecorator(new WebsiteFetcher());
}

Este diseño desacopla sus buscadores reales de su mecanismo de almacenamiento en caché.

EDIT: Leí un poco su diseño con esta respuesta, ya que asumí que el método GetPrice devolvería algún tipo de valor NULL si el precio no se podía recuperar, en lugar de que la fábrica devolviera un valor NULL. Creo que la fábrica que devuelve NULL huele un poco, ya que la responsabilidad de una fábrica es devolver los objetos de manera confiable. Consideraría cambiar tu GetPrice interfaz de método para devolver un double? quizás, para tener en cuenta el "precio no encontrado".

contestado el 16 de mayo de 11 a las 20:05

Me suena como si necesitaras un "Caché". El almacenamiento en caché generalmente se implementa como un tipo de aspecto o dependencia que inyecta en su Fetcher implementación. Debajo supongo IPriceCache con una especie de IDictionary interfaz, pero, por supuesto, puede insertar cualquier abstracción que necesite. También sugiero abstraer las fuentes de datos para los buscadores de precios ...:

class MainFetcher : IPriceFetcher {

 IEnumerable< IPriceSource > mSource;
 IPriceCache mCache;

 public MainFetcher( IEnumerable< IPriceSource > pSource, IPriceCache pCache )
 {
     mSource = pSource;
     mCache = pCache; 
 }

 public double GetPrice(int pID)
 {
     double tPrice;
     // get from cache
     if (mCache.TryGet(pID, out tPrice) {
         return tPrice;
     } else {
         // throws if no source found
         tPrice = mSource
             .First(tArg => tArg != null)
             .GetPrice(pID);
         // add to cache
         mCache.Add(pID, tPrice);
     }
 }
}

contestado el 16 de mayo de 11 a las 21:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.