From RAD to test driven ASP.NET website
Both unit testing and R.A.D. (Rapid Application Development) impacted quite deeply my insights over software development. Yet, I have found that combining those two approaches within a single ASP.NET project is not that easy especially if you want to keep the best of both worlds. There are at least א (alef zero) methods to get the problem solved. Since my blog host does not provide yet that much disk storage, I will only describe here 2.5 design patterns that I have found to be useful while developing ASP.NET websites.
- R.A.D: all your 3-tier layers (presentation, business logic and data access) get compacted into a single ASPX page. ASP.Net 2.0 makes this approach very practical thanks to a combination of GridView, DetailsView and SqlDataSource.
- CRUD layer: The business logic and the data access are still kept together but removed from the ASPX page itself. This design pattern (detailed below) replaces the SqlDataSource by a combination of ObjectDataSource and library classes.
- Full blown 3-tier: Like, above but the business logic and the data access gets separated. Compared to the CRUD layer approach, you get extra classes reflecting the database objects.
R.A.D.
Using combinations of GridView, DetailsView and SqlDataSource, you can fit your 3-layers a single ASPX page, the business logic being implemented in code-behind whenever required. This approach does not enable unit-testing but if your project is fairly simple, then R.A.D. works with a dramatic productivity. For example, PeopleWords.com
has been completely developed through R.A.D (with a single method added to the Global.asax file that logs all thrown exceptions). I would maybe not defend R.A.D. for large/complex projects, but I do think its very well suited for small projects and/or drafting more complicated ones.
The forces behind R.A.D. :
- (+) Super-fast feature delivery: a whole website can designed very quickly.
- (+) The code is very readable (not kidding!): having your 3-layers in the same place makes the interactions quite simple to follow.
- (+) Facilitate the trial & error web page design process: It’s hard to get a web page very usable at once, R.A.D. let you re-organize web page very easily.
- (-) If the same SQL tables get reused between ASPX pages, then data access code tends to be replicated each time (idem for business logic).
- (-) No simple way to perform unit testing.
- (-) No structured way to document your code.
“CRUD layer” design pattern
The main purpose of the “CRUD layer” is to remove the business logic and the data access from the ASPX page to push it into an isolated .Net library. Yet, there are several major constraints associated to this process. The first constraint is to ensure an easy migration from R.A.D. to “CRUD Layer”. Indeed, R.A.D. is usually the best prototyping solution. Therefore the main issue is not to implement from scratch but to improve the quality of the R.A.D. draft implementation. The second constraint is to maintain business logic and data access design as plain as possible (verifying the YAGNI principle among others). The CRUD layer is an attempt to address those two issues.
The CRUD layer consists in implementing a class
public class FooCrudLayer
{
public FooCrudLayer() { ... } // empty constructor must exists
public DataTable GetAllBar( ... ) // SELECT method
{
DataTable table = new DataTable();
using (SqlConnection conn = new SqlConnection( ... ))
{
conn.Open();
SqlCommand command = new SqlCommand( ... , conn);
using (SqlDataReader reader = command.ExecuteReader())
{
table.Load(reader);
}
}
return table;
}
public void Insert( ... ) { ... } // INSERT method
public void Delete( ... ) { ... } // DELETE method
public void Update( ... ) { ... } // UPDATE method
}
Notice that the select method returns a DataTable
whereas most ObjectDataSource tutorials would return a strongly-typed collection List<Bar>
. The presented approach has two benefits. First, the migration from the SqlDataSource is totally transparent (including the sorting and paging capabilities of the GridView).
<ObjectDataSource Id="FooSource" runat="server"
TypeName="FooCrudLayer"
SelectMethod="GetAllBar"
InsertMethod="Insert"
DeleteMethod="Delete"
UpdateMethod="Update" >
... (parameters definitions)
</ObjectDataSource>
Second, database objects are not replicated in the .Net code. Indeed, replicating the database objects at the .Net level involves some design overhead because both sides (DB & .Net) must be kept synchronized.
The forces behind the CRUD layers
- (+) Very small design overhead compared to R.A.D.
- (+) Easy migration from R.A.D.
- (+) Unit testable and documentable.
- (-) No dedicated abstraction for business logic.
- (-) Limited design scalability (because it’s not always possible to avoid code replication).
Full blown 3-tiers
I won’t say much about this last option (this post is already far too long), but basically, the 3-tier design involves to strong type the database objects in order to isolate business logic from data access. This approach comes as a large overhead compared from the original RAD approach. Yet, if your business logic gets complex (for example workflow-like processes) then there is no escape, layers have to be separated.