dzisiaj omówię kolejny element mojego CQRSa czyli Zapytania - Query. Nadmienię jeszcze, że sam CQRS to wzorzec projektowy, a nie architektura (dzięki Norek, za uwagę żeby to podkreślić!). Xamarin jest oczywiście stworzony do MVVM - Model, Model widoku, Widok i z takiej architektury korzystam, a CQRS jest tutaj stosowany pomocniczo.
Ok, a więc Query - czyli zapytanie, które z definicji nie zmienia stanu systemu, mam dla niego przygotowany interfejs :
public interface IQuery<TResult>
{
}
Mimo że również jest to marker interface mamy już tutaj typ generyczny, który określa jaki typ danych zapytanie zwraca. Obsługujący zapytanie handler implementuje interfejs:public interface IQueryHandler<in TQuery, TResult> where TQuery : IQuery<TResult>
{
TResult Execute(TQuery query);
}
Nasz handler ma określony typ zapytania oraz typ zwracanych danych, zwracam również uwagę, że dla ścieżki zapytań nie stosuję metod async. Wynika to ze specyfiki bazy danych - SQLite mimo że ma bibliotekę w nugecie obsługującą asynchroniczne zapytania - niestety nie spełniła moich oczekiwań i pozostałem przy wersji synchronicznej. Nie ma to w moim projekcie większego znaczenia i nie widzę potencjalnych benefitów z korzystania z bazy w sposób asynchroniczny. Upraszcza to również naszą szynę zapytań, która implementuje następujący interfejs:
public interface IQueryBus
{
TResult Process<TQuery, TResult>(TQuery query) where TQuery : IQuery<TResult>;
}
Tak jak dla komend tak i tutaj pozwolę sobie zamieścić implementację mojej szyny zapytań - kiedy pisałem swojego CQRSa brakowało mi w internetowych artykułach implementacji - wszyscy skupiali się tylko na interfejsach, a szukać przykładu musiałem na githubie przeglądając projekty.
public class QueryBus:IQueryBus
{
private readonly ILifetimeScope _resolver;
public QueryBus(ILifetimeScope resolver)
{
_resolver = resolver;
}
public TResult Process<TQuery, TResult>(TQuery query) where TQuery : IQuery<TResult>
{
var queryHandler = _resolver.ResolveOptional<IQueryHandler<TQuery,TResult>>();
if (queryHandler == null)
{
throw new Exception(string.Format("No handler found for query '{0}'", query.GetType().FullName));
}
return queryHandler.Execute(query);
}
}
Implementacja szyny jest bardzo prosta - szukamy zarejestrowanego handlera i jeśli go znajdujemy to wykonujemy zapytanie i zwracamy dalej jego wynik. Żeby powielić schemat posta o komendach, tutaj również pozwolę sobie zamieścić przykładowe zapytanie i handler zapytania.
public class GetBeeHivesOnApiary :IQuery<List<BeeHive>>
{
public int ApiaryId { get; protected set; }
public GetBeeHivesOnApiary(int apiaryId)
{
this.ApiaryId = apiaryId;
}
}
public class GetBeeHivesOnApiaryHandler :IQueryHandler<GetBeeHivesOnApiary, List<BeeHive>>
{
protected SQLiteConnection _database;
public GetBeeHivesOnApiaryHandler(SQLiteConnection database)
{
_database = database;
}
public List<BeeHive> Execute(GetBeeHivesOnApiary query)
{
return _database.Query<BeeHive>("SELECT * FROM tb_beehive WHERE bh_ap_id = " + query.ApiaryId.ToString());
}
}
OK, mamy więc omówioną z przykładem ścieżkę jaką wędrują w systemie zapytania, do przeczytania jutro - omówimy kolejny element czyli zdarzenia - Events.
0 komentarze:
Prześlij komentarz