My two bits:
there is a difference between a layer on one machine and creating
independent objects on multiple networked systems. I'm refraining my
comments to the idea of a layer of classes, all implemented in a single
solution, and deployed to a single machine. I can discuss the other, but
that doesn't appear to be the topic in question.
I usually have a low level data object of some kind, just to isolate my code
from the calls to ADO.NET... mostly because I don't trust that the ADO
interface itself won't change. I add on other classes to form the data
layer. The logic for connecting to the database, and the logic for getting
the data into the right entities, is all handled in the data layer objects
(which may be quite a few classes).
The business objects layer does not know the following:
--- whether data is accessed using SQL statements or stored proces
--- the names of the stored procedures, or the names of the tables
themselves.
--- when to create a transaction and when to commit it
--- where to get the connection string, and what to do if the login fails
--- how to manage the connection object, or whether the database is
connected
--- what version of MDAC is installed
--- what foreign key column has to be copied to a dependent table row
when it is created
--- whether primary key values are mnemonic, auto-number, or
uniqueidentifier (GUID), and who assigns them.
It is OK if the business layer knows the following...
--- that data is stored in tables, as long as the BL doesn't need to refer
to the actual table names
--- that the data is stored in a SQL Server RDBMS (that's pretty specific,
but I'm willing to live with it)
--- the names and data types of the columns
--- the values for the Primary Keys
Other people may want to partition these responsibilities differently, and
I'm not saying that this list, which usually works for me, will work for
anyone else, or even that it always works for me in every possible case.
That depends on the application.
I have some other rules of thumb:
--- write no code that obscures good functionality already provided in
the framework Extension is OK. Hiding for the sake of hiding is harmful.
--- I will write a defect for every 20 lines of code (on a really good
day). If I don't want to create job security, I will write as little code
as possible.
--- Use design patterns to hide the details of implementing an object, so
that objects that have the same
attributes can be substituted for each other (my loose
interpretation of the Liskov Substitution Principle)
--- Minimize Coupling. Maximize Cohesion. Harder to do than it sounds.
Personally, I have no problem passing around data in DataSet objects. I
personally have not become disenchanted with them, but I don't pretend to
use every feature. If I did, I imagine I would join the chorus of folks who
can find fault with this generic disconnected data object. I also have no
problem using a DataReader, and copying the data into a collection of
DataRow objects that I pass around.
The U/I layer is more than the ASP.NET web pages. I use code behinds
exclusively. In addition, there is nearly no code in the code behind. Only
enough to create an object that is used by the page and to call that object.
This layer of U/I objects is still part of the U/I layer. This is done so
that I can write NUnit tests for the U/I objects without having to call the
Web pages themselves to test the application.
My rules of thumb for the U/I management layer:
--- no web or windows controls may be passed as a parameter to the U/I
object layer.
--- validation happens in the U/I management layer by calling the
business object layer. It does not happen in the
code behind and, as much as possible, it does not happen in the U/I
management code itself... it's a pass through.
--- If I must hard-code a list of values for the web pages, the list is
specified in an object in the U/I management layer, not in the web page.
--- Use the O/S to provide security services as much as possible. Do not
store passwords in the database, due to the
complexities of handling encrypted in a secure manner. (This also
goes back to writing as little code as I can).
Business rules live in the BL. The rules do, and the formulas do.
Constants, however, and easily isolated hard-coded strings, all live in the
config files. Note: all layers can use the config files. There is nothing
at all wrong with placing the names of your stored procedures in the config
files as well as the business rule constants. (For example, if I have a
rule that says "when an invoice goes unpaid for 30 days, send a message to
the exchange alias "WarnAccounting", then I will store the number 30 and the
alias "WarnAccounting" in the config file, along with the name of an XSL
template that will be used to generate the layout of the e-mail itself).
I hope this helps.
--- Nick Malik