Привет. Есть не лишенное здравого смысла мнение, что IQueryable не хило так нарушает LSP:
blog.ploeh.dk/2012/03/26/IQueryableTisTightCoupling/. Воистину, единственный провайдер linq, который «переварит» любое условие — это in-memory реализация, которая противоречит самому смыслу интерфейса, как транслятора.
При этом Repository и Specification мне нравятся еще меньше:
blog.byndyu.ru/2011/08/repository.html.
И все было бы хорошо в QueryObject, если бы я не считал, что
public class FindAccountByEmailQuery
{
private readonly ILinqProvider linqProvider;
private string email;
public FindAccountByEmailQuery(ILinqProvider linqProvider, string email)
{
this.linqProvider = linqProvider;
this.email = email;
}
public Account Execute()
{
return linq.Query()
.Where(x => x.Email == email)
.SingleOrDefault();
}
}
очень-многа-букав для такой простой вещи, как
.Where(x => x.Email == email)
.SingleOrDefault();
На одном проекте попробовал такой подход: там где нужно получать объекты по запросу: linq торчит наружу. Метод GetById, естественно стоит особняком, ввиду его частого использования. Пока linq-запросы поддаются тестированию и остаются маленькими — ничего не делаем. Как только появляются адские запросы или я использую конструкцию больше двух раз или невозможно написать тестируемый linq (используются конструкты вида SqlFunctions etc) автоматически выделяется отдельный метод.
Если методов, для получения значений по критерию становится слишком много (с учетом того, что наружу торчит linq это случается редко), методы уезжают в отдельный Repository.
В данном случае есть осознанное нарушение SRP: доменный объект может содержать как методы для получения значения по критерию так и другие операции. Я иду на это, потому что каждый доменный объект тем не менее работает только со своей сущностью. Здесь я предпочту иметь меньше классов и более простой код, чем сделать все «кошерно».
Что я имею на выходе: мало инфраструктурного кода, тестируемость, сильное связывание компонентов на уровне linq, которое меня не очень беспокоит.
Из минусов вижу то, что не для всех разработчиков в проекте может быть очевидно, когда linq стоит отрефакторить и когда вводить новый объект для запросов. Фактически формальные критерии есть, но они сложнее, чем если просто сказать: «всегда для запросов делай репозиторий, потом сервис и т.д.».
Есть ли еще какие-то минусы такого «гибридного» подхода, которые я не вижу?