Another option for keeping application tiers independent

J

jehugaleahsa

The Unavoidable Dependency between the User Interface and the Data
Tier

There is a big push by software engineers, consultants and authors
alike that a clean separation of application logic into tiers is the
only way to write code. However, in the real world, this is not as
practical / possible as they make it seem. The truth is that most
applications that interact with authenticated users cannot be so
perfect.

The most well-known approach to tier separation is known as the three
tier architecture. It is a layered architecture where the user
interface rest upon the domain tier and the domain tier rests upon the
data tier. This approach is great because it keeps the interface from
knowing where the data comes from. However, in reality, this is rarely
the whole truth.

In an N-tier application (specifically a three-tier application),
there are definite benefits of keeping the user interface unaware of
where its data is coming from. The ultimate goal is to ensure that
changes to the data source do not affect the user interface. To some
extent, however, this is not entirely possible. If we can ensure that
our data tier interface remains unchanged then we are fine.
Unfortunately, changes to the interface are usually inevitable.
Therefore, the data tier will likely affect the domain model and
therefore likely affect the user interface. A great deal of effort can
be put toward dealing with the issue, but it is by no means a simple
task. I will assume that the reader is aware of the intricacies of
tier separation.

The problem is that eliminating dependencies is almost impossible in
most applications. The ideal would be to have a library of data
objects, which are created directly from the data source, to have
business objects that are purely functional and may provide a few
public properties and to have a user interface that doesn’t care where
its data is coming from. Unfortunately, only in the simplest
applications can this be achieved.

It is not hard to separate the domain tier and the data tier. The
domain can be given an instance of a data object. Since the data
object provides its information through an interface, changes to the
data source will not affect the domain. We can make the data tier a
library for reuse in other applications. If the data tier requires
connection information, we can pass a valid connection object to the
library. As we will see, this is where things become an issue…

We may also want to make our domain tier a library, as well. We would
like to reuse as much domain logic as possible across applications. Of
course, this is not as simple as the data tier. For instance, if the
domain tier is dependent on our data tier, it may have to provide
connection information to the data tier. Suddenly, our domain tier is
awfully aware of the data source. In the real world, this is usually
not a big deal. However, when developing a large enterprise project it
can mean the difference between success and failure. The only option
is to take the connection information out of the domain tier
altogether. But, wait, how does the data tier get what it needs?

We definitely don’t want the user interface passing connection
information. But, if we are requiring users to log into our
application, the interface has to know a little about the data source
anyway. Your shop may have single sign-on or use proxy connections,
where all that is needed is a user name or client ID. However, this
information still comes with the assumption that the data source
requires them. There is NO WAY to generically specify login
information in every possible situation! In the real world, it is okay
for a shop to assume that all data sources are databases. Don’t let a
smart consultant with a PhD tell you that you can’t pass login
information from the interface; it is impossible to get around it.

Why not Hard-Code?

With that in mind, you may ask, “Why not hardcode the login
information in the data tier?” The reason is that in most development
shops, someone needs the ability to audit who is looking at and
changing their data. Even if your shop doesn’t require this now, it
probably will some day. By completely separating your tiers by hard-
coding you are likely to generate dependencies anyway; when you decide
to include auditing you must change the data tier interface to accept
the login information. This will likely require a change in one or
more of the other tiers. It is like trying microwave soup with the lid
on – as soon as it gets too hot, the lid will blow off and make a
mess. In this case, believe it or not, having some dependencies is the
better, long-run solution.

Providing Login Information while Remaining “Independent”

The question still remains: how to get the login information from the
interface to the data tier without involving the domain tier and still
keeping the interface’s awareness to a minimum. The trick is that the
user interface changes for each application. You want to reuse your
domain logic and data objects as much as possible. However, your
interface is unlikely to be as reusable. The difference is seeing the
login user interface as separate from the rest of the user interface.
The idea is to create a new class that is specific to the application.
Its sole purpose in existence is to grab data from the data tier and
pass it to / generate the domain objects. It does the same thing in
the opposite direction as well. This class will do the work required
to create that extra connection field required by the data tier. It
will take that responsibility away from the domain tier. This can be
taken even further, to completely remove any dependency that the
domain has on the data.

This new class, a mediator, isn’t really part of the user interface.
It is told what it needs solely by the login user interface. If you
think about it, you may wonder whether this new mediator makes the
domain dependent on the application. It is easy to see that the data
tier is like a service, data is either requested from it or changes
are persisted. The connection information is dealt with by the
mediator. However, the domain also needs to tell the data tier to
persist changes. The obvious way for it to do this is to have the
domain tier directly call a data tier method. This, of course, will
require connection information, so we can’t do that. We can’t have the
domain call a method in the mediator because this would make the
domain dependent on the application. Geez!

One solution is to make the domain raise events and to have the
mediator respond to them. This works great because it allows the
interface to interact with the domain directly. This also allows the
application to optionally ignore domain requests – this may or may not
be a good thing. Unfortunately, many developers do not understand the
event model very well. Furthermore, there is no way of forcing a
listener to handle every event that gets fired.

Another solution is to provide the domain library as a set of abstract
classes. Here, in order to use a domain object, you inherit from the
abstract class overriding methods that request data objects or pass
them back for update. In this scenario, most of the logic is embedded
in the abstract classes and applications wishing to use the domain are
forced to provide each required method. Unfortunately, this means that
the domain library isn’t concrete. This isn’t necessarily a bad thing,
but it prevents the library from being treated like a service.
Additionally, not many developers are totally comfortable with
abstract classes either. Whether events are more confusing the
abstract classes depends on the developer. Finally, there is a
potential for an explosion of classes – it is not fun overriding
thirty different domain objects in each application you write.

