Click here to Skip to main content
Click here to Skip to main content

Open Source Extensible Enterprise n-tier Application Framework with Multilayered Architecture

, 3 Dec 2010 MIT
Rate this:
Please Sign up or sign in to vote.
Xenta architecture overview

Introduction

Xenta is an extensible n-tier application with multilayered architecture. Xenta is an open source project and available for free. It is developed in .NET/C# programming language and initially targeted to web based solutions. It's licensed under the modified MIT license.

The purpose of this article is to introduce you to the architecture of Xenta.

To work with the source code, you will need Microsoft Visual Studio 2010.

Architecture Overview

Layers

Xenta has 4 layers:

  • Data Access Layer (DAL)
  • Business Logic Layer (BLL)
  • Service Layer
  • Presentation Layer

Each layer contains its own entities. The entities are mapping while going through layers (e.g. DAL entities are mapping into BLL entities).

Data Access Layer

DAL consists of data abstraction and implementations of abstraction for each concrete data source. Data abstraction describes data entities and data provider interfaces. Base class for data entities is DataEntityBase. For data provider interfaces, there is base interface IDataProvider. Providers instantiation is based on configuration file. By default, the data source is Microsoft SQL Server (2005 or above), but believe me, it is easy enough to change the data source.

IDataProvider - Base Class for Data Provider Interfaces

public interface IDataProvider
{
    #region Methods
    void Initialize(string name, NameValueCollection properties);
    #endregion
}

IUserDataProvider - Interface of User Data Provider

public interface IUserDataProvider : IDataProvider
{
    #region Methods
    #region C
    bool InsertUser(Guid guid, string username, string firstName, 
	string lastName, Gender gender, DateTime? birthDate, string email, 
	string comment, string passwordHash, string passwordSalt, 
	int languageID, int currencyID, int timeZoneID, int countryID, 
	bool isActive, DateTime createdOn, DateTime updatedOn, out int userID);
    #endregion
    
    #region R
    UserData GetUser(int userID);
    UserData GetUserByEmail(string email);
    UserData GetUserByUsername(string username);
    UserData GetUserByGuid(Guid guid);
    UserDataCollection GetAllUsers(string searchTerm, int? roleID, 
	int? countryID, int? languageID, int? currencyID, int? timeZoneID, 
	DateTime? createdOnStart, DateTime? createdOnEnd, bool showHidden, 
	int startIndex, int count, out int totalCount);
    #endregion
    
    #region U
    bool UpdateUser(int userID, Guid guid, string username, string firstName, 
	string lastName, Gender gender, DateTime? birthDate, string email, 
	string comment, string passwordHash, string passwordSalt, int languageID, 
	int currencyID, int timeZoneID, int countryID, bool isActive, 
	DateTime createdOn, DateTime updatedOn);
    #endregion
    
    #region D
    bool DeleteUser(int userID);
    #endregion
    #endregion
}

DAL Configuration Sample

<xentaDAL>
  <providers>
    ...
    <add name="UserDataProvider" type="SiberTek.Xenta.Data.Providers.UserDataProvider, 
	SiberTek.Xenta.Data.MsSqlServer.Core" connection="MsSqlServerConnection" />
    ...
  </providers>
</xentaDAL>

Business Logic Layer

BLL describes business rules and management logic. It contains business entities, managers, tasks, etc..

Entities inherit from BusinessEntityBase.

Each manager implements IManager interface. Usually manager is singleton. Manager can contain events, which can be handled by other managers or tasks or something else. For example, there defined EntityEventArgs class and EntityEventHandler delegate, for events related to entity operations such as Create/Update/Delete.

UserManager - User Manager Class Sample

public class UserManager : IManager
{
    #region Constants
    private const string GuestUsername = "guest";
    #endregion
    
    #region Fields
    private static UserManager _instance = null;
    private bool _initialized;
    private IUserDataProvider _userDataProvider;
    private IUserAttributeDataProvider _attributeDataProvider;
    private IUserSessionDataProvider _sessionDataProvider;
    #endregion
    
    #region Constructors
    private UserManager()
    {
        _initialized = false;
        _userDataProvider = null;
        _attributeDataProvider = null;
        _sessionDataProvider = null;
    }
    #endregion
    
    #region Events
    public event EntityEventHandler UserCreated;
    public event EntityEventHandler UserUpdated;
    public event EntityEventHandler UserDeleted;
    #endregion
    
    #region Properties
    public static UserManager Instance
    {
        get
        {
            if(_instance == null)
            {
                _instance = new UserManager();
            }
            return _instance;
        }
    }
...

Tasks implement ITask interface. Each task has Period property, this property contains a TimeSpan value, which defines task execution period.

Tasks are scheduled by the TaskScheduler class. This class starts a thread which reads execution schedule form a text file each one second, runs required tasks and reschedules next execution time for each task in list.

ITask - Task Interface

public interface ITask
{
    #region Properties
    TimeSpan Period
    {
        get;
    }
    #endregion
    
