Created: 2012-03-26 02:52
Updated: 2014-02-14 13:20
License: other



A version of the NerdDinner project using FubuMVC specifically to try out a custom convention style. Please remember this is a work in progress, as are all software projects. ;-)

About my custom conventions

This custom convention is intended to be an experiment for a simple CRUD website. The goal is to separate the database operations from the loading, validating, and presenting/modeling of that data. Some specific goals are:

  • Create view models separate from the data entities. This helps prevent the browser from attempting to put data into the entity where it is not wanted.
  • Allow a single view model to work for the viewing, updating, editing, deleting, and listing of the underlying data entity.

I am NOT attempting to do the following:

  • Abstract the database operations so far away that you don't know what you're hitting. I'm using PetaPOCO and I like it.
  • Go all crazy with design patterns, they will simply grow from the goal being worked toward.

The conventions

ViewModels are recognized by ending in 'ViewModel'. They also need the [EntityType] attribute so the conventions know the data entity type being represented by the view model. They will likely just end up needing the [EntityType] attribute in the future but this is how it works at the moment.

If you want the view model to respond to updates (POST) implement the IEntityUpdateable<> interface. The UpdateEntity method will be called with the entity that was retrieved from the database (or new if it doesn't already exist). It is up to you to properly and responsibly transfer data from the view model to the entity. If you don't want to do that by hand then look at an object mapper such as AutoMapper.

The view model must have a default parameterless constructor as well as a constructor that takes a single argument which is the entity type specified in the [EntityType] attribute. The entity constructor will be used when GETting a single entity for viewing, editing, or listing. Simply transfer the data from the entity given to the view model. Note that the entity given may be null in the case where the user may be requesting an edit form for a new item. I recommend providing empty values to all properties in the view model.

Look at the DinnerViewModel for an example of the conventions.

Future goals

  • Before proceeding with an update I want the convention to ask the view model if it is in a valid state. If so then the update can occur, otherwise the user will be informed of what they need to do to get the view model to a valid state.
  • Provide for search capability across all view models.
  • Get the edit view determination working. <-- DONE
  • Change the requirement of the view designer to need the LinkTo(new Get { Id = x }). I want to it to be truly simple for the design to interface with the view model conventions.

Some notes on design principles used

At this point the convention plumbing is trending along the Single Responsibility Principle (SRP). Partly because FubuMVC is built that way, and partly because it's simply easier to focus on one task at a time when coding.

However, the view model itself is following more of a Common Closure Principle. The CCP is usually targeted toward whole packages but I have applied it at a single class level here. My reasoning is this; if there is a change in data transfer (update or loading) then the validation is likely to change as well. But I am trying to keep the overall responsibilities of the view model as small as possible without going overboard. So some may see a slight breach of the SRP.

Details of the convention plumbing

(located in FubuNerdDinner/Handlers)


####GetHandler<TViewModel, TEntity> This simply retrieves the requested entity by id, hands it off to the view model and returns the view model to the view. If there is no entity by that id is gives the view model null for the entity. It also handles the getting of an entity for editing purposes. The Get method is for the read only view, whereas the Edit method is for the view that produces an editable form.

####SaveHandler<TViewModel, TEntity> For the moment this creates a new entity and asks the view model to update it. In the near future it will attempt to get an existing entity from the database for an actual update. This is where the validation call would occur as well as checking if this entity should have existed before updating.

####ListingHandler<TViewModel, TEntity> This will retrieve multiple instances of the entity from the database. Currently it supports only a simlpe count of records to retrieve and exact matching via the FilterObject. It will create a view model for each returned entity and return the list of resulting view models to the view.

Action Sources

(located in FubuNerdDinner root)

####GetHandlerActionSource ####SaveHandlerActionSource ####ListingHandlerActionSource

Each of these locate the appropriate view models and create actions that FubuMVC can respond to. They do much the same thing but in minor different ways to accommodate the different handlers. Note that the GetHandlerActionSource produces two action calls, one for viewing a model, and one for editing a model.

Url Policies

(located in FubuNerdDinner root)

####GetHandlerUrlPolicy ####SaveHandlerPolicy ####ListingHandlerUrlPolicy

Each of these produce a URL route to the above created actions. They are constrained by the appropriate http verb and named based on the view model class name, minus the 'ViewModel'. The ListingHandlerPolicy does ad an 'es' to the name. This will be changed in the future to handle proper pluralization. The reason for 'es' is that it was conflicting with the 'Dinners' folder name. I've yet to determine how to have FubuMVC ignore folder paths.

View Filters

(located inFubuNerdDinner root)



These are the 'IViewForActionFilter's that FubuMVC will use to determine the proper view for viewing and editing a model. Views for editing, that is the 'form', must be named 'Edit' and use the view model as it's model type. Views for viewing, read only, must be named 'View' and use the view model as it's model type. Yes I know there is a slight inconsistency between the name of the view and the name of the filter (Get vs View) I'm still trying to determine which I like better.

Cookies help us deliver our services. By using our services, you agree to our use of cookies Learn more