Changing the Paradigm to Find a Different Solution

Thinking of the domain as a service can lead to a new solution. If we
pull directly from our domain object, we are forced to handle the
domain object requesting data. That leads us back to our last two
solutions. Similar to our data tier, our mediator needs to request
information or request an action be done on the application’s part. We
can achieve this by having the application make requests to the domain
tier. However, if we make it the domain object’s job to fully
construct themselves we are no better off, since they will need to
grab information from the data tier. Instead, we have to provide what
our domain objects need. For instance, if our domain object requires a
data object, then we request a domain object by passing the data
object as a parameter. The domain object is then returned to do
business-specific logic on the underlying data. When it is time to
update the database, the mediator can request from the domain object
the data that has been mutated. The mediator can then pass the data
object to the data tier with the connection information.

This approach is very different from the usual hierarchical structure
in applications. Most applications tend to layer the tiers, such as
the three tier architecture. However, in the real world, the interface
is always a little more aware of the data tier than it should be.
Furthermore, the domain is dependent on the data tier. With previously
described approach, the domain tier and the data tier can be
completely independent and concrete implementations. Furthermore, the
login user interface can hide the application-specific mediator behind
an interface with a factory class, thus isolating the rest of the
application from data source changes.

There are multiple ways of implementing this setup. To keep the domain
tier and the data tier completely separate, the mediator must know how
to take a data object and create a domain object and how to take a
domain object and create a data object. This usually involves
providing public accessors and mutators for setting the data in each
of the objects. There are reasons why this is bad, though. For one, in
order to update a database, primary key information must exist as long
as the record is active. However, if a field representing a primary
key is present in the domain, it will probably make the domain
dependent on the data tier anyway. We cannot drop the primary key
altogether, so we could allow the domain to provide a data dictionary
for storing non-domain pertinent information. We could then store
primary key information in the domain as a key-value pair. This is not
a bad solution, but it takes away from some of the type-safety. There
is also nothing stopping a domain developer from using the key-value
pair, by-passing all your hard work to keep things independent.

Introducing the Data Object Wrapper

Another option is to have the domain tier provide an interface
describing the data it is expecting. I call this interface a data
object wrapper. Applications that use the domain tier can implement a
data object wrapper, using aggregation to redirect the accessors and
mutators to an underlying data object from the data tier. Furthermore,
the data object can implement the data object wrapper interface, and
thus remove the need for aggregation. The data object is kept alive
throughout the session and then updated by passing the underlying data
object to the data tier. This can be a difficult task in stateless
environments. If your data objects are data object wrappers, you can
avoid an explosion of classes across the tiers.

Should I Display the Data Object or the Domain Object?

There has always been some confusion as to what should be used to
display data on a user interface. For instance, in a lot of form-based
applications, the user interface simply displays the data from the
data source, perhaps performing some type of formatting of the data.
In the layered tier approach, the domain object is responsible for
providing the user interface with the data. This means that the domain
object needs to provide accessors and potentially mutators. This
suddenly creates a shift in what the domain objects are really all
about. Domain objects are about capturing business logic. Their job is
not to manage data – that is the data tier’s job. Domain objects are
supposed to perform a business process given the data it needs.
Applications that use domain objects as an intermediate step between
the data tier and the user interface try to apply business logic to
things like input validation. While there may be a business rule that
indicates that all customers have a name, it is somewhat overkill if
that is all your domain object is doing. Many practitioners suggest
validation logic be placed on the user interface-side, the domain-side
and the data source-side. In other words, let the interface capture
obvious validation issues. If the user somehow circumnavigates it, let
the domain double check. If the domain fails, use database constraints
as the last stand. There is little point writing a domain object if
all it does is something already covered by every other tier.

Eliminating the Middle Man

In a layered architecture, eliminating the domain tier is a huge sin.
That is because the user interface becomes more intimately aware of
the data source. In the real world, the application is already aware
of the data source (to some degree). Assume that we don’t pass the
domain objects to the user interface. Instead, assume we pass to the
user interface the same thing we pass to the domain, a data object
wrapper. We use the domain tier as more of a filter, decorator, data
checker or part of a workflow. Our mediator will request data from the
data tier. It will pass a data object wrapper to the domain tier. The
domain tier will perform business rules on the data, mutating it if
necessary, and then hand it back to the mediator. The mediator then
takes the data object wrapper and binds it to the interface. Since we
get out the same thing we put in (namely, the data object wrapper), we
can by-pass the domain tier altogether. Again, only the mediator is
aware of the data tier. Furthermore, if we want, we can include many
more layers, we can separate the data object wrapper interface from
the domain tier and use it in the other N tiers. In this sense,
transferring the data from the data tier to the user interface is like
passing data down a production line.

A big benefit of this approach is that only one part of the user
interface, the concrete mediator, is aware of the N tiers. It also
helps identify the responsibility of each tier without clouding them
with unnecessary accessors and mutators. Furthermore, by passing the
data object wrappers amongst tiers, the Iterator and Visitor design
patterns can be easily used to allow for future tier additions.

Implementation

