Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
(My) Default ASP.NET Architecture

In developing any software application, it's important to keep things as simple as possible and add complexity only when needed.  (I spoke about this in a previous post, Planning for vs. Reacting to Change.)  On the flip-side, a certain amount of architecture may be assumed at the start of a project depending on the type of application being developed.  Selecting an appropriate, default architecture provides your application with a solid foundation and offers guidance for other developers, and yourself, for further development.  This entry describes a suggested, default architecture for data-driven, ASP.NET applications for both individual and team development.  Disclaimer:  it is important to note that there are many appropriate architectural foundations and this is just one of them.

Architectural Assumptions

In providing an architectural approach, some assumptions are made concerning the project goals.  These assumptions should be used as a litmus test to determine if the described architecture may be an appropriate fit for your ASP.NET application.

  • Testability is of utmost importance.  If test-driven development (TDD) will not be practiced, then most of what follows becomes a moot point.
  • Separation of concerns is strongly enforced.  This policy is enforced using separate, physical assemblies which communicate with each-other via dependency-inversion with interfaces.
  • Presentation and business logic layers are "data-layer agnostic" as much as reasonably possible.  Like the previous point, this is more for testability than it is for the expectation that the data-layer will be actually switched out at a later time.  The phrase "reasonably possible" is mentioned because there is often an unavoidable amount of implicit, data-layer assumptions built into both the presentation and business logic layers.  For example, if the data-layer uses proxies to watch for an "is-dirty" state, as NHibernate does, then the business layer need not concern itself with these details; but switching to a less-autonomous data-layer may force the business layer to take on this responsibility.  Certainly, with enough effort, the business layer could become completely data-layer agnostic; but in most cases, the extra effort isn't worth the added cost of premature generalization.
  • NHibernate is used as the data-layer ORM.  A data-access object (DAO) abstract factory pattern is employed to easily switch between production and "mock" DAOs for unit-testing. 
  • Model-View-Presenter (MVP) is optionally employed to keep code-behind pages as part of the view, pure and simple.  MVP is the simplest solution I have found for making ASP.NET code-behind logic more maintainable and testable.  But MVP comes with a cost - it adds another layer of indirection and complexity to the application; therefore, the "Presenters" layer (the "P" in MVP) may be considered as an optional piece to the suggested architecture.  Furthermore, the benefits of MVP may be employed later in the project life-cycle, when warranted, without having to modify the existing presentation layer.  (Martin Fowler has suggested that MVP be split into Supervising Controller and Passive View.  What's described below is consistent with Supervising Controller.)

In a Nutshell

Below is a graphical summary of the architecture.

Application Architecture

In the above diagram, each raised box represents a distinct specialization of the application. Each gray box then represents a separate, physical assembly; e.g. MyProject.Web.dll, MyProject.Presenters.dll, MyProject.Core.dll, etc. The arrows represent assembly dependencies. For example, the .Web assembly depends on the .Presenters and .Core assemblies.

The assemblies avoid bi-directional dependency using the techniques "Dependency Inversion" and "Dependency Injection."  ("DI" in the diagram should be read as "dependency injection.")  To illustrate, note that the .Core assembly contains, along with domain objects, the DAO interfaces.  The .Data assembly contains classes which inherit from these interfaces to define the DAO implementations.  The .Core assembly is then given its DAO dependencies from another layer, such as from .Presenters or .Tests.  Furthermore, another "service layer" could be employed to centralize the DAO-creation services.  (Martin Fowler discusses the service-layer approach in Patterns of Enterprise Application Architecture.)  One approach that I've had success with is leveraging the Castle Windsor project to inject DAO dependencies.  Note that this third party tool adds another layer of complexity to the application and should be carefully considered before use (aka "happy fun ball").

Implementation Details

In all seriousness, an entire book could be written to describe the preceding diagram in full detail.  What follows are a number of articles which explain the architecture in greater depth.  (Warning:  utterly shameless plugs for articles I've written!)

Dependency Injection for Loose Couplinghttp://www.codeproject.com/cs/design/DependencyInjection.asp.  As mentioned previously, the architecture uses dependency injection (DI) throughout the application to keep the application layers loosely coupled.  This article offers a simplified overview of the technique - it assumes DI is being performed from one layer to another layer.  For a more complete discussion of Dependency Injection, or more cryptically called "Inversion of Control," be sure to read Martin Fowler's article on the subject.  Fowler's article also goes into greater depth of using DI "containers."

NHibernate Best Practices with ASP.NEThttp://www.codeproject.com/aspnet/NHibernateBestPractices.asp.  This audaciously named article gives a good overview of the abstract data-access-object factory used within the architecture and how it's used with test-driven development.  This article includes many details of the suggested architecture, sans the inclusion of MVP and Castle Windsor.

Using NHibernate with Multiple Databaseshttp://www.codeproject.com/useritems/NHibernateMultipleDBs.asp.  If your project requires communications with multiple databases, then this article provides an extension to the previous NHibernate article.  There is very little, published content concerning this subject and I am open to hearing alternative approaches.

Model-View-Presenter with ASP.NEThttp://www.codeproject.com/aspnet/ModelViewPresenter.asp.  There are many resources available for learning about MVP; the emphasis here is with ASP.NET and a variant I call "User-Control as View."  The Model-View-Presenter pattern may be employed in the architecture to allow code to be unit-tested that would usually have been found in the code-behind pages.  The downloadable sample “MVP Enterprise Solution,” found within the article, includes a working example of using the Castle Windsor project within ASP.NET.  As noted previously, both MVP and the Castle Windsor project add additional layers of complexity to the application and should only be employed if their benefits of maintainability and testability are warranted in your environment.

Parting Thoughts

There is no single best-practice for architecting ASP.NET, web applications; but, after taking described assumptions into account, what I've described may serve as a solid, default architecture for future project work.  At the very least, it provides a pool of ideas for consideration.  As always, I'm open to thoughts, rebuttals, criticisms and suggestions and welcome your comments.

Billy


Posted 10-05-2006 10:56 AM by Billy McCafferty

[Advertisement]

Comments

Derik Whittaker wrote re: (My) Default ASP.NET Architecture
on 10-05-2006 4:39 PM

Nice post, a lot of this will also work for WinForms developers as well..

Nice.

Jim Bolla wrote re: (My) Default ASP.NET Architecture
on 10-05-2006 4:45 PM

Awesome post. Our setup on our most recent projects is nearly identical, and trying to refactor the others to get them there.

Joe Niland wrote re: (My) Default ASP.NET Architecture
on 10-06-2006 5:23 AM

Excellent post. Lots of food for thought here. I am very intrigued by your statement "Business logic should be easily testable without depending on a live database" from the NHibernate best practices article. I have actually always unit-tested the BL by using a database so this is an alternative I will explore.

Greg Young wrote re: (My) Default ASP.NET Architecture
on 10-06-2006 1:48 PM

I can't agree enough with unit tests not hitting the database, people really seem to miss the boat on this one. I do it a bit differently though, I use the repository pattern which also helps to provide me a strong contract at the domain level.

Billy McCafferty wrote re: (My) Default ASP.NET Architecture
on 10-06-2006 2:07 PM

On my last larger project, we unfortunately made the decision to use a live DB for our unit tests and left ourselvs no simple way to mock the DB without something like TypeMock.  When the number of unit tests began approaching 500, running the entire suite of tests would take about 15 minutes.  When it became prohibitive to run all the unit tests, we began running unit tests that were applicable to "our area."  Within just a couple of weeks the unit testing effort completely failed as we unknowingly started breaking "other people's" unit tests.  It also became a serious pain in the *** to keep the DB up to date with correct test data.

A few hard-learned lessons from that:

* Keep DB-specific unit tests isolated from business-logic unit tests.

* Use mock-data-access objects within the business-logic unit tests.

* Run "everybody's" unit tests as frequently as possible.  (CruiseControl on an hourly or nightly basis should suffice.)

* Use something like NDbUnit for any unit tests that test the data-access layer with a real DB.

Joe Niland wrote re: (My) Default ASP.NET Architecture
on 10-06-2006 7:29 PM

Thanks for explaining that further - in the project i'm currently in, the CC.NET build takes 8-10 mins mostly because of rebuilding the db and running the tests.. I guess I hadn't thought about it properly and thought it was a necessary evil.. We still run them all and put up with it because the alternative would be what you described, Billy, and that defeats the purpose of continuous integration really doesn't it..

Definitely agree with all those lessons - I will continue to explore how to improve things over here.. Thanks..

Dave Donaldson wrote re: (My) Default ASP.NET Architecture
on 10-09-2006 5:05 PM

Out of curiosity, where do you manage transactions? And where do you actually manage those transactions, in the data layer or the business logic?

Billy McCafferty wrote re: (My) Default ASP.NET Architecture
on 10-09-2006 5:30 PM

A single transaction is applied to the entire HTTP web request using an IHttpModule.  The transaction is begun during HTTP begin request and it gets committed during HTTP end request.  It does this at begin/end to support lazy loading of collections throughout the lifetime of the request.  The technique/pattern itself is called "open session in view."

Dave Donaldson wrote re: (My) Default ASP.NET Architecture
on 10-09-2006 6:57 PM

Hmmm... interesting. I suppose this is from the NHibernate best practices article?

Billy McCafferty wrote re: (My) Default ASP.NET Architecture
on 10-09-2006 7:27 PM

That's correct, you can also find quite a bit on the web about "open session in view" at http://www.google.com/search?hl=en&q=open+session+in+view

Although it works great for web applications, it isn't necessary for WinForms development.

Christopher Bennage wrote re: (My) Default ASP.NET Architecture
on 10-11-2006 9:53 AM

Very helpful stuff.  Thanks for the post.  

I was just reading about different approaches to DB/unit testing in Jimmy Nilsson's book, Applying Domain-Driven Design and Patterns.

I also wish that I had been better acquainted with DI on my last big project. :(

Vikas Kerni wrote re: (My) Default ASP.NET Architecture
on 10-31-2006 10:25 PM
Billy McCafferty wrote re: (My) Default ASP.NET Architecture
on 11-01-2006 12:05 PM

Thanks for the link Vikas...your overview is one of only a few I've seen that attempt to include SOA as a first class citizen...it's a timely addition.

Billy

Vikas Kerni wrote re: (My) Default ASP.NET Architecture
on 11-06-2006 11:25 PM

Thanks for you encouraging remarks. This architecture was more aimed at good distributed architecture because lot of places, database server is protected by a firewall. I thought a good architecture should be good enough to scale to that scenario.  SOA imho is more than that.

SOA = Good Distributed System + Message Version Control + more loose coupling.

The architecture example that I showcased was missing Message Version Control and is sharing Code over Contract. Hopefully, Windows Communication Foundation will make transforming a good distributed architecture to Service Oriented architecture more seamless and less painful.

Billy McCafferty wrote Custom Collections with NHibernate, Part III: Refactored
on 12-07-2007 12:45 PM

Download the source code for this post. In part I and part II of this series, we established our goals

Ian Joyce » Blog Archive » links for 2007-04-16 wrote Ian Joyce » Blog Archive » links for 2007-04-16
on 12-13-2007 3:23 PM

Pingback from  Ian Joyce  » Blog Archive   » links for 2007-04-16

Architecture of the application « Projekti Kihihi wrote Architecture of the application « Projekti Kihihi
on 01-08-2008 3:57 PM

Pingback from  Architecture of the application « Projekti Kihihi

http://devlicio.us/blogs/billy_mccafferty/archive/2006/10/05/_2800_My_2900_-Default-ASP.NET-Architecture.aspx wrote http://devlicio.us/blogs/billy_mccafferty/archive/2006/10/05/_2800_My_2900_-Default-ASP.NET-Architecture.aspx
on 03-25-2008 4:27 AM
Mihai Danila wrote re: (My) Default ASP.NET Architecture
on 04-25-2008 1:45 PM

I don't see why HBM files would be part of the Core. They should definitely be a part of the data access layer. Put it differently, when you switch to a DAL that does not use NHibernate, what will the purpose of these mappings be? They are intrinsicly correlated to the DAL, not the Core.

Billy McCafferty wrote re: (My) Default ASP.NET Architecture
on 04-25-2008 1:54 PM

I couldn't agree more!  Here is the primary reason for putting them into core:  convenience of maintainability.  And since they can just site in core without requiring that core have any direct dependency on a data access mechanism, such as NHibernate, they don't muddle the separation of concerns with respect to assembly references.  For this reason, I find it incredibly helpful, for maintainability purposes, to have the HBMs sitting right next to the domain objects they describe...but there's absolutely nothing to prevent you from maintaining them in the data layer, instead, if you feel more comfortable with them there.

ASP.NET MVC Archived Buzz, Page 1 wrote ASP.NET MVC Archived Buzz, Page 1
on 07-01-2008 5:01 PM

Pingback from  ASP.NET MVC Archived Buzz, Page 1

Jogsaqxv wrote re: (My) Default ASP.NET Architecture
on 08-04-2010 11:12 AM

kkCiLz Mvtgcrz hrled mnqpir ijfp qmcpkydfgh aajv zhaiyq jszrqpnzmg.

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)