如何组织代码以环绕 API 缓存系统

How to organize code to wrap around an API a cache system

我正在构建一个应用程序,它通过一个已经创建的库使用外部 API。让我们想象一下这个外部服务提供给定地点的天气信息。我们有这样的控制器:

class WeatherController
{
    public function show($place, WeatherLibrary $api)
    {
        return $api->getWeatherFor($place);
    }
}

看起来不错,但是这个 API 有每分钟的请求数限制,因此需要缓存系统。我正在考虑使用 API Laravel 提供的本机缓存。但是,为了保持我的代码井井有条,我想避免在我的控制器中使用逻辑的缓存部分,如下所示:

use Illuminate\Support\Facades\Cache;

class WeatherController
{
    public function show($place, WeatherLibrary $api)
    {
        return Cache::get($place, function() use ($place, $api) {
            $result = $api->getWeatherFor($place);
            Cache::put($place, $result, 60);
            return $result;
        });
    }
}

我应该采用什么方法来组织这个?我在考虑存储库模式,但我不太确定这是否是正确的方法,因为存储库至少具有类似 CRUD 的操作,并且此“存储库”将根据外部服务具有自定义方法业务逻辑。

根据主教的评论,您可以像这样创建一个代理 class:

class WeatherLibraryProxy
{
    /**
     * @var WeatherLibrary
     */
    protected $driver;

    public function __construct(WeatherLibrary $driver)
    {
        $this->driver = $driver;
    }


    /**
     * Catch all method calls and either serve results from the cache
     * or defer to the underlying api driver and cache the result
     */
    public function __call($method, $parameters)
    {
        $cache_key = $method . implode(',', $parameters);

        return cache()->remember(
            $cache_key,
            now()->addMinutes(60),
            function () use ($method, $parameters) {
                return $this->driver->$method(...$parameters);
        });
    }

}

现在任何共享功能(如检查剩余速率限制)都可以放入您的代理 class,您可以在您的应用程序的任何地方使用它,而不是底层的 WeatherLibrary class。

然后在您的控制器中,将 WeatherLibrary 更改为 WeatherLibraryProxy:

class WeatherController
{
    public function show($place, WeatherLibraryProxy $api)
    {
        return $api->getWeatherFor($place);
    }
}

Laravel 的服务容器应自动将 WeatherLibrary 注入代理的构造函数。如果没有,那么您可以指导 Laravel 如何在您的 AppServiceProvider.php:

中构建一个新实例
$this->app->bind(WeatherLibrary::class, function ($app) {
    return new WeatherLibrary($arg1, $arg2, ...);
});

有关自动注入的更多信息:https://laravel.com/docs/6.0/container#automatic-injection