A project I’m currently working on is leveraging the newly released ASP.NET MVC Framework as the web presentation framework. The non-technical challenges of employing MVC are worthy of a whole post of their own, but the topic for this post is explaining an approach I’ve utilised to make the most of MVC in building scalable, testable web applications. When introducing new technology, the mention of those two terms alone is almost enough to appease the concerns of IT managers & decision makers... well, some anyway.
Structure
Being a fan of the Patterns & Practices guidance for application architecture, web applications are structured in the following manner.
Baseline Architecture:
- Client – Consumer application which renders content delivered by the web.
- MVC Web Application – Web application which enables interaction by the client, processes requests and renders content.
- Business Services – Services application which represents the business logic offered by the Web Application.
- Protected Resources – Databases, web services, file systems, line of business systems, etc.
MVC Web Application Breakdown:
Where:
- The custom Controller Factory utilises Unity (configuration vs convention – you decide) for dependency injection driven Controller assembly. This concept is equally applicable to containers such as Spring.NET and StructureMap. The Controller Factory takes an argument of another Controller Factory as a fallback mechanism for creating a Controller.
- The Controller Factory resolves the Controller based on the controller name (as determined by the MVC Framework) and System.Web.Mvc.IController. If the Controller resolution fails, the fallback Controller Factory is called.
- Service Agents are injected into the controller when the Controller instance is resolved out of the container. Personal preference is via constructor arguments, but what ever works...
A policy I enforce in the application is that the web application does not have any direct access to the database or any other protected resource – everything goes through an Agent. Primarily, this has two purposes:
- The application enjoys the security benefits of N-tier deployment, and
- The application is services enabled by default. No need to attempt to retrofit services around complex class libraries after the fact.
For start-ups, this approach is ideal in that you can deploy your web and service applications on the one server communicating via named pipes (or net.tcp on localhost), keeping infrastructure costs to a minimum. As your application grows and is adopted by a wider audience, you can move your business services onto another host, and the impact on your application, theoretically, is merely configuration. From there on out, it’s the usual scale out/up story to beefing up the deployment.
Testing
Unit tests are written to target the Controller classes, in the same way unit tests are written to test any other form of business logic classes. The general way unit tests are employed are as follows:
- Write at least one unit test per Controller Action. How many additional tests are written will vary based on the functionality offered by the action.
- Mock out the behaviour of the Service Agent interface(s) based on expectations of the unit tests. My personal weapon of choice is Rhino Mocks.
- Instantiate the controller with your mocked out agent(s) and execute the action in question.
- At a minimum, make test pass assertions based on:
- the type of Action returned
- the presence (or absence) of validation errors in the model state
- the content of the model returned with the view
With a number of UI targeting unit tests in your arsenal and included as part of your build acceptance, it can be proven that the UI will render and behave as expected when hit by a client with a browser.
No comments:
Post a Comment