First, go catch up on what's come before:
- Preamble
- The Humble Dialog Box
- Supervising Controller
- Passive View
- Presentation Model
- View to Presenter Communication
- Answering some questions
What's the Model?
I've spent most of the series talking about the View or the Presenter, but the Model piece of the triumvirate has a role to play as well.
A couple years ago I participated in a session on design patterns for fat clients that Martin Fowler was running to collect data for his forthcoming sequel to the PEAA book. One of the topics that came up in conversations afterward was whether or not it was desirable to put the real Domain Model on the client or use a mirror version of the Domain Model that you allow to have some user interface specific functionality. In other words, is the Domain Model pattern applied to the user interface a completely different animal with different rules than the traditional POCO Domain Model in the server? We didn't come up with any kind of a consensus then, and I've never made up my mind since.
As I see it, you have four choices for your Model:
- Domain Model Class - Just consume the real application classes. Many times it's the simplest path to take, and leaves you with the fewest moving parts. My current and previous projects both used this approach with a fair amount of success. In my current project our Domain Model classes implement quite a few business rules that come into play during screen interactions. The downsides to consuming the real Domain Model in the View are that you're binding the View more tightly to the rest of the application than may be desirable and the possibility of polluting the Domain Model with cruft to support INotifyPropertyChanged interfaces and other User Interface needs. Part of the reason I'm perfectly happy to consume Domain Model objects directly on my project is that our screen design doesn't require any special UI support for our custom data binding solution.
- Presentation Model - Even if you're largely following a Model View Presenter architecture, the Presentation Model is still useful. Think of this realistic screen scenario: the real domain objects are an aggregate structure and your View definitely needs some INotifyPropertyChanged-type goo. In this particular case a Presentation Model that wraps and hides the real domain objects from the View is desirable. The Presentation Model probably provides a flattened view of the domain aggregate to make data binding smoother while implementing much of the UI infrastructure code, allowing the domain classes to take the shape that is most appropriate for the business logic and behavior and keeping them from being polluted with UI code. I should not that using a Presentation Model will potentially add extra work to synchronize the data with the underlying domain objects.
- Data Transfer Object - Forget about behavior and just use a lump of data. This is often a result of hooking the user interface directly to the return values of a web service. In my previous application we wrote an absurd amount of mapping code to map data transfer objects to domain objects and vice versa and I think we all felt like it ended up being a huge waste (we *really* needed to isolate our client from the backend, so it was mostly justified). This time around I'm honestly quite content just to bind some of the user interface directly to the Data Transfer Objects returned from our Java web services. I feel a little bit dirty about this, but this approach is dirt simple. It does couple us a bit more to the server than I would normally like, but I'm hoping to compensate with a fairly elaborate Continuous Integration scheme that does a fully integrated build with end to end StoryTeller/Fit tests anytime either codebase changes to detect breaking changes.
- DataSet/DataTable/DataView - See below:
Somebody has to ask, what about DataSet's? I despise DataSet's in general, but there's no denying that sometimes the easiest way to solve a problem is to revert back to Stone Age techniques and use them. There's a memorable scene from the first Lord of the Rings movie when Gandalf is speaking to Frodo very dramatically of the One Ring - "It wants to be found." It's the same way in WinForms. The user interface widgets often want to consume a DataSet. The entire WinForms UI toolkit was originally wrapped around a very datacentric view of the world and sometimes you just go along with it.
Alright, it's not that bad. I will happily use a DataSet/DataTable/DataView in my user interface as the ostensible Model any time it's the easiest way to use a UI widget or when I want to take advantage of the sorting and filtering capabilities of a DataTable (I think that equation changes when Linq to Objects hits). The Data* classes aren't my real domain though, I generally convert my real classes to a DataTable just in time for display. And no, a DataSet-centric approach doesn't buy me much because as I'll show in the next section the Model has real responsibilities beyond just being a dumb bag of data. Plus the little issue that DataSet's are not interoperable and we're using web services written with Java for our backend services.
While a DataSet makes the data binding generally very simple, you're left with the usual drawbacks to a DataSet. You can't embed any real logic into the DataSet, so you have to be careful with duplication of logic. If you insist on a DataSet approach, I'd recommend giving the Table Module approach some thought as a way to centralize the related business logic for a particular set of data to avoid duplication. Personally, I think DataSet's are clumsy to use inside of automated tests in terms of test setup. A strongly-typed DataSet helps to at least get some Intellisense, but they annoy me as well. Plus you'll often find yourself building data that's meaningless to the test just to satisfy the referential integrity rules of a DataSet. That's wasted effort.
Posted
Tue, Jun 5 2007 10:19 PM
by
Jeremy D. Miller