• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 410
  • Last Modified:

Passing results of operations in a Data Access Layer back to calling layers? Best practice?

i have an application where I am trying to adhere to  n-Tier architecture principles. I want to keep a fairly strict pathway to my data access layer for both input and output. Input is passed through typed parameters passed from the calling routines in my business logic layer, but I have a variety of possible return values that need to get passed back to the caller. Some routines only return a boolean indicating the success of an operation, whereas others return data objects,etc.

What I would like to do is create a generic return object that all my DAL routines could make use of, and give calling routines a consistent interface for the return values. I would like this object to contain the results of each DAL operation. If an exception occurs, I want to be able to pass the details of the exception back to the caller. If a data row or a data table needs to be passed back to the caller, I would like to be able to do it through this generic object.

My tentative idea is to create a class that would contain a boolean property indicating the success of the DAL operation, a custom exception object, and a generic return object for any data that needs to be passed back to the caller. I could then load this return object with the appropriate information and return it to the caller.

My question is whether or not this is considered a 'bast practice' in the n-Tier realm? In the past, I have just handled the results of various routines locally, but with a layered architecture that approach seems to yield hard-to-maintain spaghetti code.
0
kkamm
Asked:
kkamm
  • 8
  • 7
1 Solution
 
abelCommented:
The best practice, imo, is to use a third party ORM, like NHibernate (.NET) /Hibernate (java). You then use any of the many automation tools to create your classes/objects that map to your database. I prefer a layout of pure entities to start with and a config file that takes care of the underlying relations. The entities of course already contain pointers/lists for the relations and the ORM can automatically map these.

