Web Design, Programming, Tutorials
Programming
Setting up Structure Map
May 14th
StructureMap
If you haven’t heard about dependency injection, it is something you should definitely look into. It can save tons of time when coding.
StructureMap is a dependency injection framework that makes it easy to create new instances of objects and even cache an instance of an object so various references to the object use the same instance. This is very useful with objects such as database connections.
You can download the library from the StructureMap Home Page. The example used in this post is using StructureMap 2.6.1. I’m also using Visual Basic. I had a tough time finding information on how to use StructureMap with Visual Basic, so hopefully, this will help someone else.
Setting Up MVC
When using StructureMap with ASP.NET MVC 1.0, I’ve found it works best if a folder is created in each project; Main, Data, and Service; where the StructureMap files are stored. This keeps them organized and makes them easy to find as you need to update them. A reference to the StructureMap DLL will need to be added to each project as well.
In the main project’s StructureMap folder, the following files are needed:
StructureMapControllerFactory
Imports System.Web.Mvc
Imports StructureMap
Public Class StructureMapControllerFactory
Inherits DefaultControllerFactory
Protected Overrides Function GetControllerInstance(ByVal controllerType As System.Type) As System.Web.Mvc.IController
Return ObjectFactory.GetInstance(controllerType)
End Function
End Class
This class sets up a factory class to get an instance of the StructureMap factory. It will be used to replace MVC’s default controller factory so that StructureMap will be in charge of creating our controllers.
BootStrapper
Imports StructureMap
Imports YourProject.Data
Imports YourProject.Services
Public Class BootStrapper
Public Shared Sub ConfigureStructureMap()
ObjectFactory.Initialize(AddressOf StructureMapRegistry)
End Sub
Private Shared Sub StructureMapRegistry(ByVal x As IInitializationExpression)
x.AddRegistry(New MainRegistry())
x.AddRegistry(New DataRegistry( _ ConfigurationManager.ConnectionStrings("iSeries").ConnectionString, _ ConfigurationManager.ConnectionStrings("Interbase").ConnectionString))
x.AddRegistry(New ServiceRegistry())
x.Scan(AddressOf StructureMapScanner)
End Sub
Private Shared Sub StructureMapScanner(ByVal scanner As StructureMap.Graph.IAssemblyScanner)
scanner.Assembly("YourProjectNamespace")
scanner.Assembly("YourProjectNamespace.Data")
scanner.Assembly("YourProjectNamespace.Services")
scanner.WithDefaultConventions()
End Sub
End Class
This class is where StructureMap is configured. Within the StructureMapRegistry method, each AddRegistry call is made to include each of our project’s registry files, which have yet to be created. We will get to that in a moment, but notice that the DataRegistry is accepting values for connection strings. This is how information can be passed into a class being created by StructureMap when you would like to keep the configuration settings within the Web.config file.
Also, within the StructureMapScanner method, an entry needs to be made to scan each project’s namespace.
MainRegistry
Imports DCS.Data
Imports StructureMap.Configuration.DSL
Public Class MainRegistry
Inherits Registry
Public Sub New()
[For](Of IWebContext)() _
.Use(Of WebContext)()
End Sub
End Class
Each project will have its own registry file where you can specify that Interface-A should create an instance of Class-A. In the example above, I want StructureMap to create an instance of WebContext (from the main project) when it comes across a reference to IWebContext.
In the Data project’s StructureMap folder you will need:
DataRegistry
Imports StructureMap.Configuration.DSL
Imports Spartan.DAO.Database
Public Class DataRegistry
Inherits Registry
Public Sub New(ByVal iSeriesConnectionString As String, ByVal interbaseConnectionString As String)
'Data Connections.
[For](Of YourDataContext)() _
.HybridHttpOrThreadLocalScoped _
.Use(Function() New YourDataContext())
[For](Of DB2Connection)() _
.HybridHttpOrThreadLocalScoped _
.Use(Function() New DB2Connection(iSeriesConnectionString))
[For](Of InterbaseConnection)() _
.HybridHttpOrThreadLocalScoped _
.Use(Function() New InterbaseConnection(interbaseConnectionString))
'Repositories.
[For](Of IShiftRepository)() _
.Use(Of ShiftRepository)()
End Sub
End Class
The DataRegistry is interesting because we are passing in two connection strings from the Web.config file. The first data connection is for Microsoft SQL, the second for DB2 and the third for Interbase. The second and third are using the DB2Connection and InterbaseConnection classes which are home grown database connection class that I wrote. All three of these database connections are being cached by using the .HybridHttpOrThreadLocalScoped property. Using this will make the database cached instance work for both your live program and your unit testing.
The entry under repositories is what all remaining entries would look like within the registry class. Since the data project contains our repositories, they would all be listed here.
And, in the Service project’s StructureMap folder you will need:
ServiceRegistry
Imports StructureMap.Configuration.DSL
Public Class ServiceRegistry
Inherits Registry
Public Sub New()
[For](Of IShiftService)() _
.Use(Of ShiftService)()
End Sub
End Class
The service registry is similar to that of the main project. All service classes found in the service project would be listed here.
Changes to the Global.asax
The last thing to do is setup the Global.asax file to load StructureMap. Make the following changes to the Application_Start method:
Sub Application_Start()
RegisterRoutes(RouteTable.Routes)
'StructureMap
BootStrapper.ConfigureStructureMap()
ControllerBuilder.Current.SetControllerFactory(New StructureMapControllerFactory())
End Sub
Now, the default controller factory used by MVC will be overridden by our StructureMapControllerFactory. When a controller is created, it will now use StructureMap.
So, how do you use this when creating a class? This is the easy part.
StructureMap will use the greediest constructor from your controller, meaning, if you have two constructors, the one with the most parameters will be used. Each parameter references an Interface and sets a class variable for the object. StructureMap uses the registry classes that we setup to map an interface to a real object.
Example Controller
Imports YourProject.Data
Imports YourProject.Services
Imports StructureMap
Public Class ExampleController
Inherits System.Web.Mvc.Controller
Private _shiftService As IShiftService
Private _webContext As IWebContext
Public Sub New( _
ByVal shiftService As IShiftService, _
ByVal webContext As IWebContext)
_shiftService = shiftService
_webContext = webContext
'Initialize service classes.
_shiftSummaryService.Initialize(New ModelStateWrapper(Me.ModelState))
End Sub
...
All that you need to do is create a constructor, pass in all the interface references you need for the controller, then store them in a class variable. If you need to use the ModelState for validation within your service class, the best way I’ve found to do this is use an Initialize function in your service class to pass in the ModelState. This is because the ModelState is not yet created by MVC when the service classes are created by StructureMap.
When StructureMap creates the instance of the ShiftService, it will look at the greediest constructor from that class to create instances for any required parameters. This continues for all classes that are referenced.
Once the solution has been configured to use StructureMap, the only thing you’ll need to maintain will be the registry files as you add new classes and interfaces to your project. Don’t forget to add a registry entry or you will recieve an exception.
Using the Linq IQueryable Toolkit
Feb 10th
The IQToolkit provides you with a framework for building your own LINQ providers. The toolkit comes with providers for Microsoft SQL, Microsoft SQL CE, Access, MySQL, and SQLite.
This post will show you how to use the toolkit to talk to Microsoft SQL. Later, I’ll be attempting to write my own providers for DB2 and Interbase 7.1.
Download the toolkit
First thing to do is download the IQToolkit (version 0.16a as of this posting).
Reference the toolkit
I setup a console project to test the toolkit. Within your project, create a reference to the IQToolkit.Data.dll and the IQToolkit.dll. Then, add a reference to the dll for the provider you wish to use. In this case, we are using the IQToolkit.Data.SqlClient.dll for Microsoft SQL.
Table objects
Next, you will need to create a class object for each table you need to work with in the database. I have a table that has two fields: PLCID and SummaryProdMode. Create a class object to hold these properties and then use the Column attribute to bind the property to the table column.
Imports IQToolkit.Data.Mapping
Public Class Machine
Private _id As Integer
Private _mode As Boolean
<Column(IsPrimaryKey:=True)> _
Public Property PLCID() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
<Column()> _
Public Property SummaryProdMode() As Boolean
Get
Return _mode
End Get
Set(ByVal value As Boolean)
_mode = value
End Set
End Property
End Class
DataContext
The data context contains read only properties for each database table being used. In this case, I’m dealing with the DCM_Machines table.
Imports IQToolkit
Imports IQToolkit.Data.Mapping
Imports System.Linq
Public Class MachineDataContext
Private _provider As IEntityProvider
Public Property EntityProvider() As IEntityProvider
Get
Return _provider
End Get
Set(ByVal value As IEntityProvider)
_provider = value
End Set
End Property
<Table()> _
Public ReadOnly Property Machines() As IEntityTable(Of Machine)
Get
Return EntityProvider.GetTable(Of Machine)("DCM_Machines")
End Get
End Property
Public Sub New(ByVal provider As IEntityProvider)
_provider = provider
End Sub
End Class
Using the provider
Use the following code to setup and use the provider with LINQ.
Imports IQToolkit.Data
Module Module1
Sub Main()
Dim provider = DbEntityProvider.From( _
"IQToolkit.Data.SqlClient", _
"YOUR_CONNECTION_STRING", _
"Machine")
Dim db = New MachineDataContext(provider)
Dim machs = (From m In db.Machines _
Select m).ToList()
For Each m In machs
Console.WriteLine("id=" & m.PLCID)
Next
Console.ReadLine()
End Sub
End Module
ASP.NET MVC – Organizing your solution
Feb 4th
Keeping your project files organized will help you navigate through your projects and help you find what you are looking for to make changes faster.
Here are some tips that I use to help keep my ASP.NET MVC solutions organized:
- Use multiple projects to separate sections of the program.
- Use folders to group interfaces, classes, and factories that deal with an object.
- If objects can be used by other programs, put them in their own class library project.
Option 1
When beginning a new program, I usually start by creating an ASP.NET MVC solution with 4 projects:
- The main project – which contains the MVC components of the application such as Controllers, Views, CSS, and JavaScript files.
- The unit testing project – which contains all of the requirements for the application along with any fake repositories used for testing.
- The data project – which contains the repositories and domain objects used by the application.
- The services project – which contains the service objects that are the middle-man between the Controller and the Repository.
Option 2
Folders are used within the projects to further organize the code files within. The data project could have a domain object called Operation. Domain objects are placed in a folder with the same name as the object. Interfaces, Repositories, and Factories that deal with the object are also placed in the domain object folder.
The service project is organized similar to the data project.
The unit test project is slightly modified from the default to create a folder which contains all fake repository objects and another folder to contain requirement tests. Depending on the detail of unit testing you wish to utilize, you may have unit tests that verify application requirements and/or unit tests that further verify other parts your code.
In my opinion, you can write application requirements that detail exactly what the program should do and as long as you unit test these requirements, you don’t need to waste time trying to unit test 100% of your program. If a bug is found later, it will most likely be because it was not an application requirement. Then, add a new requirement, write a unit test, and make the application pass the new test.
Option 3
Where I work, we have various databases that we need to communicate with. We wanted a way to use these databases the same way every time without having to remember all of the different objects and names that needed to be used by each. So, I developed a parent wrapper class around the basic database functionality and then individual wrappers around each database object that inherited from the parent wrapper. Now, we can call up any of the database connections and work with them in exactly the same way, the only difference between them is the call to the factory when creating the instance.
These database wrappers were added to their own separate project so that in future applications, we could continue to reuse them.
Selecting the last item of a collection using Linq-to-sql
Jan 29th
Today I had some problems with Linq-To-SQL. I needed to get the last item from a collection and only return items where the date was older than two weeks from now.
Data Tables
The main items that I want to return from Linq are Revision records. A Revision has a collection of Document_History records that keep track of updates to the revision. The Document_History record has a Date field that is the date and time when the history entry was inserted, or when the revision was modified.
The Problem
When using the data objects that Linq generates, you are allowed to perform some functions, such as Last and Reverse on the collection of items.
Revision.Document_History.Last.Date
The above code would return the most recent date, or the last time a revision was updated.
But, when you try to use the Last method within a Linq statement, Visual Studio throws an error.
The Fix
To get around this error, I figured out that you can select the latest date using a sub query and the Max method. Below is an example of the Linq-To-SQL code that worked for me.
Dim revs As List(Of Revision) = From r In dbcontext.Revisions _
Where (From h In r.Document_History _
Select h.Date).Max <= cutoffDate _
Select r
Dependency Injection
Jan 27th
Dependency Injection is a design pattern that is used in Object Oriented Programming to create a relationship between two objects without object A depending on object B. Instead, through the use of an Interface, a place-holder for object B can be referenced in object A. Then, object B can be passed into object A where it is utilized. If there is an object C that uses the same interface as object B, it may also be passed into object A.
A Tightly Coupled Relationship
Below is an example of two classes that are tightly coupled, meaning that if we want to replace object B with another class, we must change the code of object A. Object A is dependent on object B to execute.
Public Class ObjectA
Private _obj As ObjectB
Public Sub New(ByVal obj As ObjectB)
_obj = obj
End Sub
Public Sub RunB()
_obj.Run()
End Sub
End Class
Public Class ObjectB
Public Sub Run()
Console.WriteLine("Run from objectB")
End Sub
End Class
A Loosely Coupled Relationship
Now, let’s look at a better approach that will allow us to replace ObjectB with another class using the same interface or replace it with a child class of ObjectB.
Below, the ObjectB class now implements the IObjectB interface and the references to ObjectB in ObjectA have been replaced with a reference to the interface IObjectB instead.
Public Interface IObjectB Sub Run() End Interface
Public Class ObjectA
Private _obj As IObjectB
Public Sub New(ByVal obj As IObjectB)
_obj = obj
End Sub
Public Sub RunB()
_obj.Run()
End Sub
End Class
Public Class ObjectB
Implements IObjectB
Public Sub Run() Implements IObjectB.Run
Console.WriteLine("Run from objectB")
End Sub
End Class
Now, ObjectA can execute no matter if we pass into it a copy of ObjectB or another class that implements the IObjectB interface. The Interface ensures that all objects that implement it will have a subroutine called Run. That is all that ObjectA needs to know. This breaks ObjectA’s dependency on ObjectB.
The Repository Pattern
Jan 26th
The repository pattern is a design pattern that creates repositories of data that talk to a data source. These repositories are used to segregate the code that talks to a data source from the rest of your program. For example, your code that communicates to a database connection is stored within a repository class. Communication between your program and the database is all funnelled through the repository class.
In Domain Driven Design, you create data objects that are logical representations of the data stored within a data source; such as a database. These data objects may not mimic the design of a database, but instead be a more logical grouping of properties and methods. The repository pattern can be used to create a repository class for each data object, where that object can then be created, read, updated, or deleted (CRUD) from the data source.
Using the repository pattern with Dependency Injection allows for the repository class to be mocked during testing, where a more controlled and faster repository can be created. Also, if the data source changes to a XML file or another database, the repository class can be replaced.
The Repository pattern is one of the design patterns that I use to build a more maintainable and flexible application.
Vector class for Lotus Script
Jan 25th
The following is a script library written in Lotus Script to allow programmers to use a vector in their Lotus Script code.
Option Declare Option Base 0 Public Class Vector 'Private data members Private vArray As Variant Private vCapacity As Integer Private vSize As Integer Public Sub New() 'Initialize variables Call SetCapacity(0) Call SetSize(0) Call Reserve(10) End Sub Public Sub Delete() Call EraseAll() 'Clear the array Set vArray = Nothing End Sub Private Sub SetCapacity(newCapacity As Integer) vCapacity = newCapacity End Sub Public Function Capacity() As Integer Capacity = vCapacity End Function Private Sub SetSize(newSize As Integer) vSize = newSize End Sub Public Function Size() As Integer Size = vSize End Function Public Function Empty() As Boolean 'Declare variables Dim valid As Boolean 'Get the size If (Size() = 0) Then valid = True Else valid = False End If 'Return if the vector is empty Empty = valid End Function Public Sub Reserve(minCapacity As Integer) 'Declare variables Dim openElements As Integer Dim newCapacity As Integer 'Initialize variables openElements = Capacity() - Size() newCapacity = Capacity() 'Check if there are any open slots remaining If (openElements = 0) Then newCapacity = (Capacity() * 1.5) End If 'Make sure that the minimum capacity is stored If (newCapacity < minCapacity) Then newCapacity = minCapacity End If 'Check if a change to the capacity is being made If (Not(Capacity() = newCapacity)) Then 'Check the size to determine if the array's contents need to be 'saved while redimensioning the array If (Size() = 0) Then 'Nothing stored yet, perform redim Redim vArray(newCapacity) Else 'Keep the array's contents while extending its boundaries Redim Preserve array(newCapacity) End If 'Update the capacity Call SetCapacity(newCapacity) End If End Sub Public Sub PushBack(newItem As Variant) 'Check if the passed item/items is actually an array or a list If ((Not(Isarray(newItem))) And (Not(Islist(newItem)))) Then 'Add the single item Call AddItem(newItem) Else 'Add multiple items Forall item In newItem Call AddItem(item) End Forall End If End Sub Private Sub AddItem(newItem As Variant) 'Declare variables Dim newSize As Integer 'Initialize variables newSize = Size() + 1 'Make sure there is enough capacity in the vector Call Reserve(newSize) 'Insert the item into the vector If (Isobject(newItem)) Then Set vArray(Size()) = newItem Else vArray(Size()) = newItem End If 'Increment the vector size Call SetSize(newSize) End Sub Public Function At(index As Integer) As Variant 'Check the vector's index boundaries If ((index < 0) Or (index => Size())) Then Error 2000, _ "[Vector Class]:(Function: At): Index out of bounds." 'Check if the item is an object If (Isobject(vArray(index))) Then Set At = vArray(index) Else At = vArray(index) End If End Function Public Sub Erase(index As Integer) 'Check if the index is out of bounds If ((index < 0) Or (index => Size())) Then Error 2000, _ "[Vector Class]:(Function: Erase): Index out of bounds." 'Declare variables Dim newArray As Variant Dim x As Integer Dim y As Integer 'Initialize variables y = 0 'Make a new array with the same capacity Redim newArray(Capacity()) 'Loop through each element of the vector For x = 0 To Size() - 1 'Check if the current index is the one to remove If ((x <> index) And (Not(y > Size()))) Then 'Check if the item is an object If (Isobject(vArray(x))) Then Set newArray(y) = vArray(x) Else newArray(y) = vArray(x) End If 'Increment the index counter y = y + 1 End If Next x 'Subtract one from the vector size Call SetSize(Size() - 1) 'Set the new array as the vector vArray = newArray End Sub Public Function BeginIndex() As Integer BeginIndex = 0 End Function Public Function EndIndex() As Integer EndIndex = Size() - 1 End Function Public Sub EraseAll() 'Declare variables Dim x As Integer 'Loop through each element of the array For x = BeginIndex() To EndIndex() 'Delete each element of the arra 'Check if the item is an object If (Isobject(vArray(x))) Then Set vArray(x) = Nothing End If Next 'Reset the size. Call SetSize(0) End Sub End Class
Copy the above code and paste into a script library for the database where you would like to use a vector. Then, you can use the example below to use the vector class.
Use "libVector" Dim vector As New Vector() dim obj As String obj = "hello" 'Load any object into the vector. Call vector.PushBack(obj) Call vector.PushBack(obj) 'Loop through the vector to retrieve objects. For i = vector.BeginIndex() To vector.EndIndex() Print "Item: " & i & " " & vector.At(i) Next
Clearing the Client Version from the Lotus Notes Directory
Jan 20th
With the latest version of Lotus Notes (8.5.1), you can now view the version of your clients by looking in the People -> by Client Version view. One problem with this, however, is that your users will show each client they have logged in as. Over the years, you may accumulate many versions for each user.
During an upgrade, you may wish to see what users are using the previous version of client against the ones who have the new version installed. But first, we’ll need to clean up the directory so we don’t see all this old version history.
To clean up these fields, you need to write an agent that will empty them for each selected person. This will allow you to run the clean up agent on only the users you wish to run it on.
Writing the agent
Open up the pubnames.ntf template file in your Notes Designer. You’ll need to go to Code and double-click on Agents to see the current agents for the template.
We’ll create a new agent and give it a name. Below is the code for the agent. Copy and paste it in the Designer.
Option Public
Option Declare
Sub Initialize()
'Declare.
Dim s As New NotesSession
Dim db As NotesDatabase
Dim dc As NotesDocumentCollection
Dim doc As NotesDocument
'Initialize.
Set db = s.Currentdatabase
Set dc = db.Unprocesseddocuments
Set doc = dc.Getfirstdocument()
While(Not(doc Is Nothing))
If (doc.Form(0) = "Person") Then
Call doc.Removeitem("ClntBld")
Call doc.Removeitem("ClntDate")
Call doc.Removeitem("ClntDgst")
Call doc.Removeitem("ClntMachine")
Call doc.Removeitem("ClntPltfrm")
Call doc.Save(False, False, False)
End If
Set doc = dc.Getnextdocument(doc)
Wend
End Sub
Now, after you refresh your names.nsf file, you can go to the Action menu and find your agent. Running the agent will only process those Person documents that you have selected.
Scheduling A Task In A Web Application On A Windows Server
Jan 19th
When writting web applications, there may be times when you would like to perform some task automatically at the same time or every X number of hours. Web applications only execute their code when a request is made from a client’s browser. Because of this, scheduled tasks within the web application will not work.
There are a few different options:
- Build a separate Windows Service application that can connect to the assemblies your web application uses to execute the scheduled task.
- Build a separate Windows application that communicates with a web service that your web application also uses to perform the scheduled task.
- Use the Windows Scheduler to call the URL of the action to perform the task.
The first option might sound good at first, but creating a separate program that talks to your web application’s library files is a bad idea. Maintenance will become a problem as you change your web application. Since the code is in two places, unit testing will not tell you that the class library you just changed has now broken your scheduled tasks in your windows service.
The second option is slightly better, but you still may have issues with changing your class library.
The third option is the one that I will show you how to configure. We will setup a VBS script that will perform a call to any web address we pass to it. Then, we’ll setup a batch file that calls the VBS script and passes in one or more URLs to execute. And finally, we’ll go through using the Windows Scheduler to create the scheduled task when the batch file should execute.
The primary advantage to this method is that you get to keep all of your code together in your web application. You simply setup an action that performs whatever task you wish to schedule and make sure that action is callable from an anonymous user.
WebRequest.vbs
You may copy the following code and paste into a text file. Then, rename the file to WebRequest.vbs. The script basically takes an argument that is passed into it as the URL. The HTTP object is setup and then the URL is called using a GET request. Then, the returned status is checked to see if the request was successful or not.
url = WScript.Arguments.Item(0)
'WScript.echo url
set WshShell = WScript.CreateObject("WScript.Shell")
set http = CreateObject("Microsoft.XmlHttp")
http.open "GET", url, FALSE
http.send ""
if (http.Status = 200) then
WScript.echo "Request successful."
else
WScript.echo "Error: " & http.Status
end if
set WshShell = nothing
set http = nothing
The Batch File
Calling the WebRequest.vbs file from a batch file is very easy. Simply create a new text file, calling it whatever you’d like, and paste in the following line of text. Rename the file to .BAT when finished.
cscript WebRequest.vbs "http://www.domain.com/controller/action"
Scheduling The Task
From your Windows server, open the Control Panel and then open the Scheduled Tasks item. Next, click on Add Scheduled Task. Choose any application to schedule. This will be changed later on. Configure how often you wish the schedule to run and enter a user name and password to run the schedule as. This users should have access to running the web browser from the server.
After your new scheduled task has been created, open it. In the Run text box, type in the full path to your batch file. Then, in the Start In text box, type in the full path without the batch file name. You can also adjust your scheduled time from within here. Click OK to save the changes and your scheduled task should be ready.
You can test the Windows Scheduler by setting the scheduled time to a minute or two in the future and then waiting for it to execute. If you just wish to test your action, or manually run it, you can simply run the batch file.
MVC and n-layer architecture
Jan 18th
When I write a program, no matter which tool I use, one problem I face is how to design the application. No matter if you use Java, PHP, or Visual Basic; if you design the application badly, you will have problems later when you try to fix bugs or enhance the product to the next version.
The MVC design pattern
MVC stands for Model, View, Controller. This is a very common design pattern used in programming today. When someone uses a MVC application, they make a request to a controller. The controller then talks to the model, which consists of business logic, to perform the action requested. The controller then processes the information from the model and sends it to the appropriate view.
By using the MVC design pattern, you will separate your business logic from your presentation HTML. This makes your application more easily maintainable because a bug can be fixed in the business logic without all that HTML display code getting in the way. Also, when multiple developers are working on a project, the developers who write HTML and CSS can work independently of the core programmers.
N-Layer Architecture
N-Layer, or multi-layer, applications are designed using layers that handle a single responsibility. Many applications use three layers; Presentation, Business Logic, and Data-Access. This is similar to the n-tier architecture, but the primary difference is tiers represent physical hardware, while layers represent software.
In the three-layer approach; the Presentation layer would consist of the user interface, the Business Logic would consist of objects that process Business Rules, and the Data-Access layer would talk directly to the data source. This design usually works well.
Putting them together
One of the things I found a little confusing about MVC at first was what exactly is the model? After much research and testing, I finally found something that makes sense to me.
The diagram above is the general layout that I use when developing a MVC application. Here is a brief description of the various components of the diagram:
- Model – In this diagram, the model would consist of the Services, Repositories, and DatabaseConnection layers along with Domain Objects.
- View - The view is the presentation or design elements of the application; such as HTML or CSS.
- Controller – The controller is the layer that drives the application. It processes requests from the user and sends them to the model and then returns the processed information to the view.
- Services – This layer handles requests from the controller. This is the layer that contains the business rules on how the application should function.
- Repositories - This layer performs CRUD (Create, Read, Update, and Delete) operations on the database.
- DatabaseConnection - This layer is further abstraction between the data source and the repository. It is not required, but I typically use it when I need to work with multiple databases. The DatabaseConnection layer was built to generalize between three different database drivers and use common methods to work with each.
- Data-Access layer- This layer would consist of the Repositories, DatabaseConnection, SQL Data Source and Other Data Sources.
- Other Data Sources – This could include a XML file, other database, or even a web service.
- Domain Objects – This is not necessarily a layer, just a collection of objects used to make it easier to work with the data from the data source. Notice that all of the other layers need to be able to work with these objects. An example of a Domain object might be an Employee class that stores information about an employee and is populated from a data source in the Repository. The Employee object can then be passed from each layer back to the view where it is displayed to the user.
This is just a brief overview of MVC with the n-layer approach.