    #region Methods
    void Initialize(NameValueCollection properties);
    void TaskProc();
    #endregion
}

Tasks Configuration Sample

<tasks>
    ...
    <add name="MessageDispatcher" type="SiberTek.Xenta.Tasks.MessageDispatcher, 
	SiberTek.Xenta.Core" />
    ...
</tasks>

Service Layer

This "thin" layer is based on WCF technology. It allows to deploy Xenta tiers on different physical servers, in one server or even in one application. Services are hosted in Xenta.Services.Host application.

ICoreService - Core Service Contract

[ServiceContract]
public interface ICoreService
{
    #region Methods
    #region Users
    [OperationContract]
    [FaultContract(typeof(XentaFault))]
    User CreateUser(string username, string firstName, string lastName, 
    Gender gender, DateTime? birthDate, string email, string comment, 
    string password, int languageID, int currencyID, int timeZoneID, int countryID);
    
    [OperationContract]
    User GetUser(int userID);
    
    [OperationContract]
    User GetGuestUser();
    
    [OperationContract]
    User GetUserByUsername(string username);
    
    [OperationContract]
    User GetUserByEmail(string email);
    
    [OperationContract]
    User GetUserByGuid(Guid guid);
    
    [OperationContract]
    UserCollection GetAllUsers(string searchTerm, int? roleID, int? 
    countryID, int? languageID, int? currencyID, int? timeZoneID, DateTime? 
    createdOnStart, DateTime? createdOnEnd, bool showHidden, int startIndex, int count);
    
    [OperationContract]
    bool IsUsernameBusy(string username);
    
    [OperationContract]
    bool IsEmailBusy(string email);
    
    [OperationContract]
    bool IsUserPasswordValid(int userID, string password);
    
    [OperationContract]
    [FaultContract(typeof(XentaFault))]
    bool UpdateUserPassword(int userID, string password);
    
    [OperationContract]
    [FaultContract(typeof(XentaFault))]
    bool ActivateUser(int userID);
    
    [OperationContract]
    bool IsUserActive(int userID);
    
    ...

Presentation Layer

The layer implements MVP pattern. UI is based on ASP.NET technology and split into several web applications such as: Xenta.Web.Admin, Xenta.Web.Home, Xenta.Web.FileStorage, Xenta.Web.Forum. You decide which application to deploy.

In context of MVP pattern there:

  • Model - Business logic which is accessible through services
  • View - Web applications
  • Presenter - Presenters and helper classes which access model through services

UserPresenter - User Presenter Class Sample

public class UserPresenter : PresenterBase
{
    #region Methods
    public void Update()
    {
        ViewDataContainer item = View.DataContainer["User"] as ViewDataContainer;
        
        int userID = (int)item["UserID"];
        string username = (string)item["Username"];
        string email = (string)item["Email"];
        string firstName = (string)item["FirstName"];
        string lastName = (string)item["LastName"];
        bool isActive = (bool)item["IsActive"];
        Gender gender = (Gender)item["Gender"];
        DateTime? birthDate = (DateTime?)item["BirthDate"];
        string comment = (string)item["Comment"];
        int languageID = (int)item["LanguageIDv];
        int countryID = (int)item["CountryID"];
        int timeZoneID = (int)item["TimeZoneID"];
        int currencyID = (int)item["CurrencyID"];
        
        if(birthDate.HasValue)
        {
            birthDate = UserContext.Current.DateTimeConvertToUtc(birthDate.Value);
        }
        
        using(CoreServiceClient svc = new CoreServiceClient())
        {
            svc.UpdateUser(userID, username, firstName, lastName, 
		gender, birthDate, email, comment, languageID, currencyID, 
		timeZoneID, countryID, isActive);
        }
    }
    
    ...

UserForm - ASP.NET Control

public partial class UserForm : ViewBase<UserPresenter>, IForm, ICommandHandler
{
    #region Properties
    [Browsable(false)]
    public int? UserID
    {
        get
        {
            return StringHelper.Parse<Int32?>(txtUserID.Text);
        }
        set
        {
            txtUserID.Text = value.HasValue ? value.ToString() : String.Empty;
        }
    }
    
    public string ValidationGroup
    {
        set
        {
            vldUsername.ValidationGroup = value;
            vldEmail.ValidationGroup = value;
            vldEmail2.ValidationGroup = value;
            vldPassword.ValidationGroup = value;
            vldPassword2.ValidationGroup = value;
            vldFileQuota.ValidationGroup = value;
        }
    }
...

Conclusion

Xenta has a clear architecture and implements best patterns & practices. It's easy to extend. It gives you freedom in packaging and deploying. You can just throw out all unnecessary things and build your own package. We welcome you in our community, where you can receive useful answers to any of your questions.

References

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Daniil Novikov

Russian Federation Russian Federation
No Biography provided

Comments and Discussions

 
QuestionWhy doesn't 'Activator.CreateInstance' throw an exception? PinmemberkkkarlB5-Dec-10 6:57 
AnswerRe: Why doesn't 'Activator.CreateInstance' throw an exception? PinmemberDaniil Novikov5-Dec-10 7:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.141015.1 | Last Updated 3 Dec 2010
Article Copyright 2010 by Daniil Novikov
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid