Basic architecture of an ASP.NET / Silverlight web application

An important aspect of web development is choosing the right application architecture or framework, one that is flexible, easy to maintain and well understood by its developers.

The (ASP) .NET framework already offers a lot of built-in features which greatly reduce the overhead of common programming activities, and a content management system delivers out-of-the-box components and back-end functionality.

When it comes to custom development, it is of course the developer’s responsibility to apply best coding practices and common design patterns when and wherever possible, preserving high flexibility and maintainability in the application’s overall architecture.

Having this said, I would like to share with you a simple n-tier approach which is currently being implemented in one of our ongoing projects. As the framework fits the project, in this particular case there is no need for a CMS. Without going into too much detail, the use-cases indicate the need of data storage and retrieval (CRUD operations on a few persistent objects), email notification, dynamic generation of images and (multi-lingual) localization support.

An ASP.NET web application hosts 2 Silverlight features which provide a media player and a deep zoom component. Both the web and Silverlight applications need to access common business objects and a persistence mechanism, albeit in different use-cases.

Let’s take a closer look at the framework:

The application’s main class library, storing all business objects and business logic provides a single point of access for client applications: a Façade object. The Façade implements a clear interface for common tasks, while hiding the actual implementation.

As you may or may not know, a Silverlight application does not have direct access to a class library, but rather makes asynchronous calls to one or more (web) services. Great progress is currently being made to ease this challenge for developers (WCF RIA Services and ADO.NET data services) but we have chosen to use a more classic approach by setting up a WCF service. Much like the Façade, this service provides a clear interface for common tasks, although limited to the tasks required by the Silverlight applications. However, the actual implementation still resides in the Façade enhancing the solution’s flexibility (i.e. some tasks may be shared with the web application).

The persistence mechanism is abstracted by a data access object or DAO referenced only by the Façade, greatly reducing any dependencies. Much unlike the active record pattern which provides business objects with CRUD operations, the DAO provides these operations for a subset or all business objects.

Some extra hints & tips:

  • As you may have noticed, there is still a strong dependency between the Façade and the DAO. This dependency can easily be overcome by dependency injection (i.e. by using the Spring.NET framework, and injecting the proper DAO at runtime by means of configuration)
  • Although this example mentions just one façade and DAO, it is recommended to set up multiple instances, each encapsulating separate units of logic or data stores. Facades can easily be set up to interact with each other, and a DAO is commonly used to abstract a single data store.
  • Façade and DAO tend to grow large as they often encapsulate the implementation of large units of work. Implementing them as a singleton greatly reduces performance overhead when consumption increases.
  • By letting persistent business objects inherit from a base entity object the DAO can provide generic methods for CRUD operations, and so can the Façade
  • By unit testing against the Façade, you are able to test most (if not all) of the business logic

Some code fragments:

Facade & DAO interfaces:


public interface IBusinessFacade

{

IList<Company> SearchCompanies(CompanySearchCriteria criteria);

Company GetCompanyById(string companyId);

IList<Employee> SearchEmployees(EmployeeSearchCriteria criteria);

IList<Registration> SearchRegistrations(RegistrationSearchCriteria criteria);

bool SaveRegistration(Registration r);

bool GenerateDeepZoomImage(DeepZoomImage img);

bool DeleteRegistration(Registration r);

}

public interface IBusinessDao

{

T GetEntityById<T>(String id) where T : Entity;

IList<Company> SearchCompanies(CompanySearchCriteria criteria);

IList<Employee> SearchEmployees(EmployeeSearchCriteria criteria);

IList<Registration> SearchRegistrations(RegistrationSearchCriteria criteria);

void SaveRegistration(Registration r);

void DeleteRegistration(Registration r);

}

A persistent business object:


[Serializable]

[DataContract]

public class Employee : Entity

{

[DataMember]

public string FirstName { get; set; }

[DataMember]

public string LastName { get; set; }

[DataMember]

public string Email { get; set; }

[DataMember]

public string Phone { get; set; }

[DataMember]

public Company Company { get; set; }

}

The WCF Service interface:


[ServiceContract]

public interface ISLService

{

[OperationContract]

IList<Company> SearchCompanies(CompanySearchCriteria criteria);

[OperationContract]

IList<Employee> SearchEmployees(EmployeeSearchCriteria criteria);

[OperationContract]

IList<Registration> SearchRegistrations(RegistrationSearchCriteria criteria);

}

Silverlight application code calling the service:


protected void SearchCompanies()

{

try

{

SLServiceClient client = new SLServiceClient();

client.SearchCompaniesCompleted += new EventHandler<SearchCompaniesCompletedEventArgs>(SearchCompaniesCompleted);

client.SearchCompaniesAsync(new CompanySearchCriteria() { NameLike = "These Days" });

}

catch

{

// ..

}

}

A sample unit test written against the DAO:


[TestMethod]

public void GetEntitiesById()

{

var mockReg = MockFactory.Instance.CreateMockObject<Registration>();

BusinessDao.Instance.SaveRegistration(mockReg);

var regById = BusinessDao.Instance.GetEntityById<Registration>(mockReg.Id);

Assert.IsNotNull(regById, "Unable to retrieve registration by id");

var employeeById = BusinessDao.Instance.GetEntityById<Employee>(mockReg.Employee.Id);

Assert.IsNotNull(employeeById, "Unable to retrieve employee by id");

var compById = BusinessDao.Instance.GetEntityById<Company>(mockReg.Employee.Company.Id);

Assert.IsNotNull(compById, "Unable to retrieve company by id");

BusinessDao.Instance.DeleteRegistration(mockReg);

}

one comment

  1. [...] a previous post I’ve discussed a simple n-tier approach using a Facade and Dao pattern. The Facade provides a [...]

Leave a reply

Switch to our mobile site