Anda di halaman 1dari 14

c 


 
         c  

ASP.NET MVC ± Defining an application architecture


Introduction
ASP.NET MVC is creating some buzz at the moment and has been long awaited. Being a Microsoft product,
MVC will be launched into the mainstream development arena and like webforms, could and hopefully will
be a popular web development platform that will be around for years to come . Microsoft and some of the key
players in this technology and has already got a good community going. The official ASP.NET MVC site
contains some very light and quick code tutorials to illustrate the features of MVC.

Although this is all good, I do have some concerns. It starts with what developers learn, the tutorials on the
official asp.net mvc site are aimed at developers at different levels and are for illustration only. They are not
samples of production quality code. What is missing at the moment are the better practice and guidelines for
developing line of business application which I am sure 99% of applications developed with ASP.NET MVC
are going to be for.

Separating concerns
On the official ASP.NET MVC site, you will find code examples that are directly fetching data using linq for
entities with linq queries directly in the controllers. Its not the responsibility of the controller to fetch data in
this manor, their is at least a layer or two missing between controller and data access. My concern is that we
will end up with same situation that is present in webforms where applications could be developed with most
of the application logic ending up in the controllers (like code behind in webforms). ASP.NET MVC already
is enforcing the separation of concerns for the view but not the controller and model. This is where the design
focus is needed.

Layers and tiers


Layering software is probably one of the most basic concepts in the software development that I think is under
estimated. I have seen on various projects in the past that its easy to get wrong with either too few or to many
layers. I find that logical layers can be defined by separating out the concerns at a high level.

A layer is a logical grouping of code that have a common concern. A tier is typically a physical boundary like
a server or client PC. A tier will contain at least one or more layers. Layers that are designed to run in the
same tier typically communicate in a finely grained manor (chatty interface) where commutating across tiers
should be coarse grain (chunky interface).

You must either be new to software development have lived in a cave or something if you have not heard of
n-tier architecture. I mention tiers here because i one of the principles that is usually forgotten about in that,
when communicating across tiers, do so in a coarse grain mannor.

High above the ground


The architecture that I am defining is nothing new, its a very common scenario using some Domain Driven
Design concepts. An alternative variant of this architecture is an SOA implementation. The SOA variant
would be the right choice an enterprise level application. The simpler variant of this architecture can be
migrated to the SOA variant.

Conceptual logical layers (simple variant)

UI tier ± This is the end users computer which will access your application via an internet browser. So the
user interface layer will contain (x)html and CSS. Plus this layer could contain JavaScript, silverlight, Flex
etc.

Presentation tier ± In this tier, you have two layers, the controller and presentation processes. The controller
layer will be invoked by the ASP.NET MVC framework in response to an http request. Your controller should
do nothing more than delegate any processing to logic in the Presentation Processes layer. The Presentation
Processes layer will typically request information from the service layer and process the service response by
mapping the information to an object that both Presentation Processes and the controller layer know about.
One of the benefits that this gives, is that you could develop your application that is decoupled from the
service layer.

Business tier ± This tier is very flexible in its implementation. In its most simplest form, it could be run in
process on web server that will actually remove the physically boundary between the business and
presentation tiers. For enterprise level implementations, this tier could be made up my many separate
applications that could be distributed across many application servers. Regardless of the physically boundary,
the Server layer should be thin as logic goes. The service layer provides an API to consumers. Beneath the
service layer you have the domain layer that contains the business logic and represents the business model.
The repository layer is bridge between your application logic and data persistence.

Persistence tier ± Does what it says on the tin, this would typically be your database server(s).
SOA variant

Here is the conceptual logically layers using an SOA approach.

The key difference here is that the service layer is broken up into multiple services that provide a distinct
function. SOA is a massive subject that i would not do it any justice trying to sell it, but using this approach
gives you many benefits that the development investment is worth it. If you are have your business tier
running out of process or on different services then WCF is the right technology for the job.

Although I have used ASP.NET MVC in the title of this post, this architecture is relevant for most technology
implementations. Everything below the controller layer is plain .net code that you could put any .net view
technology at the top of this architecture.

Architectural Rules
Rules are needed for any architecture, these are the common rules that I like to stick to.

0— !    c  c   



0— !    c         c  c  c  

0— D         c       c c   
      !

0— è     
                
è 
      !

0— O    "#     c    c   cc
D
   c              c   c
 

0— ˜       

ASP.NET MVC ± Creating an application with a
defined architecture
with 16 comments

Introduction
This post continues from this previous post. In this post I will be creating a ASP.NET MVC application using
the architecture described in the previous post. I will start by laying out the requirements, driving out the
behaviour through tests, implement Unity and create the layers for the UI, controller and Presentation
Processes.

Since writing my previous post, I have came across a blog which contains some really good tips / best
practices for ASP.NET MVC, which I recommend you read.

http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx

http://weblogs.asp.net/rashid/archive/2009/04/03/asp-net-mvc-best-practices-part-2.aspx

