Decoupling the domain from the interface with ReST and method objects

MVC (model-view-controller) is a well established pattern for developing web applications which I've used with both Rails and ASP.NET. Both of these frameworks provide powerful interface add-ons, for example validations, with which you can decorate your models and let the framework do a lot of heavy lifting for you.

However, I've found that using these can lead to a leaky coupling between domain and UI logic which slowly and inexorably adds complexity and maintainability headaches.

I've started using a new approach that I'm finding more flexible but has enough pragmatism to leverage the framework hooks and features.

Treat the web app as an API

Whatever you do, make the web application url scheme work with resources. For example if you need to expose a password reset feature you would do the following

POST /password_resets

You would then provide a link to the created password reset request in an email

GET /password_resets/wieurh2398hf38h83h028f2=f92-0fw0f

The the user would then submit a new password with

PUT /password_resets/wieurh2398hf38h83h028f2=f92-0fw0f

All good so far. But here's the next stage

Resources are not necessarily domain objects

When I create a password reset request I'm not actually creating a record in a database somewhere, I'm actually performing an operation on a User domain object to append a random token that I can use to look up the user record when the user clicks on the link.

class User
  def create_password_reset_token(at)
    password_reset_token = SecureRandom.urlsafe_base64
    password_reset_token_expires_at = at.utc + (60*60*24)
  end
end

Enter the method object. For this interaction I need two. CreatePasswordReset and ResetPassword.

class CreatePasswordReset
  attr_accessor :user_id

  def execute(at)
    user = user_store.get(user_id)
    user.create_password_reset(at)
    user_store.save(user)
  end
end

class ResetPassword
  include ActiveModel::Validations
  attr_accessor :token, :password, :password_confirmation
  
  validate :password, :presence => true   
  validate :password, :confirmation => true    
  
  def execute(at)
    if valid?       
      user = user_store.get_by_token(token)       
      user.set_password(password)       
      user_store.save(user)     
    end   
  end 
end

The corresponding controller methods are super simple as they just treat the result of the execute method as a binary result.

class PasswordResetController < ApplicationController   
  def update     
    command = ResetPassword.new(params[:reset_password])     
    if command.execute(current_time)       
      redirect_to account_path, :notice => "We've updated your password"     
    else       
      render :edit     
    end   
  end
end

The result is

  • I have a web api that maps to operations that a user wants to perform and domain that maps to the entities in my business system.
  • I have very simple controllers that delegate the domain interactions to method objects that understand the required interactions.
  • I leverage the validation helpers provided by the framework without polluting my domain with UI detail

I do appreciate that validation logic is actually domain logic but this pattern allows a pragmatic allocation of responsibilities in order to increase productivity.

Roles and responsibilities

Controllers to ensure only authorised users can perform operations on the domain

Method Objects to ensure domain interactions are valid and well formed.

Models to model the domain irrespective of how it's being interacted with.

 

What do you think?

 

Filed under  //  dev  
Posted

Spork not reloading classes

Have been caught out by this for a while with a Rails app that suddenly stopped seeing code changes to classes as I was redeveloping.. Not being able to test with spork running really slowed down my development cycle. Thankfully someone found a fix.

Replace this line in test.rb

config.cache_classes = true

with this

config.cache_classes = !(ENV['DRB'] == 'true')

and development is back to full speed.

More info here http://www.avenue80.com/tiny-tip-spork-not-reloading-classes/

Filed under  //  dev   rails   ruby  
Posted

Base View Model in ASP.NET MVC

I needed to reference some user context variables from within my site master page that had some logic behind their retrieval.

In this example I need to choose the display language for a user.

  1. Check cookie present indicating user's language preference
  2. if not use HTTP language header
  3. if not use the application default language

The pattern I've ended up using is to implement a base view model from which all my specific view models inherit as follows:

public class BaseViewModel {     
    public string UserLanguage     
    {      
       get      
       {          
           return HttpContext.Current.Request.Cookies["language"] != null        
               ? HttpContext.Current.Request.Cookies["language"].Value           
               : CultureInfo.CurrentCulture.TwoLetterISOLanguageName;     
        }
    } 
}

 

I then use the generic class for the master page

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<BaseViewModel>" %>

Which then allows me to reference the model in the master page and keep the logic for deciding the language to display centralised.

Not sure I'm wholly happy with the inheritance as it adds a level of dependencies which smells a bit off but in the small application I'm working in it was easy to implement and does what I need.

Filed under  //  dev  
Posted

UI Integration Testing with CassiniDev and WatiN, not Selenium

This was to be my first foray into UI integration testing. I've always been a sceptic, scare by the brittleness of any kind of recorded UI test. Luckily Gemma and Jonathan in the dev team persisted and showed me Selenium RC and WatiN. Both these solutions allow you to write the integration tests in code, a critical requirement for me.

Also key for me was this had to work in Hudson, my chosen Continuous Integration server, so simple deployment and management was key. I love being able to check everything I need into source control and have it 'miraculously' run on the CI server with little or no config.

The first thing I needed was a deployable web server I could, ideally, run in process. Enter CassiniDev and specifically the CassiniDev4-Lib.dll. Now this was really tricky to get going ;).

1. Add reference to CassiniDev4-Lib.dll

2. Put following code in my TestFixtureSetup

_hostServer = new CassiniDevServer();
_hostServer.StartServer(@"..\..\..\mywebapp");

 

Selenium wasn't quite so simple.

It failed the simple deploy requirement because you have to run the Selenium Server and then use the RC libraries to interact with it and send commands to then run on browsers.

It also has/d a bug where it sends a HEAD before it sends a GET which breaks if your MVC Controller Action as an HttpGet attribute. It sends a 404 because HEAD isn't acceptable.

