2
votes

I Use a CQRS thin read layer to provide denormalized lists/reporting data for the UI.

In some parts of my application I want to provide a search box so the user can filter through the data.

Lucene.NET is my full text search engine of choice at the moment, as I've implemented it before and am very happy with it.

But where does the searching side of things fit in with CQRS?

I see two options but there are probably more.

1] My Controller can pass the search string to a search layer (Lucene.NET) which returns a list of ID that I can then pass to the CQRS read layer. The read layer will take these IDs and assemble them into a WHERE ID IN (1,2,3) clause, ultimately returning a DataTable or IEnumerable back to the controller.

List<int> ids = searchLayer.SearchCustomers("searchString");
result = readLayer.GetCustomers(ids);

2] My thin read layer can have searching coded directly into it, so I just call

readLayer.GetListOfCustomers("search string", page, page1);
2

2 Answers

1
votes

Remember that using CQRS doesn't mean that you use it in every part of your application. Slicing an application into smaller components allows using various architectural principles and patterns as they make sense. A full text search API might be one of these components.

0
votes

Without knowing all the details of your application, I would lean towards a single entry point from your controller into your search layer (your option #2 above). I think your controller shouldn't know that it needs to call layer #1 for full-text enabled searching and layer #2 for just regular WHERE clause type searching.

I would assume you would have two different 'contexts' (e.g. SQLContext and LuceneContext), and these would be dependencies injected into your ReadLayer. Your read layer logic should then make the decision on when to use LuceneContext and when to use SQLContext; your controller won't know and shouldn't know.

This also allows you to swap out LuceneContext for MongoContext if there's a compelling reason in the future without your controller knowing or having to change.

And if you use interfaces for everything (e.g. ISearchContext is implemented by LuceneContext and MongoContext), then really the only change for swapping out contexts is in your IoC container's initialization/rules.

So, go with option #2, inject in your dependencies, have your controller just work through your read layer, and you should be good to go. Hopefully this helps!