Motivation
Lot's as been written on decoupling presentation logic since the ages of Smalltalk and the MVC-Framework. But still today, even in medium sized projects (far beyond the stage of prototype), developers - and that's me also - tend to put far too much code into the UI layer. Simultaneous, details on validation and security constrains applied to the UI have mostly left out.
What's the problem?
The problems do not show up immediately, but after some time and growth. Most know, that putting domain logic into the UI layer is one of the worst habits. But few identify presentation logic in the UI-Implementation as a source of high coupling and low cohesion.
This is in conflict with one of the most basic rules of object oriented design principles: Subsystems should be highly cohesive and have low coupling. Some guys out there have a pretty short answer: "Using partial classes [in C#] decouples UI controls and logic". They fail to recognize, that partial classes in C# split a class across several files - but it does not bring in low coupling, as it is still one class.
Looking at the widespread known patterns discussed by the GoF, one can notice that there are couple of patterns with "Split Object" behavior: Decorator, Compositor, Proxy, Strategy, ...
What we need is a similar "Split Object" solution for decoupling of UI implementation and presentation logic concerns.
A possible solution
Since the ages of Smalltalk, we (should) know MVC - Model View Controller. It is a simple and working pattern for presentation logic decoupling. So why switching minds and using another one? What MVC fails, is to enable isolated unit testing. The guys of Atomic Objects noticed that [Atomic Objects Agile2006] and introduced the MVP - Model View Presenter pattern. Fowler has gone even further and split this pattern into two: Supervising Controller [Fowler EAA, SC] and Passive View [Fowler EAA, PV].
Passive View
Passive View reduces the UI (read: the views) to its absolute minimum. The difference to Model-View-Controller is that the view does not care about changes in the model. The presenter is in charge of updating the view with any changes in the model. Events on the view are subscribed by the presenter, views do not change the model.
As a direct result, all view logic lives in the presenter (sometimes also called controller, as this can be considered a MVC variant). The view and the model do not know of each other and have no direct reference.
Implementing it results in chatty interfaces between presenter and view, as all shown fields are transferred through properties on the view interface. I see it as an disadvantage, especially on complex objects. Supervising Controller can help, but it depends (as always) on your context.
A nice side effect is the following: Passive View makes it possible to implement views, that are totally unaware of your domain model. Splitting these layers into different subsystems (to be language and platform neutral) makes it physically nearly impossible to put business concerns into the views.
Supervising Controller
Passive View is a nice solution, as it fits our needs and decouples the presentation logic. Still it has some drawbacks. Data binding is the most important. Current view technologies have advanced support for (object) data binding. Using it can make your life a lot easier, writing less mapping code transferring values from and to the views.
Supervising Controller stands in. Presentation logic and command execution on view-events is still task of the presenter. Simple data mapping is done declaratively by the frameworks data binding. This solution lacks the ability to split view and presenter/model physically. Still it has the advantage of less code by using data binding.
Handling modal state
Presenters lack a concrete coupling to the view implementation. Unanswered is the task of showing model dialogs (Message Boxes) and asking the user for information (File selection, ...). Directly using message boxes in the presenter is generally a bad idea. It prevents us from isolated testing and worse: from testing at all.
A better approach is using the most valuable weapon in computer science - abstraction. We create a thin interface:
public interface IHumbleMessageBoxServiceOur view subsystem implements this interface, for example with a standard message box. This service is injected into the presenter (follow up: Dependency Inversion), communication is only going through this interface. The presenter is unaware of the message box details, model dialogs, etc.
{
bool AskYesNoQuestion(string title, string message);
}
Using this approach now enables us to use unit testing again on our presenters. Generally everything should be decoupled by dependency inversion - it makes your testing life easy and your design more flexibly and explicit.
Validation
I consider validation as an integral part of the business logic. Complex validation rules regarding more than simple per-field checks are near to impossible to implement correctly in the UI (without high efforts). Those validation rules can be easily implemented in the domain model. Using current technology for declarative validation checks (Spring.Validation (Java, .NET) and MS Validation Block) make this task easy. Executing those checks should be initiated from the presenter, pushing the results (read: all at once) to the UI.
The habit of using the default "ErrorProvider" of .NET's Windows.Forms is a bad idea, because it locks the user to the current field, triggering errors. Complex validation rules require the presentation of all errors at once, thus triggering of the complete validation in the presenter is key. You may start to think, how popular "complex" rules are. The most "complex" rule one may think of is two date fields as input for a date range.
Further, there are several types of validation rules in a system. Some are part of your business requirements, but some are introduced by a certain persistence architecture(files, databases, ...). Not aggregating all types of rules in the domain layer results in wide spreading of validation concerns. Mediating validation and their results to the views is naturally placed in the presenter. No one (?) wants his UI layer to talk directly to services and operations of the domain layer.
Security
Securing operations on the domain layer is easily achieved with declarative and imperative security. Microsoft Security Block (Part of Enterprise library) is capable of both, using Aspects for this task can be easily implemented with Spring.Aop in Java and .NET. Currently this is the way, security is build into famous frameworks like JBoss Seam.
While it is acceptable for users of web applications to read "Operation not allowed", it is definitely not for rich desktop applications. I expect the same to happen in the field of AJAX enabled web applications. The question is: how can we prevent the user right in the UI layer from trying to execute certain operations?
In our domain model, we should have all operations available. Using declarative security is key here, because it clearly states security constrains at well defined,
Generically, we need to access these security attributes through reflection. For those in the .NET world and using Microsoft's IPrincipal:
bool roleMemberFlag = Thread.CurrentPrincipal.IsInRole("CanApproveClaims");
this is an easy task. It enables us to access security information for certain fields and operations via reflection. The presenter enables and disables the involved controls. Try doing that directly in your "code behind" of Dialogs, wihtout messing up all of your code.
Better design with higher effort?
Using the MVP pattern (and using patterns in general) rises the question of effort and results. In our case, testing presentation logic with unit testing the presenters in isolation is simpler and easier than any complex blackbox testing at the UI front.
Further, we gain more flexibility, code reuse and a design that meets object oriented principles. On the effort side, extracting an interface is of actually no cost, using today's refactoring support available.
Tags: domainmodel, patterns, presentationlayer
public interface IHumbleMessageBoxService{bool AskYesNoQuestion(string title, string message);}
I've been trying to wrap my head around the various implecation of building an entirely MVP app and your post jumpstarted my brain :)