3.3 Layering Your Architecture
This book is about building
code in a simple way. The principle in this chapter,
"Do one thing, and do it well," may
seem like it argues against building complex software. But it simply
means each major piece focuses on a single aspect of the overall
solution.You can organize an application in layers, so that
you'll only have to deal with small, focused chunks
of the application at any given point of time. In this section,
I'll talk about the anatomy of a layer, the
interfaces between layers, and the typical layers that
you're likely to find in better Java applications.
Before I start piling on generic information about how you should
build layers, here's what you should not do:Don't bite off too much in any given layer. Your
layers should have a simple purpose, and they should be easy to
digest at one sitting. If your layers are too fat,
they'll be too hard to test, maintain, and
understand.Don't add a layer because you read about it in a
book. In most cases, you should add new layers and design patterns
based on real, experienced need, not assumed need.If you're testing as you go, your tests will dictate
some level of layering. Don't resist. Your tests
mirror usage patterns in many ways, and they'll make
your code easier to reuse and decouple as the need arises.Pay attention to names. Think of each independent layer as a library
of services, even if there's just one client at the
moment. If you misname your services, your layer will be misused. If
a name becomes a problem, change it. If it's too
hard to change names, get a tool that lets you do so.
Intellij's IDEA was used to build some of the
software in this book, and its refactoring tools are extremely
All developers layer; some just do so more effectively and
intentionally. In the last section, you probably noticed that I threw
some requirements out to handle later. I did so because the postponed
requirements were natural layers for the emerging architecture. I
explicitly defined layers for the business domain model and the data
access object. I implicitly defined a layer for the user interface, a
potential façade, and security. Let's
look at the anatomy of a layer.
3.3.1 The Basics of Layering
OOP makes effective layering
much easier. Each layer should do one
fundamental thing. There are many different layers, but they only do
a few different types of things:
Often, a software layer
does what you need it to, but it may have
an interface that's complex or awkward for your
application. An abstraction layer presents a simpler interface for
its clients. Façade layers are abstraction layers.
These layers do the specific
work of an application. The
Account object is a functional application layer.
These layers are similar
to application layers, but they provide
common services potentially needed by many applications. The line
between a service layer and an application layer can be blurry.
3.3.2 Interfaces Between Layers
Figure 3-3 shows how
the layers fit together. Ideally, you want
a strict hierarchy. Typical layers are clients of layers below, and
provide services for layers above. The lower-level layer usually
exposes and maintains the interface, but that's not
always the case. When you expose an interface (like the darker areas
of Figure 3-3), you need to harden it more. Peer
at the same level of abstraction and do
roughly the same type of work. Peer interfaces might have a tighter
level of coupling than other layers, because peers often share
bi-directional interfaces. Often, you might not want that tighter
Figure 3-3. A typical intermediate software layer has layers above and below
The lower-level services that you use, those defined by the Java JDK,
know nothing about our account. The account uses JDK library classes
like String. If the interface of the JDK changes,
then you've got to move Account
to change with it. The Account, on the other hand,
knows nothing about the DAO layer. The Account DAO
layer that saves and loads an account is a client of the
Account.Most people think about interfaces as two equal operators, like two
people shaking hands with presumably the same equipment. Usually,
that's not the case. Most interfaces impose a
direction. One layer presents an interface, and the others use it.
Your interface should simultaneously provide complete services to the
consuming layer while protecting the inner workings of the presenting
layer.In general, you'd like your layers to observe a
strict hierarchy. Lower-level layers should not have direct knowledge
of higher-level layers. Peer relationships can be especially
troublesome, and deserve strict attention.
18.104.22.168 A word about good interfaces
You can make your layers much more effective if you concentrate on
building a good interface around them. An interface bears some
resemblance to food: everyone knows when it's good
and when it's not, although not everyone will agree;
it also takes whole lot more skill to make it right than to consume
it. You could write a whole book on interfaces and still not say
everything that needs to be said, but here are some guidelines for
good interfaces. They should be:Complete
You should be able to use your layer to do everything that you need
to do through your interface. Everything doesn't
need to be easy, but everything does need to be available.
Don't allow unnecessary duplication in your
interface. When you duplicate, you're more prone to
error, and you create additional maintenance burdens.
Don't build in future requirements until you need
them; don't add too many convenience methods;
don't expose private methods.
It should make it easy to do the things that you do the most.
Convenience and compactness are often at odds.
You'll want to assure compactness first, then
convenience. That doesn't mean that you can
completely blow off convenience.
An interface should protect you from incorrect usage. If a null value
breaks your code, throw the appropriate exception.
Consistent and predictable
An interface should observe standards, and should do similar tasks in
Often, a few smaller, more focused interfaces are better than one
Interfaces can take many forms, but all should observe these basic
principles. They apply to messaging models, distributed remote
procedure calls, and basic method invocations, too. The need for good
programming hygiene is magnified between major levels of your
architecture. Two excellent books for good interface design are
The Pragmatic Programmer by Andrew Hunt and
David Thomas and Effective Java by Joshua Bloch,
both published by Addison-Wesley.
3.3.3 Common Layers
In my classes, many
my students ask me what layers a Java
application should have. I tell them that it depends on what you want
to do. Java application architectures are converging around a common
architecture in several critical places. In truth, many of those
layers are unnecessary, or mandated by ineffective architectures like
EJB CMP. In other places, certain effective layers, in the right
circumstances, are beginning to emerge.
22.214.171.124 Business domain models
Some development shops are
moving away from a classic
business domain model. Frankly, for some applications (like those
built to baby-sit big relational databases), I'd
have to agree with their direction. When you're
doing nothing but viewing and entering relational data, a full
object-oriented model is overkill.When you do have a legitimate need for an effective business model,
it should usually be the center of your application. Build services
and interfaces around that layer to persist, display, notify, and
manipulate that model. The model is too important, and often too
complex, to clutter with other detailsso transparency becomes
126.96.36.199 Data access
frameworks exist to persist business
objects. At the low end is a simple data access object implemented
with JDBC. At higher levels, full persistence frameworks build in
value-add services like lazy loading and caching across clusters.
Since EJB CMP left such a bad taste in people's
mouths for so long, Java developers are moving back toward simpler
JDBC-based architectures, and also toward transparent persistence
solutions such as JDO and Hibernate.
The era of CORBA, where a
business domain model was distributed across many different nodes, is
dying rapidly. Instead, you're more likely to see
strategic communication between hierarchical layers of an application
(like session beans between an application server and a presentation
server), and between major applications. As such, packaging a service
with a technology like web services or JMS with XML is much more
prevalent. Older systems come into play here; also, disparate
technologies like Microsoft's .NET platforms are
increasing in popularity.
A façade is often
primary client of your business model.
Your goal is to provide a higher-level interface for the rest of the
world. Before you add a façade layer, you must understand
the value that it's providing. I've
seen clients mirror the interface of their DAO layer, verbatim,
within their façade. Façades are much more
interesting when you're doing some consolidation,
such as returning all members of a department across a distributed
interface instead of making a separate round trip for each one.For distributed architectures, you often want to present a simple,
coarse-grained interface to clients, instead of a complex,
complicated business domain model. The façade layer rolls
many fine-grained methods up to a simpler interface.If you're one of those developers who tend to
believe everything written by a major software vendor, now would be a
good time to pick up some rotten tomatoes and eggs.
I'm going to get a little heretical. Your
façade layer need not be distributed at all. You can
simply deploy your presentation layer and your business layers on the
same box. If your façade is not distributed, you probably
don't need those session beans. And if
you're not getting transactional integrity or
security from your façade, you may not need a
façade layer at all. You'll see more
about the role of an effective façade in Chapter 4.
188.8.131.52 User interfaces
One of the most
famous patterns for layering
software is the model-view-controller architecture made popular by
the Smalltalk community. Java model-view-controller architectures
break user interfaces into three separate sections: a browser-based
user interface, a controller, and a wrapper around the domain model.Java developers fully embrace this concept and generally support an
open source implementation called Struts to separate the user
interface layers for a web-based application. Figure 3-4 shows the typical arrangement. A browser-based
HTML interface calls a controller servlet, via HTTP. This servlet
invokes an action, which wraps the domain model. Then, the controller
calls a JSP, which compiles to a servlet, and returns a page to the
client, possibly with results from the domain model.
Figure 3-4. The model-view-controller architecture provides an approach to layering user interface code (this figure is a slight variation of the original, and supports web user interfaces via Struts)
option than Struts, because it supports more sophisticated user
interfaces. Still, I'm skeptical about JSF for a few
reasons:As with all event-based client-server models, JSF potentially
increases communication costs between the client and server.JSF has been designed in a closed committee. That's
often been a recipe for disaster.JSF is complex and will require specialized tools to be effective.
The tools from some vendors tend to be proprietary, so
it's important to watch the requirements evolve.
Starting with clean layers is only the first step. In the best of
cases, you're going to want to refine your designs,
and adjust your approaches. That's the subject of
the next section.