Nella mia variante del modello command / query (CQRS), definisco quanto segue per un gestore di query:
public interface IQueryHandler<in TParameter, TResult> where TParameter : IQuery<TResult>
{
TResult Execute(TParameter query);
}
public interface IAsyncQueryHandler<in TParameter, TResult> where TParameter : IAsyncQuery<TResult>
{
Task<TResult> ExecuteAsync(TParameter query);
}
public interface IQueryRebuilder<TResult> where TResult : class
{
void Rebuild(TResult queryResult);
}
public interface IAsyncQueryRebuilder<TResult> where TResult : class
{
Task RebuildAsync(TResult queryResult);
}
e definisco un dispatcher di query come segue:
public interface IQueryDispatcher
{
Task<TResult> DispatchAsync<TResult>(IAsyncQuery<TResult> query);
TResult Dispatch<TResult>(IQuery<TResult> query);
Task RebuildAsync<TResult>(TResult queryResult) where TResult : class;
void Rebuild<TResult>(TResult queryResult) where TResult : class;
}
L'intento dietro IQueryRebuilder e i metodi di ricostruzione corrispondenti nel dispatcher è di gestire la situazione in cui un modello di visualizzazione MVC ha una raccolta di elementi per la costruzione delle opzioni in un elemento select. Dal momento che tali elementi non sono inclusi nel post nel server, devono essere ricostruiti nel modello quando la vista viene restituita dopo la pubblicazione a causa di un errore. Ad esempio, un gestore di query può essere definito come segue:
public class CategoryEditQuery : IAsyncQuery<CategoryEditModel>
{
public int? CategoryID
{
get;
set;
}
}
public class CategoryEditQueryHandler : IAsyncQueryHandler<CategoryEditQuery, CategoryEditModel>, IAsyncQueryRebuilder<CategoryEditModel>
{
private IStorageContext<Category> _storageCategories;
private IStorageContext<CategoryType> _storageCategoryTypes;
public CategoryEditQueryHandler(IStorageContext<Category> storageCategories, IStorageContext<CategoryType> storageCategoryTypes)
{
this._storageCategories = storageCategories;
this._storageCategoryTypes = storageCategoryTypes;
}
public async Task<CategoryEditModel> ExecuteAsync(CategoryEditQuery query)
{
CategoryEditModel model = new CategoryEditModel();
Category category;
if (query.CategoryID != null)
{
category = await _storageCategories.Entities
.AsReadOnly()
.FindByIDAsync(query.CategoryID.Value);
}
else
{
category = await _storageCategories.Entities
.AsReadOnly()
.FirstOrDefaultAsync();
}
if (category != null)
{
model.CategoryID = category.ID;
model.Category = new CategoryModel()
{
ID = category.ID,
Name = category.Name,
OriginalName = category.Name,
CategoryTypeID = category.CategoryTypeID
};
}
await RebuildAsync(model);
return model;
}
public async Task RebuildAsync(CategoryEditModel model)
{
model.Categories = await _storageCategories.Entities
.AsReadOnly()
.SelectToListAsync(x => new SelectOptionModel()
{
Value = x.ID,
Text = x.Name,
IsSelected = (x.ID == model.CategoryID)
});
if (model.Category != null)
{
model.Category.CategoryTypeItems = await _storageCategoryTypes.Entities
.AsReadOnly()
.SelectToListAsync(x => new SelectListItem()
{
Value = x.ID.ToString(),
Text = x.Name
});
}
}
}
E l'azione corrispondente del controller MVC sarà simile alla seguente:
public async Task<ActionResult> Categories(CategoryEditQuery query)
{
CategoryEditModel model = await QueryDispatcher.DispatchAsync(query);
return PartialView(model);
}
[HttpPost]
public async Task<ActionResult> Categories(CategoryEditModel model)
{
if (ModelState.IsValid)
{
CommandResult commandResult = await CommandDispatcher.DispatchAsync(model);
return new HttpStatusCodeResult(HttpStatusCode.NoContent);
}
await QueryDispatcher.RebuildAsync(model);
return PartialView(model);
}
Ho ricevuto un riscontro da un collega che se il dispatcher delle query implementa entrambi i metodi di invio e ricostruzione è una violazione del principio di responsabilità singola e che IQueryDispatcher deve essere diviso in due interfacce: una per IQueryDispatcher e una per IQueryRebuilderDispatcher. Almeno una parte del contesto per il suo feedback è quando questo viene usato per Web Api dove la ricostruzione di un modello di vista non si applica, avendo i metodi aggiuntivi sul dispatcher che non possono essere usati e non saranno usati potrebbe essere un problema. / p>
Quindi la mia domanda è se il dispatcher della query gestisce sia la distribuzione di una query sia la ricostruzione di un risultato della query violano il Principio di Responsabilità Unica e l'interfaccia del dispatcher deve essere divisa in un'unica interfaccia per la distribuzione e una per la ricostruzione?