EffectusIsolated features
Continuing in my exploration of the Effectus code base, which originated from my Building a Desktop To-Do Application with NHibernate article on MSDN Magazine, I wanted to talk today about separating features into individual pieces in the application.
Each feature in the Effectus application is built as an isolated unit, that has little to do with any other features. This is very clear when you look at how they are organized:
Sidenote: In each feature, the component parts are named the same (Presenter, View, Model). This is intentional, since it make it that much harder to mix stuff from other features.
Every feature has a pretty rigid structure, defined by the application, and trying to reuse stuff between features is frowned upon and avoided, even if doing so can reduce duplication. As a good example, the Model for both CreateNew and Edit features are identical, but they are two different classes with no attempt to merge it up.
The reason for this is quite simple, treating each feature as an isolated unit bring us great benefits, we can have different developers working on different features without the chance of stepping on each other toes, it is very hard for one feature to break another, deploying new features is easier to handle, etc. Trying to apply DRY and consolidate common pieces of code would introduce dependencies between features, and that is a bad idea.
But what about communication between features? What about when one feature needs to call another?
As it turned out, it is quite easy to turn a lot of the inter feature communication into an indirect calls, using pub/sub. We gain the independence from dependencies using pub/sub, while maintaining the feel of a well integrated product for the user. For the rare cases of one feature calling another, I create a simple Call(“FeatureName”) mechanism (in the code, this is Presenters.Show(name) method) that all us to just explicitly invoke another feature, maybe with some initial arguments. A more complete implementation will handle even that using pub/sub, probably with an integrator class that would dispatch the appropriate event to start and invoke the feature(s) that want to handle it.
You can read more about the architecture principles behind this system in: Application structure: Concepts & Features
More posts in "Effectus" series:
- (21 Dec 2009) Isolated features
- (20 Dec 2009) Fatten your infrastructure
- (19 Dec 2009) Building UI based on conventions
Comments
So in light of the Concepts & Features article... Does the namespace Effectus.Model contain the Concepts?
Component naming makes sense, altough it makes Resharper navigation a bit slower.
I know this is a minor detail about how you are doing things, but obviously in a larger application you would have more than one Edit screen.. would you do a deeper nesting of your directories to be more like:
-Features
-Contact
-Company
or go the route of
-Features
-EditContact
-EditCompany
Maybe I missed something but i have no idea how to test presenters. Granularity of IPresenter's too low to mock it and most valuable properties like Model don't allow to apply IoC in easy manner. Am i wrong?
"Trying to apply DRY and consolidate common pieces of code would introduce dependencies between features, and that is a bad idea."
What I hear you saying is:
It is generally bad to have one feature depend on another.
It is also generally unhelpful for features to reuse a common codebase (e.g. the Model for the CreateNew feature and that of the Edit feature are identical instead of reusing a common BlahModel). I assume this would be because, even though they may happen to be identical at different times (as a broken clock is right twice per day), it is generally not true that they shall be the same--i.e., they are the same by accident, but will likely vary by design--and should therefore not share code in common. Is this correct? Am I making sense?
@Jason - you are making sense and I like the clock analogy. In "business logic" layer code reuse is a bad thing because it means logic reuse, which introduces hidden dependencies between functions that would be unrelated otherwise. If you have large applications you want to keep everything as local as possible, so you don't have to analyze the whole codebase to make changes to a single function. Ayende has chosen a wise, however somewhat bloated, approach to isolating features. I'd rather use script/DSL files to contain higher-level functions, each feature in its own file, but the method is not that important. Another aspect is the organization of the code - IMHO the static code structure defined by packages/classes/inheritance is good for infrastructure code, but not so good for business logic. For the logic its much better to group source according to business functionalities provided - for example by business processes supported by application.
Nice code and explanation. I agree with brainboost that adding unit tests to the project will be very beneficial to us. ;=)
David,
Nope, a concept in Effectus is showing screens with data bound to them.
Josh,
I don't like deep hierarchies, so I would probably go for the second version.
OTOH, if I have a LOT of features, I would have Features\Contact\EditContact at some point, just to make sure that everything is arranged in a way that I can work with.
Brainboost,
var presenter = new Presenter();
presenter.OnSave();
etc...
They are basically POCO classes, so you don't really need to do much to test them
Jason,
Yes, you are making absolute sense
A "review" of the Effectus example was published in the José Romaniello blog.
jfromaniello.blogspot.com/.../...ing-effectus.html
I'm a fan of your features/concepts architecture and I really like their isolation but I'm thinking that how I can apply it on a real project that I'm working on.
We have "create and edit documents" scenarios among the features of our application.
These scenarios contains a complicated approach and a simpler approach according to user preferences (we have two forms -a full form against a summary form-for each scenario and we are using the same form to create and edit )
The questions is how we can isolate these features so that we don't have to change all forms if there's a new piece of data should be added to them.
One thing that comes to my mind is to use a user control on our views but I'd like to have your opinion on this matter.
Thanks again
Comment preview