Requirements
So to start, we need requirements as development should be driven by requirements. In business analyst
language, this is the requirement that i will be working against.

å
— !   c    c c    
      c
     c     
à— =  
à— !  
à— !  $  
à— = 
à— =  

^here to start
ASP.NET MVC is focused around controllers and actions. So its a natural place to start describing how you
implement this architecture. On a day to day basic, I write tests first that drive out the behaviour. As my
application grows, my layers become cemented. The name of classes denote the responsibility and context.

Creating the Project


I am starting with a new ³Asp.Net MVC web application´ project called ³Web´ and a test project called
³Web.Specs´. My references are:
Web application

0— $%==  "&$=  


'


Test Project

0— ˜D "&
  
'

0— ˜#c  ˜O !"&˜#c 
O
˜D
'

0— ‰c $ "&‰c
$ 
'

0— $%==  "&$=  
( )
'  &$=  
'


Test first
Here is the test for the requirement defined above, putting the controller under test.

Y  
à  à
 
Y  

 
Y  à
  
Y 


Y 
 à
Y  à
à  
Y 
àà  
Y 
! à!à  
Y à  

   
 
"
# $Y%
Y    à &&  &à'& Y à  
"
 Y à  àà  àà 
( Y à ) Y à )

#%
Y  à*Y+,
"
 Y à )-à  à à
Y.( Y à )/+,
 àà -  àà Y *+,

 àà . Y à  àà /+à0 #%"
Y à )1,
1

# %
Y  à*àY *& &&  &à'& Y à  &à&&+,
"
 Y à  -2 . Y à /+,

 Y à )
3$ +$-/$
 Y à 2 +,,
Y+ Y à  ,

4  Y  Y - àà 
2 +,

 Y 
àY *àY +,
 Y 
) 4 **+,
à4+ Y à  àà 
2 4 ,

 Y à )
)   *+$-/$
 Y à 2 +,,
1
1
1

This test has driven out the ³CustomerController´, an interface called ³ICustomerAgent´ and two objects
called ³Customer´ and ³CustomerListViewModel´.

I have created another class library called ³Web.PresentationProcesses´ which doesn¶t have any additional
references. I have placed the Customer, CustomerListViewModel and the ICustomerAgent interface under a
new folder called ³Customers´ in the Web.PresentationProcesses assembly.

The code created so far is:

55 Y à 


   
! à!à  
Y à  
"
Y     Y à 
"
Y    " 1
Y   ) àYY " 1
Y   ) àY " 1
Y    " 1
Y    àY" 1
1
1

55 Y à 2 4à*


Y  
à  à
 

   
! à!à  
Y à  
"
Y     Y à 2 4à* 
"
Y  2 . Y à / Y à  " 1
1
1

55( Y à )

Y  
à  à
 

   
! à!à  
Y à  
"
Y  ' ( Y à )
"
2 . Y à / Y à 2 +,
1
1

55 Y à  àà 

Y  

 
Y 
! à!à  
Y à  

   
àà  
"
Y     Y à  àà 6 àà 
"
*à ( Y à ) Y à )

Y   à  2 4 -72 7

Y   Y à  àà +( Y à ) Y à ),
"

Y à )- Y à )
1

Y  4  Y 2 +,
"
à* - Y à 2 4à* 
"
 Y à  - Y à )
 Y à 2 +,
1

Y4+2 4 8à* ,
1
1
1

At this point the solution looks like this:


At the moment, the unit test will pass but if you run the application it will be broken because we don¶t have a
view and the customer controller doesn¶t have a default constructor.

Creating the View


å
— =       &= '  c&% '  

— * c              &è ' c 
&= è % $ '

+
— , c 
  
* c&  '   c    c  c
&) !  ' 

. /.9-
) à2+72 7872 787 Y à 7,9/.5 /


— !  c    cè 


.9:! -772Y-7 ;7 ! -7<54 5*5


 7
