Click here to Skip to main content
13,447,595 members (53,397 online)
Click here to Skip to main content
Add your own
alternative version


24 bookmarked
Posted 5 Oct 2008

Web-Application Framework - Catharsis - part IX - Business layer

, 5 Oct 2008
Rate this:
Please Sign up or sign in to vote.
Catharsis web-app framework - Business layer


This article is the next step in Catharsis documented tutorial. Catharsis is a web-application framework gathering best-practices, using ASP.NET MVC (preview 5), NHibernate 2.0. All needed source code you can find here.

No.ChapterNo.ChapterNo. Chapter
I  New solution VI CatharsisArchitectureXI Controller layer
II Home page observationVII Entity layer XII (UI Web layer - undone)
III Access rights,
VIII Data layer XIII Tracking, PAX design pattern
IV Localization, translationsIX Business layerXIV Catharsis Dependency Injection (DI, IoC)
V Enter into Catharsis, new EntityX Model layer XV (Code Lists - undone)

Business layer  

Catharsis framework has real multi-tier architecture, and is strongly OOP; everything is subordinated to Entity’s instance handling.  

Entities are persisted in storages like database, xml etc. All CRUD (create, read, update, delete) activities are done using data-access-objects (Dao) on data layer. Daos provide in fact only the CRUD operations. 

Business layer brings order & low and serves as the only gateway between any (upper) layer which needs to access entities. Not only 'upper’ layer - there are for example Providers nested in ‘lower’ level like .Common project, which also access entities using IFacades.


Applying business rules is one of the main tasks for the business tier. Rules can check ranges for numbers, length for strings, existence of inner objects etc. For example customer may wish to use application like evidence for an Entity and wants to store only country abbr. matching the code list, First and Second name longer then 2 letters, address containing number and so on. 

Another example of business rule could be request: 'delete' Entity. There must be check for users-access-rights and assurance that there is no reference remaining. And maybe other ‘customer’ rules which you can imagine or even remember.  

These rules contain business logic and they must be handled on a business tier. They are placed on one place and therefore can be simply maintained and reused. The same checks are applied for new Entity coming from UI as well for those coming through batch importing application.

Database features  

There are many features on database side like indexes, constraints, triggers ... ready to use. Some of them (indexes, keys, statistics) are vital components of your application and its future performance. Other can help you mainly during development (constraints).

Constraints are usually used, that’s really not contra productive, at least in development phase. They can quickly remind you where the business rules are missing (deleting). But it still means that every such a rule should be applied on a Façade too. SQL can help you but never should be the only guard. 

Forget stored procedures

Well, there are also features on db side, which should be never used when we are talking about multi-tier architecture (and OOP). That goes mainly to triggers and – stored procedures! Yes, I am serious! 

What can in fact do triggers or stored procedures do for you? Nothing more than to provide business rules checks. There are many applications using stored procedures as the only way to access stored data. Direct access to tables is disabled, application interfaces are the 'returned values' from SPs.   

Those application are not multi-tier ones. So called 'data' layer in such scenarios does not access data, but something like 'business' layer which was morphed into SPs. And that's really upside-down. Those application in fact act as the UI to database. (Don't you agree? good place for conflict, isn’t it?) 