So how does anyone implement this? The trick is to define a library
that provides interfaces representing the data object wrappers – one
for each entity type in the application. For each tier, create a new
library or application that references the wrappers library. For the
data tier, create factory classes that receive the connection
information needed to generate data objects. Return the generated data
objects using the data object wrapper interface. Thus the data objects
are data object wrappers.

In the application, create a concrete mediator class for each entity.
This mediator will have a way of receiving connection information,
probably by using a registry class (static class for holding
application-wide data), and will use it to requests data object
wrappers from the data tier. It will then pass the data object
wrappers through / to all the tiers, including the user interface.

You should probably hide each concrete mediator behind a mediator
interface and a factory class, if you feel it is necessary. It may be
beneficial to do this if you believe you may use different concrete
mediators. If certain operations, such as updating can be performed
uniformly for each mediator, it may make sense to create an abstract
mediator.

Have all requests from the user interface access the mediator to
retrieve information. Have the mediator request the data from the data
tier. Pass the data returned from the data tier to each of the other
tiers. You can do this multiple ways. One way would be to have a
method in a tier accept a list of data object wrappers and return a
list of data object wrappers. This approach allows the domain, for
instance, to filter data that didn’t meet certain business rules. It
would also allow the domain to wrap the domain object wrapper yet
again with its own business-aware data object wrapper. If this process
of passing and returning lists of data object wrappers can be
standardized across all the middle tiers, the Iterator and Visitor
patterns can be used to simplify the process.

More than likely, though, you will not have tier after tier that
actually work in any particular order. However, if you do, this can be
a powerful solution. More realistically, each tier will just want to
look at the data and perform business-specific checks and actions on
it. Since each tier will look at the data through the data object
wrapper, all changes will occur polymorphically. If the application
has state, updates in the data tier can be as simple as calling update
on the data object.

Some Other Thoughts

One thought that comes to mind is that the interface can be similar to
the other middle tiers, in that it is given data and it returns data.
In this scenario, the interface is not at an end of the application
architecture – it is in the middle. Of course, actions will be invoked
by the user interface, but, when data is returned from the data tier
it doesn’t necessarily have to be the final destination. The data,
once processed by the user, can be sent through the tiers again and
eventually end up in the data tier to be persisted. Here, an
application can be thought of as circle from and to the data tier.

Exception handling should be simpler to implement if the tiers are
adjacent to the application. In reality, data tier errors can rarely
be handled by the domain tier and are thus propagated to the user
interface. Again, this means the layered approach makes exception
handling more difficult and more risky. For instance, part of the data
may have already been processed by the domain tier before an error
occurred. In some cases, there are ways to rollback, but the code to
do so can be cluttering and error-prone. By placing the tiers adjacent
to the application, errors can be caught at any point, dealt with and
then the rest of the process can resume uninterrupted.

An application should be much more scalable using this approach. Since
the tiers are independent, each could be placed in their own process,
running independently from multiple service points. If a tier does not
modify the data it is given, it can be run in a separate thread with
ease. New tiers should be easy to integrate into a system. In fact, if
the tiers are implemented with a common interface, reflection can be
used to load libraries and services dynamically at runtime.

In a Stateless Environment

Someone reading this might think this approach is difficult to
implement in a stateless environment. True, it puts a hamper on
treating the user interface as a middle tier in the process. But the
real problem is that it is hard to track the identity of data objects.
It would be nice to hide the primary key information, etc. from the
rest of the application. In fact, you can probably still hide the
identifier from the middle tiers. The middle tiers will get ran
somewhere between the data tier returning data and the user interface
displaying it. At that point the state disappears and the middle tiers
have already completed their tasks. So, really, the user interface is
the only tier that needs to retain identifier information somehow.
Well, since our application is already partially aware of the data
source, this isn’t so horrible. Simply create another data object
wrapper that implements the other, adding another field for the
identifier. Pass the smaller interface to the middle tiers and pass
the bigger interface to the user interface. When the user indicates
that they want to save their changes, pass the bigger interface back
to the data tier.

Read-Only is “Easy”

There may be the need to restrict some services from editing the
underlying data object. This is easily achieved by creating a read-
only data object wrapper interface. Then write an editable interface
that implements the read-only interface. Pass the data objects around
using the read-only interface for read-only tiers and the editable
interface to mutating tiers.

Conclusion

Not long ago, there was a considerable amount of confusion where I
work. We were trying to figure out what exactly a business object was.
We were confused as to how to separate the user interface from the
data tier while still passing login information around. We were
confused about how to reuse our code as much as possible. We were
trying to find ways to handle exceptions better when they could be
coming from multiple places. We were trying to figure out how an
application could utilize dynamic workflows without needing to take
down production.

I kind of used this essay as a way to identify all my problems, to see
what my options were and to see what I could deduce from it all. As it
turns out, I think I derailed my previous assumptions about layered
architectures. Obviously, I don’t think they are completely useless,
but I do feel that they may not always be appropriate.

I think I can fully understand my confusion about business objects. In
this essay I referred to them as domain objects. My coworkers and I
have been really trying to understand what a business object really
is. At first, we had confused data objects with business objects. We
thought of the database queries as more of the data tier and the
objects representing that relational data as our business objects. I
believe this a huge misunderstanding that not only my coworkers and I,
but the majority of developers, share. Our problem stemmed from this
need for the domain tier to rest above the data tier. However, in an
application where the data is simply sent from database to user
interface, the domain objects were usually light-weight. The real
problem was that we were trying to mix business logic with the data it
worked on. It is easy to see how we could have made such a simple
mistake, given our presumptions.

