Presentation Model: “Screen” Store Example
“It is better to trust in the LORD Than to put confidence in man.” —
[Psalm
118:8](http://www.blueletterbible.org/cgi-bin/tools/printer-friendly.pl?book=Psa&chapter=118&version=NKJV#8)
Ok folks. First, a couple disclaimers:
-
I first heard of this kind of approach from JP back during his Richmond boot-camp
tour. 🙂 Having said that, I’m fully expecting folks (including JP) to shoot
holes in the implementation of this example. I’m merely providing it to better
explain the pieces involved.
-
A lot of the patterns and layering you will see in the
example are intentionally overdone for the purposes of showing
the mechanics.
Now that we have that little tidbit out of the way. This post is intended to
be read in 2 parts.
First, get the code man…
You can grab the entire source code (either from my google code repository or
in [this
convenient little zip file](http://joeydotnet.googlecode.com/files/ScreenStoreExample.zip)). Feel free to browse the code for a bit, but
come back to this post so I can show an example of adding a
new, simple feature using this code base.
Some quick notes regarding the solution:
-
If you open a console to the root directory of the solution you can run
“build test” to run all the unit tests and see them succeed (hopefully, :P)
-
I was lazy in this example and didn’t include my usual “build run” target
which will automatically compile, test and fire open a browser to run the
application [without
opening VS](http://www.jpboodhoo.com/blog/DirectoryStructureForProjects.aspx). So you’ll have to open up VS and do the old skool Ctrl+F5
(sorry).
Second, let’s add a quick little feature
Right now the monitor search results show brand, size and model. Let’s say
the customer would like to also see whether or not the monitor is in stock from
the search results. Let’s see what that would take.
First off, you may have noticed that I’m using the very handy SmartGridComponent from
Ayende (now included in the [Castle Contrib
project](http://using.castleproject.org/display/Contrib/Home)). Here’s how it’s used in this example:
<%
component smartGrid, { @source:searchResults, @displayId:false } :
section more:
%>
<td>${Html.LinkTo("Details", "monitor", "view", item.Id)}</td>
<%
end
end
%>
As you can see, I don’t actually have to specify which columns should be
shown in the grid, since it’s “smart” enough to figure it out from the object it
is bound with. This means we don’t even have to modify our view to add this
feature!
(To learn more about using this component, check out the new [Castle
project wiki page on it](http://using.castleproject.org/display/Contrib/Smart+Grid+Component)).
So we know that the object that is used to display our search results is
MonitorSearchResultDTO. Instead of just adding an IsInStock
property to it now, let’s drive it out via TDD like good little boys and girls.
😀
If we glance at our Monitor domain object, we see that a
Monitor already has the ability to check if its in stock or not
(whether it should or not is a whole ‘nother discussion which is not
the point of this post). Well, sounds like all we have to change is our mapper
implementation. Let’s open up our
MonitorToMonitorSearchResultDTOMapperTest fixture.
Mapper tests are, for the most part, not all that exciting. A lot of value
matching and that’s about it. So let’s set up our 2 stubbed monitors to return
values for their IsInStock() methods.
// ...
SetupResult.For(mockDell24InchUltraSharp.IsInStock()).Return(true);
// ...
SetupResult.For(mockApple30InchCinemaDisplay.IsInStock()).Return(false);
Now let’s add our asserts to make sure the mapper is doing its job of setting
the appropriate values on the DTO. (Yes, I realize relying upon indexers in
this case is just plain ugly, but you’ll forgive me this time, won’t ya? 🙂
// ...
Assert.IsTrue(listOfSearchResults[0].IsInStock);
// ...
Assert.IsFalse(listOfSearchResults[1].IsInStock);
You’ll probably notice we don’t have an IsInStock property
on our MonitorSearchResultDTO yet. Simply use ReSharper to add
it now.
(You’ll probably want to update the constructor for this DTO in order to
keep it immutable, since there really isn’t a reason it should ever need
to change once created.)
Now if your run our test suite using “build test” from the console, you’ll
probably get a compilation error. That’s because we need to actually make the
necessary changes to get our test to pass. So we just need to update our mapper
to use the modified constructor on our DTO.
return new MonitorSearchResultDTO(monitor.Id, monitor.Brand, monitor.Model, monitor.Size, monitor.IsInStock());
So, re-run the tests using “build test” and we should be green. Now just build the app and refresh
the browser. You should see that a new column appears in the search results
named “Is In Stock”.
(Note: This feature actually only takes about 30 seconds to add. Needless to
say it took much longer to actually write about it and explain it here.)
One last problem though. It’s using “true/false” as the values which makes
for a pretty terrible user experience.
Need Something To Do?
So, dear reader. Here’s a petty little task for you if you’re interested.
**How would you change the search results “Is In Stock” column to show
“Y/N” instead of “true/false”? ** (Bonus points for leveraging built-in
MonoRail features in the solution)
In Conclusion
Let the flaming begin. 🙂 Seriously though, I hope this example at least
has some value and maybe gives a small glimpse into one way to use a
presentation model to maintain a nice separation between your presentation and
domain. I’d really like to see other folks post some examples as well.