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?
Where should this code go?
In a post last winter I said that a coder only becomes a true software craftsman when they start asking themselves "where should this code go?" It's a neverending question that you use to guide your designs and assign responsibilities. In a typical Model View Presenter architecture the screens are composed of 4 basic types of classes ("M", "V", "P", and the Service Layer). When you're designing a screen along MVP lines, you need to assign each responsibility of the screen to one of these 4 pieces, while also determining the design constraints upon each piece. The question of "who does what?" isn't perfectly black and white, and there are many variations on the basic structure. That being said, here's my best advice on the duties and constraints of each of the four pieces. As a rule of thumb, try to put any given responsibility as close to the top of this list as possible without violating the design constraints of each piece.
Service Layer
The Service Layer classes are generally a Facade over the rest of the application, generally encompassing business logic and data persistence. In general it's important and valuable to decouple the view layer from the rest of the application as much as possible to create orthogonality between the backend and the user interface. If you find the Presenter collaborating with more than a single Service Layer class you might think about putting a single Facade over the two services for that screen.
In terms of constraints, the Service Layer is not aware of anything specific to any one screen, but it is perfectly permissible for the Service Layer classes to be aware of the Model and perform work on the Model. If you've chosen to use actual Domain Model classes for the Model in the screen, the Service Layer is probably just some sort of Repository class. You might think of it like this. Say you're building a monolithic application today, but tomorrow you want to expose web services to interact with the system as an alternative to the user interface. The Service Layer classes should be able to work within the web service code without any change.
The Service Layer should encompass any type of business logic that is not specific or tightly coupled to a specific screen. In my current system there is going to be a validation on certain date values against a calendar service that compares the selected date against the business days for a particular country. If I were to embed that logic into the Presenter for my first Trade screen the logic wouldn't be very accessible to the following Trade screens. That calendar logic definitely belongs in the Service Layer.
Model
The Model can be more than just a lump of data. For a multitude of reasons, I want my Model class to implement most of the business rules for the given business concept. The first reason is simply cohesion. As much as possible, I want all of my business rules for an Invoice or a Trade or a Shipment in the domain class for that particular domain concept. If I want to look up the business rules for an Invoice, I want a single place to go look. One of my primary design goals is to put code where you would expect to find it, and to me that means that invoicing business rules go in the Invoice class.
The second reason is reuse or elimination of duplication. If you make a Presenter or the View itself responsible for business rules you're much more likely to find yourself duplicating the same business rules across multiple screens. Those declarative validation controls in ASP.Net are sure easy to use, but there's a definite downside because the responsibility for validation is in the wrong place.
For example, the Model classes in my current system are responsible for:
- Validation rules. The validation rules for a domain concept should definitely be kept in the Domain Model. Validation logic is most definitely a business rule. Besides, it's just so common to have multiple screens acting upon the same classes. You don't really want to have to duplicate validation rules from a "Create Trade" screen to the parallel "Edit Trade" screen do you? If nothing else, putting validation rules in the Domain Layer makes these rules very simple to test compared to the same rules living in either the View or even the Presenter. I've got a lot more to say on this subject in the next post on the Notification pattern. In the meantime, Jean-Paul has a good post on Validation In The Domain Layer - Take One and again Validation In The Domain Layer - Take Two.
- Default values. My last two projects have included quite a bit of logic around assigning default values. The normal scenario is that the use selects one value, and from that one value you can determine logical defaults for one or more other fields. I have a little business rule to code tomorrow morning that will automatically set the values for two date fields to 2 days after the Trade Date as soon as the Trade Date is selected for the first time. That rule is going right into the appropriate Trade subclass, both because it's where I'd expect to find that code and because it makes that business rule very easy to drive with Test Driven Development. Because the rule is wholly implemented in the Model class and the Model class is completely decoupled from the View and Service Layer, I can test these rules by applying purely state based testing. For this approach to work I do need the screen to automatically synchronize itself with the Model when the Model changes, but the WinForms tooling makes that kind of Observer Synchronization relatively simple.
- Calculated values. Again, this is dependent upon using Observer Synchronization, but I place the responsibility for calculating derived values into the Model class. It's a business rule, and the Model class has all of the data, so it's the natural place for this logic. As with defaulted values, the calculation can be relatively simple to test in isolation.
The constraint on the Model is that whether or not we allow the Model classes to call out to any kind of service, and if so, how much coupling do we allow with other services? In my current project I don't allow any direct coupling from my Model classes to any external system. If a business rule for defaulting or calculating a value requires information or a service from outside the Model, I would partially move that responsibility out into the Presenter to keep the Model decoupled from infrastructure. The Model classes don't know how they're displayed nor how they're persisted and updated. In other systems you might opt for more of an Active Record approach that puts some form of service communication responsibility into the Model classes.
The Presentation Model approach isn't really an exception to these constraints because the Presentation Model itself usually wraps the actual Model.
View & Presenter
The screen itself is split between two main actors, the View and the Presenter. As we've seen in the previous posts on the Supervising Controller, Passive View, and Presentation Model, there's a couple different ways to split responsibilities between the View and Presenter. Rather than rehash those posts, I just want to add a few more thoughts:
- Don't allow the View to spill out into the Presenter. There might be exception cases, but don't allow any reference to any Type in the System.Windows.Forms namespace from the Presenter class. Any WinForms mechanics in the Presenter is like letting water splash out of the tub and rot the floor.
- Keep the View as thin as possible. Always ask yourself if a given piece of code could or should live outside of the View. My advice is to move anything out of the View that doesn't have to be there.
- Don't allow the Presenter to become too big. It's far too tempting to use the Presenter as a receptacle for any random responsibility. If you see parts of the Presenter that don't seem to be related to the rest of the Presenter, do an Extract Class refactoring to move that code to a smaller, cohesive class.
- There is no ironclad rule that says 1 screen == 1 view + 1 presenter. It's often going to be valuable to reduce the complexity of either the Presenter or View by breaking off pieces of the screen into smaller pieces. There may still be one uber-Presenter and one uber-View for the entire screen, but don't hesitate to make specific Presenter or View classes for a part of a complicated screen. There's also no ironclad law that says n views = n presenters as well.
Getting back to first causes, it all comes down to two things:
- Is each piece of the code easy to understand?
- Is the code easy to test?
Answer these two questions in the affirmative and I think your design is effective.
Where Next?
That's the end of the 100/200 level material on "Build your own CAB." Now we get to move on to the cool parts -which will undoubtedly prove more difficult to write. The next post will be on Domain-centric validation with the Notification pattern. After that I think the consensus is to go into building the Application Shell (2-3 posts), then my MicroController stuff (2-3 posts), and automated testing (???). Bringing up the rear will be event aggregation and synchronization with the backend. I am leaving Pluggability until last, but that's because I'm going to slip in a StructureMap 2.1 release at the end of June and demonstrate a couple of new features.
I'm hoping to wrap this up no later than the first week in July.