We were so concerned about keeping everything separate that we
shuttered at the thought of our user interface knowing about the data
tier. The more applications we wrote, the more we began to see this
ideal fade. It was just recently that I had finally accepted that in
an authenticating application, tier separations were not 100%
possible. So, I told myself to assume that it was okay for the
application to know a little bit. If I hadn’t done that, I would still
be struggling over how to do the impossible. I feel with my current
look on things that it is fine for the application to know a little
about the different tiers. The mediator suddenly starts looking a lot
like a layer. The middle tiers are really at the bottom, communicating
through the mediator. In that sense, I really just created a new three-
tier architecture, where the bottom layer spans out like roots. The
mediator hides what gets done beneath the surface, but is unique
enough that it can’t be reused across applications (or can it?).

We have written multiple applications where we have seen the same code
showing up in each one of them. The same code, multiple times! We
could have saved so much time and made our maintenance so much easier
if we had found how to make our tiers independent. Our applications
can now be more flexible, quicker to write and far more scalable.

We have always wondered about ways to handle exceptions better. It is
awkward to wrap every database request with exception handling.
Especially since that exception probably can’t be dealt with
immediately or at all. Instead, we plagued our domain objects with the
code to report exceptions while the user interface had to implement
another set of routines to make the user aware. There was no way to
place detours on the way from the database to the user interface. With
the current approach, we can process one data object wrapper at a
time, detect exceptions immediately, detour to error reporting and the
user interface and then return to where we were to continue on to the
next data object wrapper. This is, of course, a little harder in a
stateless environment; however, it would still allow server-side
reporting to be done. Since exceptions can be caught by the
application, the application can mark each erroneous data object
wrapper, not preventing other data objects from being processed.

When your company requires data to be processed in a workflow, the
three-tier architecture makes your domain tier seem rather lofty. The
domain tier is essentially responsible for checking that the user
isn’t doing things out of order or that automated tasks are completed
successfully. The domain tier, which should be fairly specific to a
business process, is actually taking on the job of many business
processes. Whatever happened to classes that do one thing and do it
well? With this approach, the mediator takes on the responsibility of
the domain tier, passing data to each domain object in turn. Then, the
domain object is only responsible for performing its single task. Best
of all, with a flexible interface for all tiers, new steps in a
workflow can be added dynamically at runtime.

Overall, I would say allowing the application to know a little more
about the data tier isn’t a bad thing. For most of us, I would say
that the code for each tier is all put together in the same source
code set. However, when you start seeing code being duplicated across
projects, you need to think about ways to reuse code and eliminate
dependencies. The three-tier architecture has gotten us to the point
where we can see the need to keep our code separated. However, it
fails to specify how to do that when one tier gets its information
from the other, specifically login information. It also fails in
allowing us to build onto existing systems without breaking
dependencies and forming new ones. The number of N-tier architectures
and interacting services are growing. There is a stronger need to
control the order of events, placing workflows into the logic of
software. Finally, we need something that works well regardless of the
size of the application, since growth is unpredictable.

Please email me if you have further insights or you want to tell me
I'm just plain wrong. I would appreciate the advice and feedback.
 
I

Ignacio Machin ( .NET/ C# MVP )

Hi,

I failed to see the question in your post.
Next time either post a link or write an addecuated subject
 
B

Bob Powell [MVP]

I think this is possibly the longest post I've ever seen on this group.
I will try to be brief in my response.

Dependencies within any architecture are inevitable. How those dependencies
affect the physical structure of the application is important. Detailed
information on levelization of an architecture, the mastering of the
physical rather than the logical structure of the application can be
obtained easily in a .Net context using nDepend.

Your point about authentication seems to be confused and I don't see how it
might change the way your dependencies work. The role or rights of the user
may be specific but unless your architecture is lousy that shouldn't make
the application more monolithic.

I think that you are describing a need for a service oriented architecture
in which the authentication plays a primary role.

I work in a very secure environment; a bank. We have to deal with
authentication and data oriented architectures all the time and we pay
careful attention to the way we build our applications. We certainly don't
have the sort of problems you seems to complain of and we deal with trades
of millions of dollars per day.



--
--
Bob Powell [MVP]
Visual C#, System.Drawing

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.


The Unavoidable Dependency between the User Interface and the Data
Tier

There is a big push by software engineers, consultants and authors
alike that a clean separation of application logic into tiers is the
only way to write code. However, in the real world, this is not as
practical / possible as they make it seem. The truth is that most
applications that interact with authenticated users cannot be so
perfect.

The most well-known approach to tier separation is known as the three
tier architecture. It is a layered architecture where the user
interface rest upon the domain tier and the domain tier rests upon the
data tier. This approach is great because it keeps the interface from
knowing where the data comes from. However, in reality, this is rarely
the whole truth.

In an N-tier application (specifically a three-tier application),
there are definite benefits of keeping the user interface unaware of
where its data is coming from. The ultimate goal is to ensure that
changes to the data source do not affect the user interface. To some
extent, however, this is not entirely possible. If we can ensure that
our data tier interface remains unchanged then we are fine.
Unfortunately, changes to the interface are usually inevitable.
Therefore, the data tier will likely affect the domain model and
therefore likely affect the user interface. A great deal of effort can
be put toward dealing with the issue, but it is by no means a simple
task. I will assume that the reader is aware of the intricacies of
tier separation.

The problem is that eliminating dependencies is almost impossible in
most applications. The ideal would be to have a library of data
objects, which are created directly from the data source, to have
business objects that are purely functional and may provide a few
public properties and to have a user interface that doesn’t care where
its data is coming from. Unfortunately, only in the simplest
applications can this be achieved.

It is not hard to separate the domain tier and the data tier. The
domain can be given an instance of a data object. Since the data
object provides its information through an interface, changes to the
data source will not affect the domain. We can make the data tier a
library for reuse in other applications. If the data tier requires
connection information, we can pass a valid connection object to the
library. As we will see, this is where things become an issue…

We may also want to make our domain tier a library, as well. We would
like to reuse as much domain logic as possible across applications. Of
course, this is not as simple as the data tier. For instance, if the
domain tier is dependent on our data tier, it may have to provide
connection information to the data tier. Suddenly, our domain tier is
awfully aware of the data source. In the real world, this is usually
not a big deal. However, when developing a large enterprise project it
can mean the difference between success and failure. The only option
is to take the connection information out of the domain tier
altogether. But, wait, how does the data tier get what it needs?

We definitely don’t want the user interface passing connection
information. But, if we are requiring users to log into our
application, the interface has to know a little about the data source
anyway. Your shop may have single sign-on or use proxy connections,
where all that is needed is a user name or client ID. However, this
information still comes with the assumption that the data source
requires them. There is NO WAY to generically specify login
information in every possible situation! In the real world, it is okay
for a shop to assume that all data sources are databases. Don’t let a
smart consultant with a PhD tell you that you can’t pass login
information from the interface; it is impossible to get around it.

Why not Hard-Code?

With that in mind, you may ask, “Why not hardcode the login
information in the data tier?” The reason is that in most development
shops, someone needs the ability to audit who is looking at and
changing their data. Even if your shop doesn’t require this now, it
probably will some day. By completely separating your tiers by hard-
coding you are likely to generate dependencies anyway; when you decide
to include auditing you must change the data tier interface to accept
the login information. This will likely require a change in one or
more of the other tiers. It is like trying microwave soup with the lid
on – as soon as it gets too hot, the lid will blow off and make a
mess. In this case, believe it or not, having some dependencies is the
better, long-run solution.

Providing Login Information while Remaining “Independent”

The question still remains: how to get the login information from the
interface to the data tier without involving the domain tier and still
keeping the interface’s awareness to a minimum. The trick is that the
user interface changes for each application. You want to reuse your
domain logic and data objects as much as possible. However, your
interface is unlikely to be as reusable. The difference is seeing the
login user interface as separate from the rest of the user interface.
The idea is to create a new class that is specific to the application.
Its sole purpose in existence is to grab data from the data tier and
pass it to / generate the domain objects. It does the same thing in
the opposite direction as well. This class will do the work required
to create that extra connection field required by the data tier. It
will take that responsibility away from the domain tier. This can be
taken even further, to completely remove any dependency that the
domain has on the data.

This new class, a mediator, isn’t really part of the user interface.
It is told what it needs solely by the login user interface. If you
think about it, you may wonder whether this new mediator makes the
domain dependent on the application. It is easy to see that the data
tier is like a service, data is either requested from it or changes
are persisted. The connection information is dealt with by the
mediator. However, the domain also needs to tell the data tier to
persist changes. The obvious way for it to do this is to have the
domain tier directly call a data tier method. This, of course, will
require connection information, so we can’t do that. We can’t have the
domain call a method in the mediator because this would make the
domain dependent on the application. Geez!

One solution is to make the domain raise events and to have the
mediator respond to them. This works great because it allows the
interface to interact with the domain directly. This also allows the
application to optionally ignore domain requests – this may or may not
be a good thing. Unfortunately, many developers do not understand the
event model very well. Furthermore, there is no way of forcing a
listener to handle every event that gets fired.

Another solution is to provide the domain library as a set of abstract
classes. Here, in order to use a domain object, you inherit from the
abstract class overriding methods that request data objects or pass
them back for update. In this scenario, most of the logic is embedded
in the abstract classes and applications wishing to use the domain are
forced to provide each required method. Unfortunately, this means that
the domain library isn’t concrete. This isn’t necessarily a bad thing,
but it prevents the library from being treated like a service.
Additionally, not many developers are totally comfortable with
abstract classes either. Whether events are more confusing the
abstract classes depends on the developer. Finally, there is a
potential for an explosion of classes – it is not fun overriding
thirty different domain objects in each application you write.

Changing the Paradigm to Find a Different Solution

Thinking of the domain as a service can lead to a new solution. If we
pull directly from our domain object, we are forced to handle the
domain object requesting data. That leads us back to our last two
solutions. Similar to our data tier, our mediator needs to request
information or request an action be done on the application’s part. We
can achieve this by having the application make requests to the domain
tier. However, if we make it the domain object’s job to fully
construct themselves we are no better off, since they will need to
grab information from the data tier. Instead, we have to provide what
our domain objects need. For instance, if our domain object requires a
data object, then we request a domain object by passing the data
object as a parameter. The domain object is then returned to do
business-specific logic on the underlying data. When it is time to
update the database, the mediator can request from the domain object
the data that has been mutated. The mediator can then pass the data
object to the data tier with the connection information.

This approach is very different from the usual hierarchical structure
in applications. Most applications tend to layer the tiers, such as
the three tier architecture. However, in the real world, the interface
is always a little more aware of the data tier than it should be.
Furthermore, the domain is dependent on the data tier. With previously
described approach, the domain tier and the data tier can be
completely independent and concrete implementations. Furthermore, the
login user interface can hide the application-specific mediator behind
an interface with a factory class, thus isolating the rest of the
application from data source changes.

There are multiple ways of implementing this setup. To keep the domain
tier and the data tier completely separate, the mediator must know how
to take a data object and create a domain object and how to take a
domain object and create a data object. This usually involves
providing public accessors and mutators for setting the data in each
of the objects. There are reasons why this is bad, though. For one, in
order to update a database, primary key information must exist as long
as the record is active. However, if a field representing a primary
key is present in the domain, it will probably make the domain
dependent on the data tier anyway. We cannot drop the primary key
altogether, so we could allow the domain to provide a data dictionary
for storing non-domain pertinent information. We could then store
primary key information in the domain as a key-value pair. This is not
a bad solution, but it takes away from some of the type-safety. There
is also nothing stopping a domain developer from using the key-value
pair, by-passing all your hard work to keep things independent.

Introducing the Data Object Wrapper

Another option is to have the domain tier provide an interface
describing the data it is expecting. I call this interface a data
object wrapper. Applications that use the domain tier can implement a
data object wrapper, using aggregation to redirect the accessors and
mutators to an underlying data object from the data tier. Furthermore,
the data object can implement the data object wrapper interface, and
thus remove the need for aggregation. The data object is kept alive
throughout the session and then updated by passing the underlying data
object to the data tier. This can be a difficult task in stateless
environments. If your data objects are data object wrappers, you can
avoid an explosion of classes across the tiers.

Should I Display the Data Object or the Domain Object?

There has always been some confusion as to what should be used to
display data on a user interface. For instance, in a lot of form-based
applications, the user interface simply displays the data from the
data source, perhaps performing some type of formatting of the data.
In the layered tier approach, the domain object is responsible for
providing the user interface with the data. This means that the domain
object needs to provide accessors and potentially mutators. This
suddenly creates a shift in what the domain objects are really all
about. Domain objects are about capturing business logic. Their job is
not to manage data – that is the data tier’s job. Domain objects are
supposed to perform a business process given the data it needs.
Applications that use domain objects as an intermediate step between
the data tier and the user interface try to apply business logic to
things like input validation. While there may be a business rule that
indicates that all customers have a name, it is somewhat overkill if
that is all your domain object is doing. Many practitioners suggest
validation logic be placed on the user interface-side, the domain-side
and the data source-side. In other words, let the interface capture
obvious validation issues. If the user somehow circumnavigates it, let
the domain double check. If the domain fails, use database constraints
as the last stand. There is little point writing a domain object if
all it does is something already covered by every other tier.

Eliminating the Middle Man

In a layered architecture, eliminating the domain tier is a huge sin.
That is because the user interface becomes more intimately aware of
the data source. In the real world, the application is already aware
of the data source (to some degree). Assume that we don’t pass the
domain objects to the user interface. Instead, assume we pass to the
user interface the same thing we pass to the domain, a data object
wrapper. We use the domain tier as more of a filter, decorator, data
checker or part of a workflow. Our mediator will request data from the
data tier. It will pass a data object wrapper to the domain tier. The
domain tier will perform business rules on the data, mutating it if
necessary, and then hand it back to the mediator. The mediator then
takes the data object wrapper and binds it to the interface. Since we
get out the same thing we put in (namely, the data object wrapper), we
can by-pass the domain tier altogether. Again, only the mediator is
aware of the data tier. Furthermore, if we want, we can include many
more layers, we can separate the data object wrapper interface from
the domain tier and use it in the other N tiers. In this sense,
transferring the data from the data tier to the user interface is like
passing data down a production line.

A big benefit of this approach is that only one part of the user
interface, the concrete mediator, is aware of the N tiers. It also
helps identify the responsibility of each tier without clouding them
with unnecessary accessors and mutators. Furthermore, by passing the
data object wrappers amongst tiers, the Iterator and Visitor design
patterns can be easily used to allow for future tier additions.

Implementation

So how does anyone implement this? The trick is to define a library
that provides interfaces representing the data object wrappers – one
for each entity type in the application. For each tier, create a new
library or application that references the wrappers library. For the
data tier, create factory classes that receive the connection
information needed to generate data objects. Return the generated data
objects using the data object wrapper interface. Thus the data objects
are data object wrappers.

In the application, create a concrete mediator class for each entity.
This mediator will have a way of receiving connection information,
probably by using a registry class (static class for holding
application-wide data), and will use it to requests data object
wrappers from the data tier. It will then pass the data object
wrappers through / to all the tiers, including the user interface.

You should probably hide each concrete mediator behind a mediator
interface and a factory class, if you feel it is necessary. It may be
beneficial to do this if you believe you may use different concrete
mediators. If certain operations, such as updating can be performed
uniformly for each mediator, it may make sense to create an abstract
mediator.

Have all requests from the user interface access the mediator to
retrieve information. Have the mediator request the data from the data
tier. Pass the data returned from the data tier to each of the other
tiers. You can do this multiple ways. One way would be to have a
method in a tier accept a list of data object wrappers and return a
list of data object wrappers. This approach allows the domain, for
instance, to filter data that didn’t meet certain business rules. It
would also allow the domain to wrap the domain object wrapper yet
again with its own business-aware data object wrapper. If this process
of passing and returning lists of data object wrappers can be
standardized across all the middle tiers, the Iterator and Visitor
patterns can be used to simplify the process.

More than likely, though, you will not have tier after tier that
actually work in any particular order. However, if you do, this can be
a powerful solution. More realistically, each tier will just want to
look at the data and perform business-specific checks and actions on
it. Since each tier will look at the data through the data object
wrapper, all changes will occur polymorphically. If the application
has state, updates in the data tier can be as simple as calling update
on the data object.

Some Other Thoughts

One thought that comes to mind is that the interface can be similar to
the other middle tiers, in that it is given data and it returns data.
In this scenario, the interface is not at an end of the application
architecture – it is in the middle. Of course, actions will be invoked
by the user interface, but, when data is returned from the data tier
it doesn’t necessarily have to be the final destination. The data,
once processed by the user, can be sent through the tiers again and
eventually end up in the data tier to be persisted. Here, an
application can be thought of as circle from and to the data tier.

Exception handling should be simpler to implement if the tiers are
adjacent to the application. In reality, data tier errors can rarely
be handled by the domain tier and are thus propagated to the user
interface. Again, this means the layered approach makes exception
handling more difficult and more risky. For instance, part of the data
may have already been processed by the domain tier before an error
occurred. In some cases, there are ways to rollback, but the code to
do so can be cluttering and error-prone. By placing the tiers adjacent
to the application, errors can be caught at any point, dealt with and
then the rest of the process can resume uninterrupted.

An application should be much more scalable using this approach. Since
the tiers are independent, each could be placed in their own process,
running independently from multiple service points. If a tier does not
modify the data it is given, it can be run in a separate thread with
ease. New tiers should be easy to integrate into a system. In fact, if
the tiers are implemented with a common interface, reflection can be
used to load libraries and services dynamically at runtime.

In a Stateless Environment

Someone reading this might think this approach is difficult to
implement in a stateless environment. True, it puts a hamper on
treating the user interface as a middle tier in the process. But the
real problem is that it is hard to track the identity of data objects.
It would be nice to hide the primary key information, etc. from the
rest of the application. In fact, you can probably still hide the
identifier from the middle tiers. The middle tiers will get ran
somewhere between the data tier returning data and the user interface
displaying it. At that point the state disappears and the middle tiers
have already completed their tasks. So, really, the user interface is
the only tier that needs to retain identifier information somehow.
Well, since our application is already partially aware of the data
source, this isn’t so horrible. Simply create another data object
wrapper that implements the other, adding another field for the
identifier. Pass the smaller interface to the middle tiers and pass
the bigger interface to the user interface. When the user indicates
that they want to save their changes, pass the bigger interface back
to the data tier.

Read-Only is “Easy”

There may be the need to restrict some services from editing the
underlying data object. This is easily achieved by creating a read-
only data object wrapper interface. Then write an editable interface
that implements the read-only interface. Pass the data objects around
using the read-only interface for read-only tiers and the editable
interface to mutating tiers.

Conclusion

Not long ago, there was a considerable amount of confusion where I
work. We were trying to figure out what exactly a business object was.
We were confused as to how to separate the user interface from the
data tier while still passing login information around. We were
confused about how to reuse our code as much as possible. We were
trying to find ways to handle exceptions better when they could be
coming from multiple places. We were trying to figure out how an
application could utilize dynamic workflows without needing to take
down production.

I kind of used this essay as a way to identify all my problems, to see
what my options were and to see what I could deduce from it all. As it
turns out, I think I derailed my previous assumptions about layered
architectures. Obviously, I don’t think they are completely useless,
but I do feel that they may not always be appropriate.

I think I can fully understand my confusion about business objects. In
this essay I referred to them as domain objects. My coworkers and I
have been really trying to understand what a business object really
is. At first, we had confused data objects with business objects. We
thought of the database queries as more of the data tier and the
objects representing that relational data as our business objects. I
believe this a huge misunderstanding that not only my coworkers and I,
but the majority of developers, share. Our problem stemmed from this
need for the domain tier to rest above the data tier. However, in an
application where the data is simply sent from database to user
interface, the domain objects were usually light-weight. The real
problem was that we were trying to mix business logic with the data it
worked on. It is easy to see how we could have made such a simple
mistake, given our presumptions.

We were so concerned about keeping everything separate that we
shuttered at the thought of our user interface knowing about the data
tier. The more applications we wrote, the more we began to see this
ideal fade. It was just recently that I had finally accepted that in
an authenticating application, tier separations were not 100%
possible. So, I told myself to assume that it was okay for the
application to know a little bit. If I hadn’t done that, I would still
be struggling over how to do the impossible. I feel with my current
look on things that it is fine for the application to know a little
about the different tiers. The mediator suddenly starts looking a lot
like a layer. The middle tiers are really at the bottom, communicating
through the mediator. In that sense, I really just created a new three-
tier architecture, where the bottom layer spans out like roots. The
mediator hides what gets done beneath the surface, but is unique
enough that it can’t be reused across applications (or can it?).

We have written multiple applications where we have seen the same code
showing up in each one of them. The same code, multiple times! We
could have saved so much time and made our maintenance so much easier
if we had found how to make our tiers independent. Our applications
can now be more flexible, quicker to write and far more scalable.

We have always wondered about ways to handle exceptions better. It is
awkward to wrap every database request with exception handling.
Especially since that exception probably can’t be dealt with
immediately or at all. Instead, we plagued our domain objects with the
code to report exceptions while the user interface had to implement
another set of routines to make the user aware. There was no way to
place detours on the way from the database to the user interface. With
the current approach, we can process one data object wrapper at a
time, detect exceptions immediately, detour to error reporting and the
user interface and then return to where we were to continue on to the
next data object wrapper. This is, of course, a little harder in a
stateless environment; however, it would still allow server-side
reporting to be done. Since exceptions can be caught by the
application, the application can mark each erroneous data object
wrapper, not preventing other data objects from being processed.

When your company requires data to be processed in a workflow, the
three-tier architecture makes your domain tier seem rather lofty. The
domain tier is essentially responsible for checking that the user
isn’t doing things out of order or that automated tasks are completed
successfully. The domain tier, which should be fairly specific to a
business process, is actually taking on the job of many business
processes. Whatever happened to classes that do one thing and do it
well? With this approach, the mediator takes on the responsibility of
the domain tier, passing data to each domain object in turn. Then, the
domain object is only responsible for performing its single task. Best
of all, with a flexible interface for all tiers, new steps in a
workflow can be added dynamically at runtime.

Overall, I would say allowing the application to know a little more
about the data tier isn’t a bad thing. For most of us, I would say
that the code for each tier is all put together in the same source
code set. However, when you start seeing code being duplicated across
projects, you need to think about ways to reuse code and eliminate
dependencies. The three-tier architecture has gotten us to the point
where we can see the need to keep our code separated. However, it
fails to specify how to do that when one tier gets its information
from the other, specifically login information. It also fails in
allowing us to build onto existing systems without breaking
dependencies and forming new ones. The number of N-tier architectures
and interacting services are growing. There is a stronger need to
control the order of events, placing workflows into the logic of
software. Finally, we need something that works well regardless of the
size of the application, since growth is unpredictable.

Please email me if you have further insights or you want to tell me
I'm just plain wrong. I would appreciate the advice and feedback.
 
M

Marc Gravell

I skimmed... I just want to say "dependency injection" and "inversion
of control" - oh, and really bizarre post...

Marc
 
J

jehugaleahsa

I think this is possibly the longest post I've ever seen on this group.
I will try to be brief in my response.

I apologize for the size. I should have probably put this on a blog or
something.
Dependencies within any architecture are inevitable. How those dependencies
affect the physical structure of the application is important. Detailed
information on levelization of an architecture, the mastering of the
physical rather than the logical structure of the application can be
obtained easily in a .Net context using nDepend.

I'm not interested in tools. I interested in concepts / better ways of
handling day-to-day design decisions.
Your point about authentication seems to be confused and I don't see how it
might change the way your dependencies work. The role or rights of the user
may be specific but unless your architecture is lousy that shouldn't make
the application more monolithic.

The point with authentication was that there is no such thing as a
perfect separation between the user interface and the data tier. With
that unavoidable fact, it really doesn't make sense for there to be a
tier in the middle that, more than likely, also needs to be aware of
the data tier. This huge post was really identifying that single fact
and finding ways of eliminating unnecessary dependencies. I never said
the solution proposed should be accepted by anyone; I just thought
someone might find a different approach to system architecture useful.
The folks in my shop did. I would recommend reading "Patterns of
Enterprise Architecture" by Martin Fowler. He touches on the common
problem of keeping tiers separated. In many shops, the solidity of a
system takes months to be realized. Furthermore, keeping things
independent increases the ease of testing. I am kind of questioning
the viability of a layered architecture as is found in many newer
applications. There is a strong assumption by developers that an
application will always stay within the boundaries of a DLL. The
architecture I presented eliminated some of these issues . . . but
obviously came with problems of its own . . .

This article had nothing to do with the size of the application. I
have successfully implemented a few applications with this
architecture. It turned out to be a very useful architecture for
maximizing code reuse. In fact, by cutting a few corners, I was able
to implement these applications with about the same amount of code as
any other architecture. I kind took the goals of the architecture to a
higher degree than necessary. My goal in doing that was to show that
complete independence can be achieved.
I think that you are describing a need for a service oriented architecture
in which the authentication plays a primary role.

Not a primary role. Not even close! The problem is that even a single
log on screen can impact the entire application if the architecture
isn't carefully chosen. Most layered architectures rely on a static
data source. Some risk can be mitigated by hiding the data source
behind the data tier. However, this will not be 100% possible. 99% of
the time in single-point applications, this probably doesn't even
matter. However, in multi-point architectures, assuming one data
source can totally ruin a system. Even switching between on database
vendor to another can be a massive change, since the authentication
method may change.
I work in a very secure environment; a bank. We have to deal with
authentication and data oriented architectures all the time and we pay
careful attention to the way we build our applications. We certainly don't
have the sort of problems you seems to complain of and we deal with trades
of millions of dollars per day.

The size of the application is inconsequential. Your bank probably has
a very specific way of authenticating and a very specific type of
storage. It is probably okay for your shop to assume Oracle or SQL
Server. How aware is your user interface of the database? How aware of
the databases are your business processes? How much of the business
logic is repeated across applications? How well could your software
handle switching between Oracle and SQL Server . . . XML documents?

All I am saying is that it is almost impossible for a change in the
data tier to affect only the data tier. The reality is that it will
undoubtably affect the user interface. If you are using a layered
architecture, it is also likely that it will affect the domain layer.
How do you minimize the impact?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top