Мы используем архитектуру CQRS в нашем приложении, то есть у нас есть ряд классов, реализующих ICommand
, и для каждой команды есть обработчики: ICommandHandler<ICommand>
. То же самое относится к поиску данных - у нас есть IQUery<TResult>
с IQueryHandler<IQuery, TResult>
. Довольно часто в эти дни.Недействительность кэша в приложении CQRS
Некоторые запросы используются очень часто (для нескольких выпадающих страниц на страницах), и имеет смысл кэшировать результат их выполнения. Таким образом, у нас есть декоратор вокруг IQueryHandler, который кэширует выполнение запросов.
Запросы реализуют интерфейс ICachedQuery
, а декоратор кэширует результаты. Например:
public interface ICachedQuery {
String CacheKey { get; }
int CacheDurationMinutes { get; }
}
public class CachedQueryHandlerDecorator<TQuery, TResult>
: IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
private IQueryHandler<TQuery, TResult> decorated;
private readonly ICacheProvider cacheProvider;
public CachedQueryHandlerDecorator(IQueryHandler<TQuery, TResult> decorated,
ICacheProvider cacheProvider) {
this.decorated = decorated;
this.cacheProvider = cacheProvider;
}
public TResult Handle(TQuery query) {
var cachedQuery = query as ICachedQuery;
if (cachedQuery == null)
return decorated.Handle(query);
var cachedResult = (TResult)cacheProvider.Get(cachedQuery.CacheKey);
if (cachedResult == null)
{
cachedResult = decorated.Handle(query);
cacheProvider.Set(cachedQuery.CacheKey, cachedResult,
cachedQuery.CacheDurationMinutes);
}
return cachedResult;
}
}
Было дебаты о том, следует ли иметь интерфейс по запросам или атрибуту. Интерфейс в настоящее время используется, потому что вы можете программно изменить ключ кеша в зависимости от того, что кэшируется. То есть вы можете добавить идентификатор сущности в кэш-ключ (т. е. иметь такие ключи, как «person_55», «person_56» и т. д.).
Вопрос, конечно же, заключается в недействительности кеша (именования и недействительности кэша, а?). Проблема в том, что запросы не совпадают друг с другом с командами или сущностями. И выполнение одной команды (т. Е. Модификации записи человека) должно отображать недействительные множественные записи кеша: запись человека и выпадающие имена людей.
На данный момент у меня есть несколько кандидатов для решения:
- есть все ключи кэша, записанные как-то в классе сущностей, отмечающий объект как
ICacheRelated
и вернуть все эти ключи как часть этого интерфейса. И когда EntityFramework обновляет/создает запись, получите эти ключи кеша и аннулирует их. (Hacky!) - Команды должны аннулировать все кеши. Вернее,
ICacheInvalidatingCommand
, который должен вернуть список ключей кеша, которые должны быть недействительными. И у вас есть декоратор наICommandHandler
, который приведет к аннулированию кеша при выполнении команды. - Не аннулировать кеши, просто установите короткие периоды времени кеширования (как короткий?)
- Магия бобы.
Мне не нравится ни один из вариантов (возможно, кроме номера 4). Но я думаю, что вариант 2 - это тот, который я отдам. Проблема с этим, создание кеш-ключа становится беспорядочным, мне нужно иметь общее место между командами и запросами, которые умеют генерировать ключи. Другая проблема заключалась бы в том, что будет слишком легко добавить другой кешированный запрос и пропустить часть недействительной команды (или не все команды, которые должны быть недействительными, будут аннулированы).
Любые лучшие предложения?
Почему у вашего 'CachedQueryHandlerDecorator' есть ограничение общего типа' где TQuery: ICachedQuery'? – Steven
мм .. никогда, хотя и не уверен, что регистрация Autofac будет изменена. Я попробую – trailmax
Ой, извините. Я не знал, что вы используете Autofac. Неважно, вы не можете сделать это с помощью Autofac. – Steven