Then, step two, the actual DAL, is to create an abstraction layer on top of these entities. The layer is used for manipulation of the database and if your language allows it, will best benefit of generic programming (C#/Java/C++ come to mind). This step is a three-substeps part.

First, use the same automation tool for creating a per-table interface. These interfaces serve as a contract for your DAO objects. The interfaces are generic, based on the entities. Since you make a per-table DAO, it is possible to use a general IReadOnly and IUpdateable interface, which makes distinguishing between readonly and updateable tables easier. Here you can also define interfaces that match Enum types.

Then, you create one of more abstract classes that use will use generics and contain actions such as retrieve / add / remove / update / commit etc. These abstract classes (usually one) is used as the parent for any class from the next step, and is very generic again.

Finally (third sub-step) you create the concrete classes based on the interfaces and the abstract class of the previous steps. This is your actual implementation phase, but for many tables, you will have nothing to do here anymore, making life easy. Some tables that need special treatment (should be specified in the dao interface) will have a concrete implementation here.
Then, step three. This is the session manager for your DAL, and the connection manager. I will not go into it further (read the link below). For larger enterprises, this is an important asset, esp. when dealing with multiple databases, and you should not take this lightly. The session manager should use a thread safe singleton pattern.

The fourth step is using the factory pattern for retrieving the DAO objects. This will be your starting point from a client's perspective, because from here you get any DAO. This effectively relieves the burden of creating DAOs from the client (this should be forbidden by using the correct accessors). The client only deals with the interfaces.

Finally, step four, is putting it all together. This step also includes thinking about logging (the aforementioned Hibernate can be configured to do any kind of logging) and to think about which scenarios you allow and which not. Your abtraction layer in conjunction with the ORM should be enough to raise the appropriate errors, like user with same id or whatever.

If everything is setup nicely, you can consider a first-level and second-level caching strategy. Using an ORM is blazingly fast, but often only if you use it correctly. In the case of Hibernate, it is also a good idea to define how large the chunks are that should be retrieved from the database when you expect to loop over large sets of data.

Large parts of this story are from my own experience. However, if you read through this article: http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx you will find that these ideas are considered quite common. There are some things I do different (you can compare the stories), but the main idea is the same. You can also read that article if you do not plan to use Hibernate or NHibernate. The general idea is what counts.

-- Abel --
0
 
kkammAuthor Commented:
Abel,

Thanks for the info.

I am currently using .NET typed datasets and I am doing various per-table CRUD operations through that.  My DAL works with the dataset and the BLL works with the DAL to create and manipulate various custom business objects to present to a WPF front-end. My application will probably not be web-based.

Does NHibernate have a significant learning curve? I am reticent to spend more development time on learning yet another tool.
0
 
abelCommented:
> Does NHibernate have a significant learning curve?

It depends on where you are coming from. For somebody with ample years of experience, of which quite some in enterprise development, it shouldn't be too hard to grasp the sometimes rather abstract concepts. But these are concepts that have stood the test of time and are now used in many large and very large organizations.

The problem is usually: getting started and at the same time building up an enterprise ready approach to your data. Which means, no time to learn it the hard way. Like with many projects, your best bet is to hire a professional who can set it up and then explain it to you and who can be available when trouble strikes later (I am available! lol).

Once a system like described above is setup up, it is amazingly fast and versatile, quite easy to maintain but above all, easy to use.

I don't know how small or wide your project is going to be. But considering the subject and the area where you posted the question, I assume that it is a bit more than the most basic example. If you do want a professional review of your situation, I can move and act fast and have a possible large team of experts to help.

But I'll also be around the EE area most of the time and will be happy to consult you with the steps to take on hibernate through the Q/A system of EE.
0
Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
kkammAuthor Commented:
The application I am working on is not enterprise-grade, but it will be used in a medical data context, so I am rather concerned with correctness of data,etc. The database schema is relatively straight-forward. Most operations are single table, with an occasional parent-child relation.

Right now, a key concern would be the data representations available through the BLL layer, as my WPF UI is driven by collections from the BLL that represent queried data from the DAL. Does NHibernate allow you to specify custom types for holding memory-resident data? Specifically, my primary collection type is derived from an Observable Collection which I then use to bind to my UI controls. If I could use NHibernate to generate an infrastructure that would provide me with a UI-to-database pipeline, then it may be worth the learning curve.
0
 
abelCommented:
Yes, you can use any custom types you like. The end result is usually, that you have some place where you define what columns / fields go into what properties (getters/setters). This process can be automated. If you want them inside currently existing collections, or make use of existing base types or interfaces, that shouldn't be too much of a problem.

It sure is worth the learning curve! Once you get the hang of it, your next DB project will be setup in no-time, with much more data integrity security, higher performance and simply more possibilities because of a less complex UI layer.
0
 
kkammAuthor Commented:
You mentioned the possibility of using outside consultancy as a means to expedite this process. Are there firms that specialize in NHibernate development? What is a typical rate for a contract job of this type?

The reason I ask is that I am weighing the cost of development in-house vs. a contract job for the DAL. This project is currently an in-house project, but is being designed with an intent to market it eventually.  The work I am currently doing seems to be "re-inventing the wheel" at the DAL level, and I would rather be attending to the functionality of the program according to our user requirements.

If NHibernate is an industry standard for data access, and we want to be able to market our system, then it may well be worth the benefits of hiring someone to assist us with this.


0
 
abelCommented:
Well, you're asking the wrong guy, because with such a request I can only start promoting myself ;-).

All larger projects I worked with in the last 4-5 years involved Hibernate (java projects) and NHibernate. The projects I did this for ranged from 25K to 55M, but I was mainly active in the mid-section of the market (around 200K). Of course, NHibernate was only a part of those budgets (Euro).

Anyway, I can offer myself, I can offer you other companies and I can offer you an offshore team with (N)Hibernate knowledge. Pricing and other acquisitional like talk I'd rather do over the telephone or by mail. You can find both in my profile (EE guidelines allow it in profile, not in discussion threads).

In all humbleness (ahum), I can help you set it up quickly and stable/reliable, with the necessary hand-over documentation and post-delivery support, and I can travel if necessary.

-- Abel --
0
 
kkammAuthor Commented:
Abel,

I will have to do some more research on NHibernate to see if it fits our purposes, but I may need to get more information from you along the way.

With regards to the original question-what have you encountered as far as exception handling and return values/objects coming from a DAL layer? Is it common practice to wrap this sort of content in a custom object and pass it back to a calling layer?
0
 
abelCommented:
I'll be around for that ;)

Yes, I would pass it back to the calling layer. It is commonly considered good practice not to hide exceptions, unless you can handle them. Some exceptions might or might not be handled by the DAL, but the rest should just be propagated forward.

My own preference, however, dictates that the DAL should expose its own layer of custom exceptions. The InnerException should be set to the NHibernate Exception (or other style of DAL layer related exception). The reason for this wrapping is simple: you do not want the calling layer to need to have knowledge or information about NHibernate. You want it to deal with the abstraction layer.
0
 
kkammAuthor Commented:
If I throw a custom exception in the DAL layer, is it considered best practice to catch it in the calling layer or should I keep all layer-specific error handling within the layer and make the exception details available via a custom object that holds the details of the exception?

0
 
abelCommented:
Ah, now your touching at a much debated subject. With my method described above (with custom exceptions, I do mean exceptions), I would choose the exceptions way, which is what they're there for.

But, slightly depending on your demands, it might suit you better to do an after-the-fact check by allowing an error object around, so that you do not need try/catch. But this is usually a trap and will bring you in the long run into the same trouble as it brought Microsoft old-style coding with GetLastError. I would advice against it.

You can also blend the two methods. Use exceptions AND use an error handler event. You can make it optional that the exceptions are thrown. But this is closely the same as creating a global exception handler which you would use for logging the errors anyway (log4net works with NHibernate).
0
 
kkammAuthor Commented:
Some potential and knowable errors, like a network interface being unavailable, I can check for before even trying to instantiate a connection object. This can be taken care of explicitly in the DAL, as that is the only layer given access to the server.

My Try-Catch block for a DAL CRUD operation should only be entered after certain knowable error conditions are found to be absent. The unknowable stuff, however, I could catch as a general exception and rethrow as a custom exception for the BLL to catch. The BLL could, in turn, throw an exception that could be caught at the UI level.

The problem with a general handler would seem to be that I could not do localized cleanup if an operation failed. Right now, I have an INSERT methodology where I can't commit any changes to my UI or BLL collections until it is confirmed that a row has been created in the database and an identity value has been returned. If I encounter an error in creating that row, then I simply abort the corresponding CRUD operations for my BLL and UI, but I need to be able to register the error at each layer.

Does it make sense to define custom exceptions for each layer and catch them at the next layer up? It seems there would still be logical separation of layers that need not communicate with one another.
0
 
kkammAuthor Commented:
Abel,

I was experimenting with custom exceptions and I wanted to pass this by you before finishing up this thread.

I attached a very basic code sample of something I am trying. The gist of it is that if an error occurs locally then a new custom exception iis thrown so that the orginal callier can handle it specifically. If an exception is simply "passing through" from a deeper call then the specific exception is rethrown. It seems to work like channels for each layer, ending at the base layer, where no further exceptions are thrown.







Private Sub BaseCall()
	Try
	CallOne
	Catch CallOneEx As CallOneException
        Catch CallTwoEx As CallTwoException
	Catch Ex As Exception
        Finally
        End Try
    End Sub
 
Private Sub CallOne()
	Try
	CallTwo
        Catch CallTwoEx As CallTwoException
	Throw
        Catch Ex As Exception
        Throw New CallOneException       
	Finally
        End Try
    End Sub 
 
Private Sub CallTwo()
        Try
        Catch ex As Exception
        Throw New CallTwo Exception
        Finally
        End Try
    End Sub

Open in new window

0
 
kkammAuthor Commented:
Abel,

I posted a spinoff question for exceptions:

http://www.experts-exchange.com/Programming/Theory/Software-Design/Q_24365509.html

Thanks for all your help.
0
 
abelCommented:
tx for the grade, kkamm, I believe I didn't get your last two follow-ups through my mail. That happens sometimes. Looks like you have quite some activity in the other question now, so at least you were not entirely abandoned :)
0

Featured Post

Vote for the Most Valuable Expert

It’s time to recognize experts that go above and beyond with helpful solutions and engagement on site. Choose from the top experts in the Hall of Fame or on the right rail of your favorite topic page. Look for the blue “Nominate” button on their profile to vote.

  • 8
  • 7
Tackle projects and never again get stuck behind a technical roadblock.
Join Now