( -7 


4!.
! à!à  
Y à 
Y à 2 4à
* /79/

.9:( à   -7 à79/

. 6 à(=-7 à>7 à!  à *(=-7*7Y-7 7/
 . / Y à 2 .5 /
.5 6 à/

. 6 à(=-7 à?7 à!  à *(=-7 à7Y-7 7/

.?/ Y à 2 .5?/

.   **-7>7   -7@7à*-7>7/
.*/
./
./ 6.5/
./) àYY 6.5/
./) àY6.5/
./ 6.5/
./ àY6.5/
.5/
.5*/

.à*/
.9à*
Y à 
à3 + Y à -/"9/
  ./
.*/.9- Y à 
 9/.5*/
.*/.9- Y à 
) àYY 9/.5*/
.*/.9- Y à 
) àY 9/.5*/
.*/.9- Y à 
9/.5*/
.*/.9- Y à 
àY9/.5*/
  .5/
.91,9/
.5à*/
.5 /
.5 6 à/

The next stage is to have an IOC container resolve the dependencies for as at runtime.

Implementing Inversion of Control


Inversion of control / dependency injection has become very popular over the last couple of years and is a
great practice to use. Their are a quite a few containers on the market at the moment, all are very good and
apart from the common functionality that they all share. Some of the containers have unique qualities. I have
mainly use Castle Windsor, Unity and Ninject. My personal favourite is Ninject because it has a simple fluent
interface and contextual binding. At work we are using Unity mainly because its from Microsoft, but we do
have applications that use Castle Windsor and Spring.net. I find that once you know one container its really
ease to use another. Some of my fellow developers and I do experiment with different containers, although it
doesn¶t take long to swop them, using the Common Service Locator will make thing easier.

But before i start registering types, I common anti pattern that i have seen is that the container is defined in the
web application being at the top of layer stack and then references all of the layers below it so it can add the
types to the container. The solution to this is to pass via interface the container reference to each layer and
allow a each layer to register its types.

å
— O    c  c      c      c  c   c 
    c   


— (c  c      c    c-  -    
  
(c-  -   .      c    c  


+
— (c  c-  -         c    c  
  c  
*  /=  .    c cc  
c
*     0 c .      c c    
 c  


Some IOC containers allow you to register types in config, code or both. Although i don¶t like config anyway,
using config to register types can cause problems. Typically when you are refactoring code, like renaming or
moving types into other namespaces, that you miss out changing the config files. This results in exceptions at
runtime.

I am going to stick with using Unity here and I am going to reference the unity assemblies and use
MVCContrib unity assembly:

0— $  
-  
D 

0— $  
-  
, # 

0— $=  
D 


The best place to get the container created and configured is from within Application object in the
Global.asax. If you have used MonoRail and Castle Windsor together then this has the same usage pattern.

Y  

Y  

 
Y  

àY
Y  à à'
!  

Y  à


   
"
Y     )  à6)  à8( à)  à
"
   à à

Y    ( à à
"
"Y à1
1

( à( à)  à
à
"
"Y à1
1

à *à*)  à&+,
"
   àY + àY 
àY ,

 à'Y à+,
1

Y    à*   àY + àY à  ààY ,
"
àY
(à àY+7" àY 1
$*5"A('à17,

àY
 àY+7='Y 787" àà 15" à15"*178" àà -
7à 78 à-7(*$78*-771,
1

à* à'Y à+,
"
'+ à--Y ,
"
 à- à+,


! à!à  
! à!à   à*Y +,
à'Y+ à,


àà Y *
Y
 àà  à+à'+ àà  à,,
1
1
1
1

Creating the Agent


The next step to create a concrete class that implements the ICustomerAgent interface called
³CustomerAgent´. We didn¶t need to make it do any at the moment as we are still trying to get the application
working at runtime. Plus when we do start making the ³CustomerAgent´ do something we will drive it from a
test first. We will create this class next to where the interface lives.

Y   
Y  
à  à
 

   
! à!à  
Y à  
"
Y     Y à )6( Y à )
"
Y  2 . Y à / Y à 2 +,
"
àà(   *3$ à+,
1
1
1

I have created another class assembly called ³Web.Container.Interfaces´ which contains just one interface
called ³IModule´ which looks like this.

Y  à à'
!  


   
à
('  
"
Y  ' (à*Y 
"
à* à'Y+( à à,
1
1

This class assembly only references Unity and will be referenced by other assemblies like
³PresentationProcesses´.

Now to create the PresentationProcessesModule.

Y  à à'
!  

Y 
à
('  
Y 
! à!à  
Y à  

   
! à!à   
"
Y    ! à!à   à*Y 6(à*Y 
"
Y  à* à'Y+( à à,
"
 à
 .( Y à )8 Y à )/+,
1
1
1

Now to run the app, and now when you click the ³list´ link on the home page, you should get an ³The method
or operation is not implemented.´ exception page, which is expected at this time. What this does prove it that
the IoC is working correctly.

The next stage in this process would be drive out getting some real data from somewhere and will get returned
from the CustomerAgent. Which is going to be in my follow on post, but for now we can simply new up a
collection with some new¶d up customer objects as shown below.

Y  
à  à
 

   
! à!à  
Y à  
"
Y     Y à )6( Y à )
"
Y  2 . Y à / Y à 2 +,
"
Y2 . Y à /
"
 Y à 
"
 -7 à >78
) àYY -7>?BCD78
) àY -7  àY >78
 -7à à78
 àY-73 *7
18
 Y à 
"
 -7 à ?78
) àYY -7DCB?>78
) àY -7  àY ?78
 -7à à  78
 àY-73 *7
18
1
1
1
1

Run the app


Now when you run the application, click on the ³list´ link in the menu. You should now get this following
page.
Moving on
What we have got is an ASP.NET MVC that has unity in place to resolve types at runtime. We have types
(customer and customer ViewModel) defined in the presentation processes layer that the view are bound to.
The customer agent returns instances of these types.

The next steps will be to change the Customer agent to get the data from somewhere. This will be driven out
via tests. This is going to be the focus on my next pos

Anda mungkin juga menyukai