T
Tyrant Mikey
Hi everyone,
I could really use some help wrapping brain around "Proper n-Tier
Architectures" (TM) in .NET. Please forgive me if this is the wrong
forum in which to post this--I couldn't seem to find a
microsoft.public.dotnet.architecture newsgroup.
A recent post in August grappled with the idea of how to abstract away
the disparate layers. In that post, the author suggested the following:
======================================================================
Class Description
----------------------------------------------------------------------
Users.DAL A pure data-access component. Knows only how to insert
or update data, or return a data set containing data.
Users Knows how to invoke the methods in Users.DAL. For
data retrieval, knows how to instantiate a new User
and fill it with data returned by the Users.DAL class.
User A plain business class. Encapsulates all the business
logic for a user. Knows *nothing* about the database,
and isn't aware that the Users, Users.DAL, or
UserController classes exist.
UserController Is able to take the data in a User object and bind it
to a WebForm's controls.
WebApp The Web application itself.
======================================================================
Now, I get most of that. However, to me, UserController looks like just
an extra class--I can't seem to justify its existence in my head. In
the example cited, it took a treeview control and filled it with users:
UI Layer:
UserController.GetUserList(treeview, ConnectionObject, Language);
UserController:
public static void GetUserList(TreeView tree, ConnectionObject conn,
string Language)
{
Users users = new Users(ConnectionObject, Language);
foreach(User user in Users)
{
AddNode(user, currNode);
}
}
That code seems too tightly bound to the user interface choice for it
NOT to be in the UI itself. (It knows that it's populating a
TreeView!!) Can someone tell me why this would NOT be in the WebForm
itself?
In *my* head, UserController would be the class that coordinates the
Users.DAL and User classes. I *think* that (if we use the MVC paradigm)
User is the view, Users.DAL is the model, and UserController is, well,
the controller. Can someone please either correct my thinking or show
me a better way to think of this?
TRANSACTIONS
============
So, assuming we've resolved the issue with the mystifying
UserController class, let's consider transactions. The model proposed
at the top of this post seems to imply that the Users class only knows
how to operate on one table in the system.
What happens when you need to modify multiple tables, and treat all the
modifications as a transaction? For instance, when we add a new
employee to a database, we create records in several tables. We need to
apply them all, or none of them. Using the model shown at the top of
this post, you'd invoke several different DAL classes; in theory, you'd
also have to pass the transaction to each of them.
(There *is* an alternative, but I can't believe it's the recommended
way to do it: include code for modifying multiple tables in a single
DAL class. That seems suicidal to me; if I change the structure of the
table, I have to find every point in the code that refers to it and fix
it. The compiler isn't going to be any help there--and finding those
changes is going to be nightmarish at best.)
Currently, we have two versions of the Save method on the DAL class:
public static void Save(User user);
public static void Save(User user, SqlTransaction transaction);
When I need to include a table in a transaction, I pass the transaction
along to the Save method. I'm really hoping that there's a better way
to do this. Thanks to Steve McConnel, I absolutely *detest* pass-thru
parameters (stuff I pass to a function just because it's need by some
other function umpteen levels down the call chain). If I have to, I can
deal with them, but isn't there a better way to implement transactions
than passing the SqlTransaction object around?
TESTABILITY
===========
Okay, so, now we come to my final issue: Testability. I want to be able
to test every component I write using NUnit. That means I need to be
able to create mock objects--LOTS of mock objects if need be.
What would you guys do to improve the testability of an n-tier
architecture? I know that the liberal use of interfaces is recommended,
and I can easily see an IDataAccessComponent interface with the basic
four: Delete, Exists, Load, Save. But I'm not sure I can see the sense
in writing an interface for EVERY class in each tier.
Can you TDD gurus shed some light on this?
Thanks all. I really appreciate your help.
I could really use some help wrapping brain around "Proper n-Tier
Architectures" (TM) in .NET. Please forgive me if this is the wrong
forum in which to post this--I couldn't seem to find a
microsoft.public.dotnet.architecture newsgroup.
A recent post in August grappled with the idea of how to abstract away
the disparate layers. In that post, the author suggested the following:
======================================================================
Class Description
----------------------------------------------------------------------
Users.DAL A pure data-access component. Knows only how to insert
or update data, or return a data set containing data.
Users Knows how to invoke the methods in Users.DAL. For
data retrieval, knows how to instantiate a new User
and fill it with data returned by the Users.DAL class.
User A plain business class. Encapsulates all the business
logic for a user. Knows *nothing* about the database,
and isn't aware that the Users, Users.DAL, or
UserController classes exist.
UserController Is able to take the data in a User object and bind it
to a WebForm's controls.
WebApp The Web application itself.
======================================================================
Now, I get most of that. However, to me, UserController looks like just
an extra class--I can't seem to justify its existence in my head. In
the example cited, it took a treeview control and filled it with users:
UI Layer:
UserController.GetUserList(treeview, ConnectionObject, Language);
UserController:
public static void GetUserList(TreeView tree, ConnectionObject conn,
string Language)
{
Users users = new Users(ConnectionObject, Language);
foreach(User user in Users)
{
AddNode(user, currNode);
}
}
That code seems too tightly bound to the user interface choice for it
NOT to be in the UI itself. (It knows that it's populating a
TreeView!!) Can someone tell me why this would NOT be in the WebForm
itself?
In *my* head, UserController would be the class that coordinates the
Users.DAL and User classes. I *think* that (if we use the MVC paradigm)
User is the view, Users.DAL is the model, and UserController is, well,
the controller. Can someone please either correct my thinking or show
me a better way to think of this?
TRANSACTIONS
============
So, assuming we've resolved the issue with the mystifying
UserController class, let's consider transactions. The model proposed
at the top of this post seems to imply that the Users class only knows
how to operate on one table in the system.
What happens when you need to modify multiple tables, and treat all the
modifications as a transaction? For instance, when we add a new
employee to a database, we create records in several tables. We need to
apply them all, or none of them. Using the model shown at the top of
this post, you'd invoke several different DAL classes; in theory, you'd
also have to pass the transaction to each of them.
(There *is* an alternative, but I can't believe it's the recommended
way to do it: include code for modifying multiple tables in a single
DAL class. That seems suicidal to me; if I change the structure of the
table, I have to find every point in the code that refers to it and fix
it. The compiler isn't going to be any help there--and finding those
changes is going to be nightmarish at best.)
Currently, we have two versions of the Save method on the DAL class:
public static void Save(User user);
public static void Save(User user, SqlTransaction transaction);
When I need to include a table in a transaction, I pass the transaction
along to the Save method. I'm really hoping that there's a better way
to do this. Thanks to Steve McConnel, I absolutely *detest* pass-thru
parameters (stuff I pass to a function just because it's need by some
other function umpteen levels down the call chain). If I have to, I can
deal with them, but isn't there a better way to implement transactions
than passing the SqlTransaction object around?
TESTABILITY
===========
Okay, so, now we come to my final issue: Testability. I want to be able
to test every component I write using NUnit. That means I need to be
able to create mock objects--LOTS of mock objects if need be.
What would you guys do to improve the testability of an n-tier
architecture? I know that the liberal use of interfaces is recommended,
and I can easily see an IDataAccessComponent interface with the basic
four: Delete, Exists, Load, Save. But I'm not sure I can see the sense
in writing an interface for EVERY class in each tier.
Can you TDD gurus shed some light on this?
Thanks all. I really appreciate your help.