[ Draft ]
The presentation layer is the visual part of a program. It is what the user sees. Much of the presentation code is kept platform independent in this software architecture. These are the design patterns regularly employed.
See the ViewModels
article.
See the Presenters
article.
An extension method that converts an Entity
to a ViewModel
. You can make simple ToViewModel
methods per Entity
, converting it to a simple ViewModel
that represents the Entity
. You can also have methods returning more complex ViewModels
, such as ToDetailsViewModel()
or ToCategoryTreeEditViewModel()
.
You may pass Repositories
to the ToViewModel
methods if required.
Sometimes you cannot appoint one Entity type
as the source of a ViewModel
. In that case you cannot logically make it an extension method, but you make it a Helper
method in the static ViewModelHelpers class
.
The ToViewModel classes
should be put in the sub-folder / sub-namespace ToViewModel
in your csproj. For an app with many Views
a split it up into the following files may be a good plan:
ToIDAndNameExtensions.cs
ToItemViewModelExtensions.cs
ToListItemViewModelExtensions.cs
ToPartialViewModelExtensions.cs
ToScreenViewModelExtensions.cs
ToViewModelHelper.cs
ToViewModelHelper.EmptyViewModels.cs
ToViewModelHelper.Values.cs
ToViewModelHelper.Items.cs
ToViewModelHelper.ListItems.cs
ToViewModelHelper.Lookups.cs
ToViewModelHelper.Partials.cs
ToViewModelHelper.Screens.cs
For clarity: the ViewModelHelper
files are all ViewModelHelper partial classes
. The other files have a class
that has the same name as the file.
Inside the classes
, the methods should be sorted by source entity or application section alphabetically and each section should be headed by a comment line, e.g.:
// Orders
public static OrderListViewModel ToListViewModel(this IList<Order> orders) { ... }
public static OrderEditPopupViewModel ToEditViewModel(this Order order) { ... }
public static OrderDeletePopupViewModel ToDeleteViewModel(this IList<Order> orders) { ... }
Some ViewModels
do not take one primary Entity
as input. So it does not make sense to turn it into an extension method, because you cannot decide which Entity
is the this argument. In that case we put it in a ViewModelHelper class
with static classes
without this arguments. ViewModelHelper
is also part of the ToViewModel
layer.
Extension methods that convert a ViewModel
to an Entity
.
You typically pass Repositories
to the method. A simple ToEntity
method might look up an existing Entity
, if it exists, it would be updated, if it does not, it would be created.
A more complex ToEntity
method might also update related Entities
. In that case related Entities
might be inserted, updated and deleted, depending on whether the Entity
still exists in the ViewModel
or in the data store.
A ToEntity
method takes on much of the resposibility of a Save action.
< TODO: Describe the organization of the ToEntity extensions. >
A template for rendering the View
.
It might be HTML
.
In WebForms
this would be an aspx
.
In MVC
it can be an aspx
or cshtml
.
Any code used in the View
should be simple. That is: most tasks should be done by the Presenter
, which produces the ViewModel
, which is simply shown on screen. The View
should not contain business logic.
In a stateless environment, lookup lists in Views
can be resource-intensive. For instance a drop down list in each row of a grid in which you choose from 1000 items may easily bloat your HTML
. You might repeat the same list of 1000 items for each grid row. There are multiple ways to combat this problem.
For small lookup lists you might include a copy of the list in each Item ViewModel and repeat the same lookup list in HTML
.
Reusing the same list instance in multiple ViewModels may seem to save you some memory, but a message formatter may actually repeat the list when sending a ViewModel
over the line.
For lookup lists up until say 100 items you might want to have a single list in an Edit ViewModel
. A central list may save some memory but, but when you still repeat the HTML multiple times, you did not gain much. You may use the HTML5 <datalist>
tag to let a <select>
/ drop down list reuse the same data. You might also use a jQuery
trick to populate a drop down just before you slide it open.
For big lookup list a viable option seems to AJAX
the list and show a popup that provides some search functionality, and not retrieve the full list in a single request. Once AJAX'ed
you might cache the popup to be reused each time you need to select something from it.
You might save the server some work by doing partial loads instead of full loads or maybe even do JavaScript
or other client-native code.
Partial loads load part of a web page, intead of the whole page, so the whole page does not have to be refreshed every time. It also might save work for the server that has to do less processing then.
JavaScript
is client-native code, that could omit server code altoghether, but potentially with a penalty on maintainability, because the client-code may be written in a way that is more dependent on specific view details, which might easily change as the server-code evolves.
You could also call it: first choice full load.
In web technology you could also call it:
Full postback - AJAX
- JavaScript
When programming page navigation, the first choice for showing content is a full page load in this architecture. Only if there is a very good reason, we might use AJAX
to do a partial load. Only with a very good reason, we might start programming user interaction in JavaScript
.
But it was always the first choice to do full postbacks.
The reason for this choice is maintainability: programming the application navigation in C#
using Presenters
is more maintainable than a whole lot of JavaScript
. Also: when you do not use AJAX
, the Presenter
keeps full control over the application navigation, and you do not have to let the web layer be aware of page navigation details.
Furthermore AJAX'ing
comes with extra difficulties. For instance that MVC
<input>
tag ID’s vary depending on the context and must be preserved after an AJAX
call, big code blocks of JavaScript
for doing AJAX
posts, managing when you do a full redirect or just an update of a div. Keeping overview over the multitude of formats with which you can get and post partial content. The added complexity of sometimes returning a row, sometimes returning a partial, sometimes returning a full View
. Things like managing the redirection to a full View
from a partial action. Info from a parent ViewModel
e.g. a lookup list that is passed to the generation of a child ViewModel
is not available when you generate a partial View
. Request.RawUrl
cannot be used as a return URL in links anymore. Related info in other panels is not updated when info from one panel changes. A lot of times the data on screen is so intricately related to eachother, updating one panel just does not cut it. The server just does not get a chance to change the View
depending on the outcome of the business logic. Sometimes an AJAX
call’s result should be put in a different target element, depending on the type you get returned, which adds more complexity.
Some of the difficulties with AJAX
have been solved by employing a specific way of working, as described under AJAX
in the Aspects section.
The presentation patterns may differ slightly if used in a stateful environment, but most of it stays in tact. For instance that Presenters
have action methods that take a ViewModel
and output a new ViewModel
is still useful in that setting. In a stateless environment such as web, it is needed, because the input ViewModel
only contains the user input, not the data that is only displayed and also not the lookup lists for drop down list boxes, etc. So in a stateless environment a new ViewModel
has to be created. You cannot just return the user input ViewModel
. You would think that in a stateful environment, such as a Windows
application, this would not be necessary anymore, because the read-only View
data does not get lost between user actions. However, creating a new ViewModel
is still useful, because it creates a kind of transaction, so that when something fails in the action, the original ViewModel
remains untouched.
You would be making assumptions in your Presenter
code when you program a stateful or stateful application. Some things in a stateful environment environment would not work in a stateless environment and you might make some objects
long-lived in a stateful environment, such as Context
, Repositories
and Presenters
. But even if you build code around those assumptions, then when switching to a stateless environment – if that would ever happen – the code is still so close to what’s needed for stateless, that it might not come with any insurmountable problems. I would not beforehand worry about ‘will this work in stateless’, because then you would write a lot of logic and waste a lot of energy programming something that might probably never be used. And programming something for no reason at all, handling edge cases that would never occur, is a really counter-intuitive, unproductive way of working.
< TODO: Added benefit: Transactional. >
When you user input back as a ViewModel
from your presentation framework of choice, for instance MVC
, you might encounter null-lists in it, for lists that do not have any items. To prevent other code from doing NullCoalescing
or instead tripping over the nulls, you can centralize the NullCoalescing
of pieces of ViewModel
and call it in the Presenter
.
< TODO: Better description. Also incorporate:
- Also add a code example.
- Consider making a separate pattern description for NullCoalesce methods in general and move it to the Other Patterns section to which you then refer from this section NullCoalesce (ViewModels).
- NullCoalesce. Applied to viewmodels that are passed to Presenters. The choice is made here to only NullCoalesce things that a View / client technology might leave out. Theoretically it might be better to NullCoalesce everything in the ViewModel, but this does take full traversal of the tree, which comes with a (small) performance penalty. Also: the NullCoalesce procedures take some typing time for the programmer, and requires maintenance when the structure changes. That is why the choice is made to only NullCoalesce a select set of things, that is adapted to our specific needs, rather than something that will always work. >
When you edit a list, and between actions you do not commit you may need to generate ID’s for the rows that are not committed, otherwise you cannot identify them individually to for instance delete a specific uncommitted row. For this you can add a TemporaryID to the ViewModel
, that are typically Guids.
The TemporaryID’s can be really temporary and can be regenerated every time you create a new ViewModel
.
The TemporaryID concept breaks down, as soon as you need to use it to refer to something from multiple places in the ViewModel
.
An alternative is to let a data store generate the ID’s by flushing pendings statements to the data store, which might give you data-store-generated ID’s. But this method fails when the data violates database constraints. Since the data does not have to be valid until we press save, this is usually not a viable option, not to speak of that switching to another persistence technology might not give you data-store-generated ID’s upon flushing at all.
Another alternative is a different ID generation scheme. You may use an SQL
Sequence, or use GUID’s, which you assign from your code. Switching from int ID’s to GUID’s is a high impact change though, and does come with performance and storage penalties.
< TODO: Explain the argument that ViewModel, ToEntity and ToViewModel does require programming a lot of conversion code, but gives you complete freedom over your program navigation, but the alternative, a framework prevents writing this conversion code for each application, but has the downside that you are stuck with what the framework offers and loose the complete freedom over your how your program navigation works. >