One of unintended advantages of such approach is improved debugging. (Would you prefer to debug and trace: C# code or T-SQL?). Intended benefit is storage type independency. After some time you realize that users, roles, localized phrases etc. can be reused. Well, move them to another database or even xml, change CRUD methods on your Daos and that’s all. Rules are still applied even if the storage was changed.  


There is solely standing, independent web-application with one database; users access data exclusively via web-application UI.  

Opposite to that dream is reality with ‘always’ needed export and import ability, usually based on MS Excel at least. If your application stands in the chain you simply must provide interfaces for data exchange, incoming and outgoing.

To satisfy customer’s requirements you will probably have to develop batch applications for exporting and importing, maybe some WebServices etc.

The gateway for them all must be already created, tested and working - Business layer. Anything or anyone who wants to access ‘your’ Entities must pass through your Customs office (called façade). If all rights are checked and requirements met, entity could be processed. The reuse is obvious. And what’s more, all hours spent on precious tuning of all these rules can give you the feeling of safety. 


There are objects from day to day usage of your application which could be cached (e.g. localized resources). Performance gain could be in these cases beyond price. Good candidates for caching are in time stable, not so often changing values. In spite of that suggestion they still must be up-to-date.  

There are nice SQL and File cache dependency handlers in .NET. In a nutshell, if you are using cache object, there is mechanism which will let your data expire, when any change in datasource appears. 

SqlCacheDependency sqlDependency = new SqlCacheDependency("Northwind", "Products");
HttpContext.Current.Cache.Insert("ProductsList", products, 
                     sqlDependency, DateTime.Now.AddDays(1), Cache.NoSlidingExpiration);

Application cache - static dictionary object

Different type of Application cache are static objects. They are not thrown away when the memory starts to be full (in opposite to cache object) and they can store types not only objects.   

protected static IDictionary<string, Translator> Translations { get; set; } 

On the other hand you need another way to handle refreshing. And that’s where Business layer can help you again.   

Cache (e.g. static <code>IDictionary object) should be placed right inside the façade. It gives you assurance, that every layer accessing Entity will be provided with fresh data. Controllers and models are created per request and then thrown away. There is no place for page or control caching either!   

Secondly, cache (static object) is lazily loaded - item by item. When first request for entity comes, Entity is loaded from storage and also cached. In contrast - cleared are all at once (after any change is reported). When the announcement for change arise, the whole object is cleared.   

Expiration monitor  

You can in some cases decide to cache data on a separate level. Good example are Providers (lists of roles, users, localization phrases...). In such situations you need the way how to let your cache know that change has appeared. That's the goal for OnChange event. (the same mechanism is or can be used for the expiration of cache inside the façade).   

You can sign on 'ClearDictionary' and clear your static cache.  

  //... somewhere in the constructor
  ITranslatorFacade.OnChange += ClearDictionary;

public static void ClearDictionary(object sender, EventArgs args)

Current Catharsis implementation expects that only way to change Entity is the web-application UI. There are watches on add(), update() and delete() facade's methods which will rise OnChange event. If you will extend your application with batches like data importers, you have to also extend the mechanism for rising OnChange event.  

AOP filters

ASP.NET MVC profits from the fact that there is no ballast from the past, from previous versions. MVC is growing on a best-practices and therefore the AOP (aspect-oriented-programming) is an essential pillar. In another articles on you can read about many AOP frameworks, solutions, approaches ...
But they very often act as the compensation of the original bad design (e.g. AOP for better viewstate handling in a web-form - is it AOP or bug fix?) 

Situation in ASP.NET MVC is totally different. There are filters based on attributes which could be ran before or after controller's action. Nice example is [Transaction] attribute. That AOP filter will open transaction before action is executing and commit (or rollback) it when action is executed. You do not have to care for that aspect, just decorate action with attribute [Transaction]

Other example is [Navigation]. Catharsis uses for work-flow handling a very very rigorous solution. There is switch-case clause dependent on a CurrentRole which fills the list of navigation links. These are stored in the model and rendered as a list of links on a master page.

But the power is in the approach. [Navigation] object is the AOP filter, which is ALWAYS evaluated and is the only responsible for filling the list of navigation links. You (or someone else) can create much more sophisticated handler e.g. based on xml scenarios... and replace current [Navigation] implementation. The change will take place only inside [Navigation] filter attribute, but the improvement will inflict the whole application.  

That's the Aspect-Oriented-Programming - AOP.  


USE Guidance! They will save your time and improve effectiveness. On a Business project right-click on a folder and the context menu item will appear. Next steps are same as in the previous chapters... We'll continue with Person entity example (wizard-details are described in previous chapters):  



Entity's facade methods will be accessible via IEntityFacade interface. This is almost empty interface - the right place for your future extension.   

[ConcreteType("Firm.Product.Business.People.PersonFacade, Firm.Product.Business")]
public interface IPersonFacade : IFacade<Person> { }

IEntityFacade gains a lot from its base declaration: 

public interface IFacade<T>
    where T : IPersistentObject
        T GetById(int id);
        T GetById(int id, DateTime? historicalDateTime);
        IList<T> GetBySearch(ISearchObject<T> searchObject);

        T Add(T item);
        T Update(T item);
        void Delete(T item);

        IDictionary<string, object> ErrorData { get; set; }        

        EventHandler OnChange { get; set; }

As we've discussed above, there is also the OnChange event.


FacadeFactory, when asked for IPersonFacade interface, will provide new instance of just created PersonFacade. Guidance has created the skeleton in .business project for you: 

public class PersonFacade : BaseFacade<Person>, IPersonFacade
    public PersonFacade() : base(DaoFactory.CreateDao<IPersonDao>()) { }

    public override IList<Person> GetBySearch(ISearchObject<Person> searchObject)
        return Dao.GetBySearch(searchObject);

    protected new IPersonDao Dao { get { return base.Dao as IPersonDao; } }

BaseFacade has already implemented a lot of common behaviour. It is simple but too large to list it here, please see the source code. The attention should go to OnChange handling. I believe that the code is self documenting and a small extract will uncover the secret. 

// extract BaseFacade

   // Example of a 'Set' method
   public virtual T Update(T item)
       var t = Dao.Update(item);
       Change();                      // your listener is called
       return t;
   // OnChange works even for static objects like Providers!!   
   private static EventHandler _onChange { get; set; }
   public EventHandler OnChange    // Append your ClearDictionary here
        get { return _onChange; }  
        set { _onChange = value; } 

   protected virtual void Change()
       if (OnChange != null)
            OnChange(this, new EventArgs());
   ... // BaseFacade continues 

Error data

Well, we are discussing the Business layer as the place for business rules checking. This key goal can work only if there is a way how handle error states - situations when the business rules are not met.

ErrorData collection is the solution. And center-point of every facade. As you can see in the IFacade declaration, every facade has ErrorData collection with one simple purpose:

Fill ErrorData with any incorrectness you'll find during business rules checking.

Every business rule check must succeed or must be reported. If evaluation fails, put record into ErrorData. Business rules accomplished on facade must result in:

  1. Reporting all bugs via ErrorData collection and break execution
  2. Execute required changes only in case there are no errors.

Instead of throwing an exception the facade caller gets the list of errors. Empty collection means that everything went right. If there is any error message, required changes should not be persisted.  

The above code extract could be extended: 

public virtual T Update(T item)
    if ( CheckBusinessRules(item) )
        var t = Dao.Update(item);
        Change();                      // your listener si called
        return t;
    // ErrorData are filled with messages,
    // which should be used on calling layer

    return t;  // unchanged 't' is returned

public bool CheckBusinessRules(T item)
    bool success = true;
    if ( item.Name.Length < 2 )
        ErrorData["NameShort"] = "NameIsShort";
        success = false;

    // TODO check rules and fill ErrorData

    return success;

Catharsis ErrorData handling

Model (in next chapters we'll discuss it more) also contains its own ErrorData collection. Catharsis always renders the content of the Model.ErrorData. It means that if there is any message in the ErrorData - its always displayed on UI. This error collection in Model is used by controller in cases of type-incorrectness (e.g. date was 2008/31/13...).  

There is more for you - whenever controller gets the instance of IFacade from FacadeFactory, the IFacade.ErrorData is set to reference to Model.ErrorData!!! 

The gain is obvious, and again beyond price! When filling ErrorData collection on facade,  the Facade.ErrorData are in fact the Model.ErrorData. Its content is than always directly displayed to users, and keep them informed about troubles.

Separation of concern wins again. On facade you are filling collection ErrorData. On UI you are displaying ErrorData (if any). Catharsis infrastructure ensures that you are working with one ErrorData instance and don't have to care about Error-data-exchange among layers. 

That behavior is working for Catharsis web-application framework! If you will use facade in another application (batch importer) you have to take care! You must evaluate Facade.ErrorData and made some steps e.g. logging ... if error appears. 

Catharsis built-in Facades  

There are roles and localizations Providers built-in Catharsis and ready for use. They are nested in .Common project (which is 'lower layer' then business). But IAppRoleFacade, IAppUserFacade and ITranslatorFacade interfaces are known even on Common layer (where are stored) and therefore even Providers can access them.   

That's another good example of very-well working separation of concern! 

Roles & users

Users can be added to application in run-time (in production). Because users's access rights are evaluated almost on every method call, the list of roles and users is cached. Whenever there's a change reported (e.g. new user added) the cache is cleared. (please, observe)


Localized phrases are the pillar of Catharsis user-friendly UI. They are used almost on every page and when adjusted they are fixed. This is another good example of application cache usage, which have crucial impact on application performance.  


There'll be separate chapter for CodeLists. Those are value lists which are very rigid and localized to provide better comfort for user. E.g. Country, Currency, scale (bad, neutral, good), type (inner, outer) and so on...  

Batch export, import  

Final note goes to application extensions - exporters, importers, queue listeners and responders. Always build your design on Business layer. Once you are sure that facade is adjusted, you can expose and reuse it in any other app like batch or service.
Such application should take care about messages stored in facade's ErrorData collection.

Enjoy Catharsis!

Source code


  • 5th October, 2008: Initial post


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Radim Köhler
Web Developer
Czech Republic Czech Republic
Web developer since 2002, .NET since 2005

Competition is as important as Cooperation.


You may also be interested in...

Comments and Discussions

GeneralSo is the business layer a partial class, or a whole facade class Pin
philmee953-Nov-08 9:41
memberphilmee953-Nov-08 9:41 
GeneralRe: So is the business layer a partial class, or a whole facade class Pin
Radim Köhler3-Nov-08 19:19
memberRadim Köhler3-Nov-08 19:19 
GeneralRe: So is the business layer a partial class, or a whole facade class Pin
philmee954-Nov-08 6:37
memberphilmee954-Nov-08 6:37 
GeneralCode Lists Pin
jalchr19-Oct-08 23:33
memberjalchr19-Oct-08 23:33 
GeneralRe: Code Lists Pin
Radim Köhler19-Oct-08 23:38
memberRadim Köhler19-Oct-08 23:38 

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

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

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03-2016 | 2.8.180318.3 | Last Updated 5 Oct 2008
Article Copyright 2008 by Radim Köhler
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid