Ayende @ Rahien

Refunds available at head office

Effectus: Fatten your infrastructure

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 how to build an application infrastructure.

First, to be clear, I am not talking about infrastructure pieces such as caching, data access, etc. Those are generic concerns and you should be able to just get them from an off the shelve library. When I am talking about an application infrastructure, I am talking about the infrastructure code that you are going to need to write for a particular application. You are always going to have to do that, even if it is something as simple as just wiring the container together, or registering session handling, etc.

From my point of view, the point of the application infrastructure is to make writing the actual application features as simple as possible. Ideally, you want to make the infrastructure pieces handle everything that isn’t purely related to the feature you are implementing. The reason that an application infrastructure can do this while a generic infrastructure cannot is that an application infrastructure can make a lot of assumptions about the way the application is built.

Let us take the Main screen feature in Effectus, shall we, it will let us look at how this is implemented:

public Observable<int> CurrentPage { get; set; }


public
Fact CanMovePrev { get { return new Fact(CurrentPage, () => CurrentPage > 0); } } public Fact CanMoveNext { get { return new Fact(CurrentPage, () => CurrentPage + 1 < NumberOfPages); } } public void OnCreateNew() { Presenters.Show("CreateNew"); } public void OnActionsChoosen(ToDoAction action) { Presenters.Show("Edit", action.Id); } public void OnLoaded() { LoadPage(0); } public void OnMoveNext() { LoadPage(CurrentPage + 1); } public void OnMovePrev() { LoadPage(CurrentPage - 1); }

Looking the code, we can see that there are actually a lot of infrastructure playing a part here.

  • Automatic binding of On[Button Name]() methods to the button command.
    • Within this, enabling / disabling automatically using Can[Button Name] properties
  • Automatic binding of On[List name]Choosen(Item) to the selected item changed behavior on the list.
  • Automatic updates of property states using Fact.
  • Automatic updates of property values, using Observable.

There isn’t much to say about the automatic binding between UI actions and presenter methods, except that I think it is clear how this behavior reduce the level of infrastructure level code that you would have to write, test and maintain. But both Fact and Observable deserve a bit more explanation.

In the LoadPage() method, not shown here, we are updating the CurrentPage property, this will trigger invalidation of the all the facts bound to the value (CanMoveNext, CanMovePrev), which will force their re-evaluation. Since the infrastructure knows how to wire this up to the appropriate commands on the UI, this means that this will enable/disable the given controls automatically. What is more, look at the code. It is clear, concise and pretty easy to reason about and use.

I’ll leave the actual exploration of the implementation to you, it is fairly simple, but the idea is important. The Effectus application actually have more infrastructure code than it have code to implement its features. But that is a very small application. Just to give you an idea, the # of lines of code devoted to infrastructure in Effectus is about 600 LOC! In most applications, you probably wouldn’t even notice the amount of code that the infrastructure takes, but the effects are quite clear.

Comments

junior programmer
12/20/2009 12:08 PM by
junior programmer

I am looking forward to the rest of the blog posts on Effectus. It deserves a lot more wordings than just the MSDN article. MSDN article seems to be an overview only and a lot of the details didn't have a chance to delve into. It will be extremely good to explore section by section on the reasons behind each design decision and implementation details. Thanks.

junior programmer
12/20/2009 12:11 PM by
junior programmer

I am looking forward to the rest of the blog posts on Effectus. It deserves a lot more wordings than just the MSDN article. The article seems to be an overview only and much of the details didn't have an chance to delve into. It will be much better to explore section by section on the design decisions as well as the implementation details. It will be especially educational for people like me. Nowadays you need to know everything in order to do one thing great. Thanks.

Vadi
12/20/2009 02:43 PM by
Vadi

Sorry for changing the context of this discussion .. looks like the code may fail when CurrentPage is MaxPage or CurrentPage is 0, I am referring to OnMovePrev and OnMoveNext

firefly
12/20/2009 03:27 PM by
firefly

Currently there is no validation logic for the save, which mean if the user enter no data the save will fail. So if I have the pleasure of picking your brain, may I ask where would you put such logic and why?

Nick
12/20/2009 03:53 PM by
Nick

Any reason not to pass the observable (or perhaps just the current value) to the fact like this:

return new Fact(CurrentPage, x => x > 0);

Decouples the Fact from the class a little, and should be easier to test the fact in isolation, right?

-Nick

Brendan
12/20/2009 05:01 PM by
Brendan

I haven't had the chance to look into Effectus but it is on my list of things to do over the holidays.

Typically any windows applications that I develop are client server based and running through a web service, Effectus looks to go straight to the database from what I have read.

Is there any way you could maybe extend Effectus to work over services? It would be a great help to a lot of people and I would be very grateful.

Thanks again for another insight into best practice with NHibernate...

Kevin Gadd
12/20/2009 05:13 PM by
Kevin Gadd

Can't you pull the identity of the Observable directly out of the expression tree, instead of having to specify it twice? That would also allow you to handle expressions that contain multiple Observables.

FallenGameR
12/20/2009 09:48 PM by
FallenGameR

Why BackgroundWorker in Effectus.Features.CreateNew calls CompleteSave in thу UI thread? I tested in console App, RunWorkerCompleted runs in a new thread:

private static void Print( string info )

{

Console.WriteLine( "{0}: {1}",

    Thread.CurrentThread.GetHashCode(),

    info );

}

private static void Main( )

{

Print( "Main thread" );


var bw = new BackgroundWorker( );

bw.DoWork += (sender, args) => Print( "Do work thread" );

bw.RunWorkerCompleted += (sender, args) => Print( "Completed thread" );

bw.RunWorkerAsync( );


Thread.Sleep( 500 );

Print( "Press Enter to exit" );

Console.ReadLine( );

}

FallenGameR
12/20/2009 09:51 PM by
FallenGameR

Ouch. Forget prev comment. I didn't know it behaves differently in WinForms and WPF apps.

brainboost
12/20/2009 10:37 PM by
brainboost

Nice concept, first of all. Ok, binding to parameterless events is as easy as elegant but what about 2, 3 and more parameters in events? Are we need to put a method in the Presenters for any parameter case? Played with code, it fits not only WPF but WinForms apps as well, but then you need some presenter modifications.

Ayende Rahien
12/22/2009 07:21 AM by
Ayende Rahien

Vadi,

Why would it fail?

I tested it with both, so I am pretty sure it wouldn't

Ayende Rahien
12/22/2009 07:21 AM by
Ayende Rahien

Firefly,

I would add a validation library, hook it into the DataBindingFactory and have the infrastructure automatically catch ValidationException and show them in the UI in the appropriate manner.

Ayende Rahien
12/22/2009 07:22 AM by
Ayende Rahien

Nick,

The logic that you use isn't necessarily based on the observable, you only need to run it after the observable have run.

Ayende Rahien
12/22/2009 07:23 AM by
Ayende Rahien

Brendan,

A lot of the things in the source code would transfer to a service based model, yes.

Ayende Rahien
12/22/2009 07:24 AM by
Ayende Rahien

Kevin,

I can, but it is:

a) too hard to do for something this trivial.

b) don't assume that the observable is the source of the calculation, it is just the trigger for it.

Ayende Rahien
12/22/2009 07:25 AM by
Ayende Rahien

Brainboost,

I am afraid that I am not sure that I am following you.

If I need N parameters, I am going to have a method with an enumerable parameter

brainboost
12/22/2009 11:31 AM by
brainboost

Ayende,

Yes, I'm absolutely agree with you about enumerables if you can control event args, but events are different and even one sensible parameter, say on KeyDown event, would require to add handling method, if we need to look at the key pressed. What I have to do is

...

WireEvents(instance);

WireEventsWithParameter(instance);

in Presenters and implement that method in similar manner.

Thank you!

Comments have been closed on this topic.