Sky from the CassiniDev team was über-helpful finding this out for me http://cassinidev.codeplex.com/Thread/View.aspx?ThreadId=227174.

WatiN was a different story though. Very simple to use and all run in process. There were a couple of gotcha's though.

I had to make sure NUnit was properly running .net 4, stackoverflow helped me there http://stackoverflow.com/questions/2635794/nunit-fail-with-system-argumentexception-the-net-4-0-framework-is-not-available

And I got a rather gruesome COM exception when I pushed it all up to my CI server. 

NSystem.UnauthorizedAccessException: Retrieving the COM class factory for component with
        CLSID {0002DF01-0000-0000-C000-000000000046} failed due to the following error: 
        80070005 Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)).
    at WatiN.Core.IE.CreateNewIEAndGoToUri(Uri uri, IDialogHandler logonDialogHandler, 
        Boolean createInNewProcess)
    at WatiN.Core.IE..ctor(String url)

Luckily I found this post that talks through setting the COM permissions correctly for this kind of issue. Specifically giving the correct permissions to the user account used by the Hudson server. How To Fix UnauthorizedAccessException Retrieving The COM Class Factory For Component With CLSID

Early days, but I now have a green build that includes actually navigating to one of my forms and entering text within an integration test.

Selenium seemed to be the obvious choice for UI testing. I read somewhere that Google is throwing loads of effort into developing it so it would be a good horse to back. However, when I look at my requirements I don't need what Selenium offers.

Multi, cross-browser testing is nice but I'm just looking to confirm stories are operational and routes through my application are valid. The simplicity of WatiN seems to satisfy that nicely.

Filed under  //  dev  
Posted

ASP.NET MVC2 Wildcard Mapping on IIS6

Spent a little while tearing my hair out on this one. I followed Steve Sanderson's post ( http://blog.stevensanderson.com/2008/07/04/options-for-deploying-aspnet-mvc-t... ) but I was still getting the classic IIS 404

Capture_dcran_2010-09-22_10

Finally found the answer in the Web Service Extensions section of IIS Manager. I had to enable the ASP.NET v4 extension

0capture_dcran_2010-09-22_10

But just clicking Allow here didn't work. I actually had to go to Properties > Required Files and Allow the aspnet_isapi.dll.

1capture_dcran_2010-09-22_10

Sorted

Filed under  //  dev  
Posted

A DVCS working process

I've started working with Git using GitHub as a remote repository. This introduces into my life branching as a cheap operation and makes for a potentially different way of working.

After a discussion with the guys at work on Friday afternoon, here's what I've come up with:

Start a new feature => start a new branch.

git branch master [feature branch name]

The master branch is preserved for the current release code. My Hudson instance is only monitoring that branch for changes and ignoring all other branches.

I then push the branch to my remote repository so I get the benefits of a back-up as well being able to share the branch if I need to collaborate with someone else on it in the future.

git push [remote repository address] [feature branch name]

The code rythym then becomes add files, commit to the branch and then push to the remote repo branch.

Once the feature is finished then it's a case of squashing the commits on my local master branch and then pushing them to the remote copy.

git checkout master

git merge --squash [feature branch name]

git push

Then it's just a case of deleting the feature branch when I'm happy it's complete

Would appreciate any thoughts on this, does it sound sensible?

Filed under  //  dev  
Posted

Test teams are pointless and counter-productive

In an Agile world unit tests and integration tests, in our case driven
by BDD declared stories, are a fundamental part of a development
team's output. It could be argued that this is the most important
element as these both prove the software works and document what it is
supposed to do.

Coverage is key to the success of this. The developers need to ensure
their tests cover sufficient scenarios so as to ensure the software
delivers when used in anger.

The traditional approach is to have a dedicated test team that checks
the code in isolation as developers 'can't be trusted' to do it
themselves.

Personally I think this is balderdash.

The test-team-as-gatekeeper approach encourages a lack of
responsibility in the development team. They can produce what they
like, safe in the knowledge that the test team will pick it up. And,
if they don't, well they're mainly to blame rather than the
developers. After all, they're not testers.

Far better to have developers engaged all the way to delivery and beyond.

Pair programming, peer reviews and giving rapid iterations for the
customer to critique and break all ensure the coverage is complete.
But there is nothing like actually engaging with the user to really
bring home the importance of the feature being worked on or to bring
in to stark relief the usability assumptions they've made.

So, death to test teams. There is no place for them in an agile world.

Filed under  //  dev  
Posted

Custom domain names in Windows Azure

need a a CNAME mapping to /cloudapp.net as described here: http://blog.smarx.com/posts/custom-domain-names-in-windows-azure

Filed under  //  dev  
Posted

The trick to getting CruiseControl.net build label into PowerShell script

is to use an environment variable.

The PowerShell Task is a subclassed Executable Task which loads a series of environment variables before the task is executed. You then just need to access those in your PowerShell script via the $Env variable.

In my case
$label = $Env:CCNetLabel

Scroll down to the Notes section of this page http://confluence.public.thoughtworks.org/display/CCNET/Executable%2BTask for a full list of the environment variables that are set.

Filed under  //  dev  
Posted

Getting setup for SQL Azure

To access  SQL Azure database, you will need SQL Server Management Studio 2008 R2 CTP. The standard SSMS version doesn't cope with the slightly different schema of the cloud based cousin of the SQL 2008 we all know and love.

The Express edition of this is available here: 

http://www.microsoft.com/downloads/details.aspx?FamilyID=c772467d-e45b-43e1-9208-2c7b663d7ad1&displaylang=en

Just make sure you read the download options and don't do what I did. I downloaded just the database engine without the tools. Cue head scratching and that oh so familiar feeling of the dunce when I realised what not paying attention resulted in :)

Filed under  //  azure   dev  
Posted