Concepts & Features in NH Prof: Filtering
A while ago I mentioned the idea of Concepts and Features, I expounded it a bit more in the Feature by Feature post. Concepts and Features is the logical result of applying the Open Closed and Single Responsibility Principles. It boils down to a single requirement:
A feature creation may not involve any design activity.
Please read the original post for details about how this is actually handled. In this case, I wanted to show you how this works in practice with NH Prof. Filtering in NH Prof is a new concept, which allows you to limit what you are seeing in the UI based on some criteria.
The ground work for this feature include the UI for showing the filters, changing the UI to support the actual idea of filtering, and other related stuff. I can already see that we would want to be able to serialize the filters to save them between sessions, but that is part of what the concept is. It is the overall idea.
But once we have the concept outlined, thinking about features related to it is very easy. Let us see how we can build a new filter for NH Prof, one that filter out all the cached statements.
I intentionally choose this filter, because it doesn’t really have any options you need a UI for. Which make my task easier, but here is the UI, FilterByNotCachedView.xaml
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid> <TextBlock>Uncached statements</TextBlock> </Grid> </UserControl>
And the filter implementation:
[DisplayName("Not Cached")] public class FilterByNotCached: StatementFilterBase { public override bool IsValid { get { return true; } } public override Func<IFilterableStatementSnapshot, bool> Process { get { return snapshot => snapshot.IsCached == false; } } }
Not hard to figure out what this is doing, I think.
This is all the code required, and NH Prof knows that it is going to pick it up from the appropriate places, and make use of it. This is why after adding these two files, I can now run NH Prof and I get the following:
I don’t think that I can emphasis enough how important this is to enable creating consistent and easy to handle solutions. Moreover, since most requests tend to be for features, rather than concepts, it is extremely easy to put up a new feature.
The end result is that I have a smart infrastructure layer, where I am actually implementing the concepts, and on top of it I am building the actual features. Just to give you an idea, NH Prof currently have just 6 concepts, and everything else is built on top of it.
Comments
When I see the code base I inherited to maintain and this blog entry, it looks like magic :(
Nice post again.
Could you explain why you have defined display name as an attribute and not as a read-only property?
Just a quick clarification: When you say, "May not involve", do you mean CANNOT, or should not? I'm assuming the former because it's you posting, but the latter seems more likely, just given systems I've seen. :)
@Kyle - I would assume that Should Not would quickly devolve into usually does which becomes always does. Then the lines between Concepts & Features blur and your app becomes a big ball again making it difficult to add new features to your app.
@Ayende - I like this approach a lot. Would you consider this a valid approach for simple CRUD apps or is this overkill? I've done something like that before where you'd call a concept one of the standard actions (create, list, edit/update, delete, view) and each feature is essentially a new screen with UI and a presenter that dictates where the data comes from/goes.
Mike,
I would say that CRUD is a concept and a CRUD screen is a feature in this concept
Kyle,
I am leaning toward must not.
@Michael We use an attribute for the display name because we don't instantiate the filters in order to list them in the UI. The list of available filters is just a list of types, and the only addition metadata we needed was the display name.
Comment preview