Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting data from remote service if not cached in database - suggestion needed

Tags:

In one of my current applications, I need to get customer data from a remote service (CRM) via Webservice / SOAP. But I also want to cache the data in the mysql database, so that I dont need to connect to the webservice for the next time (data does not change often, remote service is slow and has bandwidth limit - so caching is OK / a must).

I am quite sure about the technical part of this task, but I am not so sure about how to implement this clean and transparent in my web app.

All my other data comes from a single mysql database, so I repositories which return lists or single entities queried from the database with NHibernate.

My ideas so far:


1 all in one

Use a CustomerRepository, which looks for the customer by Id, if successfull, then return it, else call the webservice and save the retrieved data to the database.

Controller looks like this:

class Controller
{
    private CustomerRepository Rep;

    public ActionResult SomeAction(int id)
    {
        return Json(Rep.GetCustomerById(id));
    }
}

Repository in pseudo / simple code like this:

class CustomerRepository 
{
    public Customer GetCustomerById(int id)
    {
        var cached = Database.FindByPK(id);
        if(cached != null) return cached;

        var webserviceData = Webservice.GetData(id);
        var customer = ConvertDataToCustomer(webserviceData);

        SaveCustomer(customer);

        return customer;
    }
}

Although the above looks somewhat straightforward, I think the CustomerRepository class will grow quite large and ugly. So I dont like that approach at all.

A repository should only load data from the database, that should be its "contract" in my app at least.


2 sepereate and sticked together in the controller

Use seperate classes for the repository (db access) and webservice (remote access) and let the controller do the work:

Controller looks like this:

class Controller
{
    private CustomerRepository Rep;
    private Webservice Service;

    public ActionResult SomeAction(int id)
    {
        var customer = Rep.GetCustomerById(id);
        if(customer != null) return Json(customer);

        var remote = Service.GetCustomerById(id);
        Rep.SaveCustomer(remote);

        return Json(remote);
    }
}

Although this looks a bit better, I still dont like to put all that logic in the controller, because error handling if the service does not return data is omitted and could probably clutter things a bit more.

Maybe I could make another service layer used by the Controller, but the code would be quite the same, but in another class.

Actually I would like to have my Controller use a single Interface/Class, which encapsulates that stuff, but I dont want one class which "does it all": accessing the repository, accessing the webservice, saving the data... it feels somewhat wrong to me...



All ideas so far are / will probably become quite bloated with caching code, error handling, etc. I think.

Maybe I could clean up a few things using AOP?

How would you implement stuff like the above?

Technical frameworks used: ASP.NET MVC, Spring.NET for DI, NHibernate as ORM, mysql as database, the remote service is accessed via SOAP.

like image 716
Max Avatar asked Sep 24 '09 07:09

Max


People also ask

Is it a good idea to use database as a cache?

Benefits. Database caching improves scalability by distributing query workload from backend to multiple cheap front-end systems. It allows flexibility in the processing of data; for example, the data of Platinum customers can be cached while that of ordinary customers are not.

Which caching strategy should be used if the website frequently needs updated data?

Write-through. In a write-through cache, the cache is updated in real time when the database is updated.

What role does a caching service perform when referring to databases?

How Caching Helps. A database cache supplements your primary database by removing unnecessary pressure on it, typically in the form of frequently accessed read data. The cache itself can live in a number of areas including your database, application or as a standalone layer.


1 Answers

You can implement your architecture cleanly with the Decorator pattern.

Let us imagine that you have an interface called ICustomerRepository.

You first implement ICustomerRepository (let's call it WsCustomerRepository) based on the web service, and don't worry about the database at all in this implementation.

You then implement another ICustomerRepository (MySqlRepository) that only talks to your database.

Finally, you create a third ICustomerRepository (CachingRepository) that implements the caching logic you describe. It starts by querying the 'local' Repository, but if it doesn't get a result from that, it queries the 'remote' Repository and inserts the result in the 'local' Repository before it returns the result.

Your CachingRepository could look like this:

public class CachingRepository : ICustomerRepository
{
    private readonly ICustomerRepository remoteRep;
    private readonly ICustomerRepository localRep;

    public CachingRepository(ICustomerRepository remoteRep, ICustomerRepository localRep)
    {
        this.remoteRep = remoteRep;
        this.localRep = localRep;
    }

    // implement ICustomerRepository...
}

As you can see, the CachingRepository doesn't even have to know about the concrete web service and database, but can concentrate on its Single Responsibility: Caching

This gives you a clean separation of responsibility, and as far as your Controllers are concerned, they only talk to an ICustomerRepository instance.

like image 105
Mark Seemann Avatar answered Oct 12 '22 08:10

Mark Seemann