﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-scoField's blog</title><link>http://www.cnitblog.com/scofield/</link><description>If we have a dream ,everything will be possible !</description><language>zh-cn</language><lastBuildDate>Tue, 05 May 2026 00:09:51 GMT</lastBuildDate><pubDate>Tue, 05 May 2026 00:09:51 GMT</pubDate><ttl>60</ttl><item><title>Layering</title><link>http://www.cnitblog.com/scofield/archive/2006/06/21/12611.html</link><dc:creator>scofield</dc:creator><author>scofield</author><pubDate>Wed, 21 Jun 2006 02:41:00 GMT</pubDate><guid>http://www.cnitblog.com/scofield/archive/2006/06/21/12611.html</guid><wfw:comment>http://www.cnitblog.com/scofield/comments/12611.html</wfw:comment><comments>http://www.cnitblog.com/scofield/archive/2006/06/21/12611.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/scofield/comments/commentRss/12611.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/scofield/services/trackbacks/12611.html</trackback:ping><description><![CDATA[
		<table cellpadding="5" align="center">
				<tbody>
						<tr>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.martinfowler.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.martinfowler.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.martinfowler.com">
												<img height="32" src="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/mflogo.gif" width="92" align="left" border="0" webstripperlinkwas="mflogo.gif" />
										</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com">Home</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com/articles.html?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com/articles.html'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com/articles.html">Articles</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com/talks.html?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com/talks.html'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com/talks.html">Talks</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com/links.html?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com/links.html'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com/links.html">Links</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com/index.html?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com/index.html'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com/index.html">Contact Me</a>
								</td>
								<td>
										<a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/index.html" webstripperlinkwas="http://martinfowler.com/isa/index.html">ISA</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.thoughtworks.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.thoughtworks.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.thoughtworks.com">ThoughtWorks</a>
								</td>
						</tr>
				</tbody>
		</table>
		<hr />
		<h1 align="center">Layering</h1>
		<hr />
		<p>Layering is one of the most common techniques that software designers use to break apart a complicated software system. You see it in machine architectures where layers descend from a programming language with operating system calls, into device drivers and CPU instruction sets, into logic gates inside chips. Networking has FTP layered on top of TCP, on top of IP, on top of ethernet. </p>
		<p>When thinking of a system in terms of layers, you imagine the principal subsystems in the software arranged in some form of layer cake, where each layer rests upon a lower layer. In this scheme the higher layer uses various services defined by the lower layer, but the lower layer is unaware of the higher layer. Furthermore each layer usually hides its lower layers from the layers above, so layer 4 uses the services of layer 3 which uses the services of layer 2, but layer 4 is unaware of layer 2. (Not all layering architectures are opaque like this, but most are - or rather most are mostly opaque. </p>
		<p>Breaking down a system into layers has a number of important benefits </p>
		<ul>
				<li style="LIST-STYLE-IMAGE: url(good-bullet.gif)">You can understand a single layer as a coherent whole without knowing much about the other layers. You can understand how to build an FTP service on top of TCP without knowing the details of how ethernet works 
</li>
				<li style="LIST-STYLE-IMAGE: url(good-bullet.gif)">You can substitute layers with alternative implementations of the same basic services. An FTP service can run over ethernet, PPP, or whatever a cable company uses without change. 
</li>
				<li style="LIST-STYLE-IMAGE: url(good-bullet.gif)">You minimize dependencies between the layers. If the cable company changes its physical transmission system, providing they make IP work we don't have to alter our FTP service. 
</li>
				<li style="LIST-STYLE-IMAGE: url(good-bullet.gif)">Layers make good places for standardization. TCP and IP are standards because they define how their layers should operate. 
</li>
				<li style="LIST-STYLE-IMAGE: url(good-bullet.gif)">Once you have a layer built you can use it for many higher level services. So TCP/IP is used by FTP, telnet, SSH, http. Otherwise all of these higher level protocols would have to write their own lower level protocols </li>
		</ul>
		<p>Layering is an important technique, but there are downsides. </p>
		<ul>
				<li style="LIST-STYLE-IMAGE: url(bad-bullet.gif)">Layers encapsulate some things well, but not all. As a result you sometimes get cascading changes. The classic example of this in a layered enterprise application is adding a field that needs to display on the UI, be in the database, and thus be added to every layer in between. 
</li>
				<li style="LIST-STYLE-IMAGE: url(bad-bullet.gif)">Extra layers can harm performance. At every layer things typically need to be transformed from one representation to another. However the encapsulation of underlying function often gives you efficiency gains that more than compensate. A layer that controls transactions can be optimized and will then make everything faster. </li>
		</ul>
		<p>But the hardest part of a layered architecture is deciding what layers to have and what the responsibility of each layer should be. </p>
		<a name="N32">
		</a>
		<h2>Evolution of layers in enterprise applications</h2>
		<p>Although I'm too young to have done any work in the early days of batch systems, I don't sense that people thought much of layers in those days. You wrote a program that manipulated some form of files (ISAM, VSAM, etc) and that was your application. No layers present. </p>
		<p>The notion of layers became more apparent in the 90's with the rise of <b>client-server</b> systems. These were two layer systems: the client held the user-interface and other application code and the server was usually a relational database. Common client tools were VB, Powerbuilder and Delphi. These made it particularly easy to build data-intensive applications as they had UI widgets that were aware of SQL. This allowed you build a screen by dragging controls onto a design area and then using property sheets to connect the controls to the database. </p>
		<p>If the application was all about the display and simple update of relational data, then these client-server systems worked very well. The problem came with domain logic: business rules, validations, calculations and the like. Usually people would write these on the client. But this was awkward and was usually done by embedding the logic directly into the UI screens. As the domain logic got more complex, this code became very difficult to work with. Furthermore embedding logic in screens made it easy to duplicate code, which meant that simple changes resulted in hunting down similar code in many screens. </p>
		<p>An alternative was to put the domain logic in the database as stored procedures. However stored procedures give limited structuring mechanisms, which again led to awkward code. Also many people liked relational databases because SQL was a standard which would allow them to change their database vendor. Despite the fact that relatively few people actually do this, many more like the freedom to do it without too high a porting cost. Stored procedures are all proprietary, so would remove that option. </p>
		<p>At the same time that client-server was gaining popularity, the object-oriented world was rising. The object community had an answer to the problem of domain logic, move to a three layer system. In this approach you have a presentation layer for your UI, a domain layer for your domain logic, and a data source. This way you can move all of that intricate domain logic out of the UI and put into a layer where you can objects to properly work with it. </p>
		<p>Despite this the object bandwagon made little headway. The truth was that many systems were simple, or at least started that way. And although the three layer approach had many benefits the tooling for client-server was compelling if your problem was simple. The client-server tools also were difficult, or even impossible, to use in a three layer configuration. </p>
		<p>I think the seismic shock here was the rise of the web. Suddenly people wanted to deploy client-server applications with a web browser. However if all your business logic was buried in a rich client then all your business logic needed to be redone to have a web interface. A well designed three layer system could just add a new presentation and be done with it. Furthermore, with Java, we saw an object-oriented language hit the mainstream. The tools that appeared to build web pages were much less tied to SQL, and thus more amenable to a third layer. </p>
		<p>When people discuss layering, there's often some confusion over the terms layer and tier. Often the two are used as synonyms. However most people see tier as implying a physical separation. Client-server systems are often described as two-tier systems, and the separation is physical: the client is a desktop and the server is server. I use layer to stress that you don't have to run the layers on different machines. A domain layer would often run on either a desktop or the database server. In this situation you have two nodes but with three distinct layers. With a local database I could run all three layers on a laptop, but it would still be three distinct layers. </p>
		<a name="N53">
		</a>
		<h2>The Three Principal Layers</h2>
		<p>So for this book I'm centering my discussion around a layered architecture of three primary layers: presentation, domain, and data source (I'm following the names of <a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.amazon.com/exec/obidos/ASIN/0201616173?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.amazon.com/exec/obidos/ASIN/0201616173'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.amazon.com/exec/obidos/ASIN/0201616173">[Brown et al]</a>). </p>
		<p>
				<b>Presentation</b> logic is about how to handle the interaction between the user and the software. This can be as simple as a command line or a text based menu system. These days it's more likely to be a rich client graphics UI or an HTML based browser UI. (In this book I use <b>rich client</b> to mean a windows / swing / fat client style UI, as opposed to an HTML browser.) The primary responsibilities of the presentation is to display information to the user and to interpret commands from the user into actions upon the domain and data source. </p>
		<p>
				<b>Data source</b> logic is about communicating with other systems that carry out tasks on behalf of the application. These can be transaction monitors, other applications, messaging systems and so forth. For most enterprise applications the biggest piece of data source logic is a database which is primarily responsible for storing persistent data. </p>
		<p>The remaining piece is the <b>domain logic</b>, also referred to as business logic. This is the work that this application needs to do for the domain you're working with. It involves calculations based on inputs and stored data, validation of any data that comes in from the presentation, and figuring out exactly what data source logic to dispatch depending on commands received from the presentation. </p>
		<p>A single application can often have multiple packages of each of these three subject areas. An application designed to be manipulated by end users through a rich client interface, but also for manipulation though a command line would have two presentations: one for the rich client interface and one for the command line. Multiple data source components may be present for different databases, but particularly for communication with existing packages. Even the domain may be broken into distinct areas which are relatively separate from each other. Certain data source packages may only be used by certain domain packages. </p>
		<p>So far I've talked about a user, this naturally raises the question of what happens when there isn't a human being driving the software. This could be something new and fashionable like a web service, or something mundane and useful like a batch process. In this case the user is the client program. At this point it becomes apparent that there is a lot of similarity between the presentation and data source layers in that they both are about connection to the outside world. This is the logic behind Alistair Cockburns Hexagonal Architecture pattern which visualizes any system as a core surrounded by interfaces to external systems. In the hexagonal architecture everything external is fundamentally an outside interface and thus it's a symmetrical view rather than my asymmetric layering scheme. </p>
		<p>I find the asymmetry useful, however, because I think there is a useful distinction to be made between an interface that you provide as a service to others, and a your use of someone else's service. Driving down to the core, this is the real distinction I make between presentation and data source. Presentation is an external interface for a service your system offers to someone else, whether it be a complex human or a simple remote program. Data source is the interface to things that are providing a service to you. I find it useful to think about these differently because the difference in client is alters the way you think about the service. </p>
		<table cellpadding="3" border="1">
				<tbody>
						<tr>
								<th>Layer</th>
								<th>Responsibilities</th>
						</tr>
						<tr>
								<td>Presentation</td>
								<td>Provision of services, display of information (eg in windows or HTML, handle user request (mouse clicks, keyboard hits, http requests, command line invocations)</td>
						</tr>
						<tr>
								<td>Domain</td>
								<td>The logic that is the real point of the system</td>
						</tr>
						<tr>
								<td>Data source</td>
								<td>Communication with databases, messaging systems, transaction managers, other packages</td>
						</tr>
				</tbody>
		</table>
		<p>One of the hardest parts of working with domain logic seems to be that people often find it difficult to recognize what is domain logic and what is other forms of logic. A good example of this was when I was told about a system where there was a list of products where all the products that sold over 10% more than they did last month was colored in red. To do this they placed logic in the presentation layer that compared this months sales to last month's sales and if the difference was more than 10% they set the color to red. </p>
		<p>The trouble is that's putting domain logic into the presentation. To properly separate the layers you need have a method in the domain layer to indicate if a product has improving sales. This method does the comparison between the two months and returns a boolean value. The presentation layer then simply calls this boolean method, and if true, highlights the product in red. That way the process is broken into its two parts: deciding whether there is something highlightable and choosing how to highlight. </p>
		<p>I'm uneasy with being overly dogmatic about this. When reviewing this book, Alan Knight commented that he was "torn between whether just putting that into the UI is the first step on a slippery slope to hell or a perfectly reasonable thing to do that only a dogmatic purist would object to". The reason we are uneasy is because it's both! </p>
		<a name="N9F">
		</a>
		<h2>When to separate the layers</h2>
		<p>Although we can identify the three common responsibility layers of presentation, domain and data source for each of the three examples, each example has its own questions as to how they are separated. If each responsibility layer is quite complex then it makes sense to break these into their own separate modules. An application with complex logic would have distinct package structures for the presentation, domain, and data source. Indeed it would probably have further mediating layers. But simpler systems might not. If all you are doing is viewing and simple data entry then it may well be reasonable to put all the logic in a series of server pages, particularly if you have a tool that makes it easy to write these server pages. </p>
		<p>The cut over point for separating these responsibilities isn't one that can be easily defined. I can't say that if your domain logic has complexity greater than 7.4 then you should separate it out from the presentation. (Or I guess I can if I leave the calculation of the complexity as an exercise for the reader.) My practice is to almost always separate the presentation from the domain/data source. The only case I'd break that rule is if the complexity of the domain/data source is close to nothing and I have tools that make it easy to tie things together. A classic example of this is the many client-server systems done in such environments as Visual Basic or Powerbuilder. They make it very easy to build rich client interfaces on top of SQL databases. Providing the presentation matches very closely to the database structure, and there's hardly any validation or calculation involved, then I'd go ahead and use it. But as soon as validation or calculation starts getting complicated I would look to separating it out into separate objects. </p>
		<p>I'm inclined to be more tolerant of separating the domain from the data source. For many simpler applications the two can look quite similar, so then I'm more inclined to keep them together. But as soon as the way I organize my business logic starts looking different from the way the data source is defined I'll want to put them into separate modules. </p>
		<p>When describing the layering structure of an application I like to use a UML package diagram to show the packages and their dependencies. I haven't used one here because the actual dependencies depend a great deal on the actual patterns that you are using. One pretty absolute rule is that nothing should depend on the presentation. No module in the domain or data source layers should ever invoke anything on the presentation. This sometimes causes problems when the presentation needs to update on a change in the domain, when this occurs you'll need to use an <a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.amazon.com/exec/obidos/ASIN/0201633612?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.amazon.com/exec/obidos/ASIN/0201633612'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.amazon.com/exec/obidos/ASIN/0201633612">Observer</a> to avoid a dependency into the presentation. </p>
		<p>Other than that we need to go into the patterns in more detail before we can sketch out any dependencies. </p>
		<a name="NB8">
		</a>
		<h2>Choosing where to run your layers</h2>
		<p>For most of this book I'm talking about logical layers: dividing a system into separate pieces to reduce the coupling between different parts of a system. Separation between layers is useful even if they are all running on one physical machine. But there are places where the physical structure of a system makes a difference. </p>
		<p>For most IS applications the decision is whether to run processing on a client, desktop machine, or whether to run processing on a server. </p>
		<p>Often the simplest case is to run everything on servers. Using a HTML front end which uses a web browser is a good way to do this. The great advantage of running everything on the server is that everything is easy to upgrade and fix because it's in a limited amount of places. You don't have to worry about deployment to many desktops and keeping them all in sync with the server. You don't have to worry about compatibilities with other desktop software. </p>
		<p>The general argument in favor of running on a client turns on responsiveness or disconnected operation. Any logic that runs on the server needs a server round trip to respond to anything the user does. If the user wants to quickly fiddle with things and see immediate feedback that round trip gets in the way. It also needs a network connection to run. The network may like to be everywhere, but as I type this it isn't at 31,000 ft. It may be everywhere soon, but there's people who want to do work now and not wait for wireless coverage to reach Dead End Creek. Disconnected operation brings particular challenges, and I'm afraid I decided to put those out of the scope of this book. </p>
		<p>With those general forces in place we can then look at the options layer by layer. </p>
		<p>The data source pretty much always will only run on servers. The exception is where you might duplicate server functionality onto a suitably powerful client. Usually this case appears when you want disconnected operation. In this case changes to the data source on the disconnected client need to be synchronized with the server. As I mentioned earlier, I decided to leave those issues to another day - or another author. </p>
		<p>The decision of where to run the presentation depends mostly on what kind of user interface you want. Running a rich client pretty much means running the presentation on the client. Running a web interface pretty much means running on the sever. There are exceptions, remote operation of client software (such as X servers in the Unix world) running a web server on the desktop, but these exceptions are rare. </p>
		<p>If you're building a B2C system- you have no choice. Any Tom, Dick or Harriet could be connecting to your servers and you don't want to turn anyone away because they insist on doing their online shopping with a TRS-80. In this case you do all processing on the server and offer up HTML for the browser to deal with. Your limitation with the HTML option is that every bit of decision making needs a round trip from the client to the server and that can hurt responsiveness. You reduce some of that with browser scripting and downloadable applets, but they reduce your browser compatibility and tend to add other headaches. The more pure HTML you can go the easier life is. </p>
		<p>That ease of life is appealing even if every one of your desktops is lovingly hand-built by your IS department. Keeping clients up to date and avoiding compatibility errors with other software dogs even simple rich client systems. </p>
		<p>The primary reason that people want a rich client presentation is because some tasks are complicated for users to do, and to have a usable application they'll need more than what a web GUI can give. Increasingly, however, people are getting used to ways to make web front ends more usable and that reduces the need for a rich client presentation. As I write this I'm very much in favor of the web presentation if you can, and the rich client if you must. </p>
		<p>This leaves us with the domain logic. You can run business logic either all on the server, all on the client, or split it. Again all on the server is the easiest way for ease of maintenance. The demand to move it to the client is either for responsiveness or for disconnected use. </p>
		<p>If you have to run some logic on the client you can consider running all of it there - at least that way it's all in one place. Usually this goes hand in hand with a rich client - running a web server on a client machine isn't going to help responsiveness much, although it can be a way to deal with disconnected operation. In this case you can still keep your domain logic in separate modules from the presentation, and indeed you can with either <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/transactionScript.html" webstripperlinkwas="./transactionScript.html"><i>Transaction Script</i></a>s or a <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/domainModel.html" webstripperlinkwas="./domainModel.html"><i>Domain Model</i></a>. The problem with putting all the domain logic on the client is that this way you have more to upgrade and maintain. </p>
		<p>Splitting across the both desktop and server sounds like the worst of both worlds, since that way you don't know really where any piece of logic may be. The main reason to do it is when you only have a small amount of domain logic that needs to run on the client. The trick then is to isolate this piece of logic into a self contained module that isn't dependent on any other part of the system. That way you can run that module on either client or server. Doing this will require a good bit of annoying jiggery-pokery - but it's a good way of doing the job. </p>
		<p>Once you've chosen your processing nodes, you should then try to keep all the code on one node on a single process. Don't try to separate the layers into separate processes unless you absolutely have to, since doing that will both cause performance degradation and add complexity as you have to add things like <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/distributedFacade.html" webstripperlinkwas="./distributedFacade.html"><i>Remote Facade</i></a>s and <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/dataTransferObject.html" webstripperlinkwas="./dataTransferObject.html"><i>Data Transfer Object</i></a>s. </p>
		<a name="NF4">
		</a>
		<h2>More Layering schemes</h2>
		<p>I've picked these three layers because these three subject matters are always present, and I think that the three represent the simplest layering scheme that makes sense for enterprise applications. You can cut the layers differently, and often these other cuts are useful, but I've found these three are always useful to have in your mind while the others are often useful but not always useful. </p>
		<p>I'll illustrate this by looking at some layering schemes that have been promoted in some useful books on IS architecture. First up is what I'll call the Brown model which is discussed in <a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.amazon.com/exec/obidos/ASIN/0201616173?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.amazon.com/exec/obidos/ASIN/0201616173'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.amazon.com/exec/obidos/ASIN/0201616173">[Brown et al]</a>. The Brown model has five layers: Presentation, Controller/Mediator, Domain, Data Mapping, and Data Source. Essentially this model places additional mediating layers between the basic three layers. The controller/mediator mediates between the presentation and domain layers, while the data mapping layer mediates between the domain and data source layers. </p>
		<p>I find that having the mediating layers is something that's useful some of the time, but not all of the time. As a result I've described mediators in terms of patterns. The <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/applicationController.html" webstripperlinkwas="./applicationController.html"><i>Application Controller</i></a> is the mediator between the presentation and domain, and the <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/databaseMapper.html" webstripperlinkwas="./databaseMapper.html"><i>Data Mapper</i></a> is the mediator between the data source and the domain. For organizing this book I've described <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/applicationController.html" webstripperlinkwas="./applicationController.html"><i>Application Controller</i></a> in the presentation section and <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/databaseMapper.html" webstripperlinkwas="./databaseMapper.html"><i>Data Mapper</i></a> in the data source section. </p>
		<table cellpadding="3" border="1">
				<tbody>
						<tr>
								<th>Brown</th>
								<th>Fowler</th>
						</tr>
						<tr>
								<td>Presentation</td>
								<td>Presentation</td>
						</tr>
						<tr>
								<td>Controller/Mediator</td>
								<td>
										<a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/applicationController.html" webstripperlinkwas="./applicationController.html">
												<i>Application Controller</i>
										</a>
								</td>
						</tr>
						<tr>
								<td>Domain</td>
								<td>Domain</td>
						</tr>
						<tr>
								<td>Data Mapping</td>
								<td>
										<a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/databaseMapper.html" webstripperlinkwas="./databaseMapper.html">
												<i>Data Mapper</i>
										</a>
								</td>
						</tr>
						<tr>
								<td>Data Source</td>
								<td>Data source</td>
						</tr>
				</tbody>
		</table>
		<p>So for me the addition of mediating layers, frequently but not always useful, represents an optional extra in the design. My approach is to always think of the three base layers, look to see if any of them are getting too complex, and if so add the mediating layer to separate the functionality. </p>
		<p>Another good layering scheme for J2EE appears in <a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.amazon.com/exec/obidos/ASIN/0130648841?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.amazon.com/exec/obidos/ASIN/0130648841'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.amazon.com/exec/obidos/ASIN/0130648841">CoreJ2EE patterns</a>. Here the layers are client, presentation, business, integration, and resource. Simple correspondences exist for the business and integration layers. They refer to resource layer as those external services that the integration layer is connecting to. The main difference is that they split the presentation layer between the part that runs on the client (client) and the part that runs on a server (presentation). This is often a useful split to do, but again it's not one that's needed all the time. </p>
		<table cellpadding="3" border="1">
				<tbody>
						<tr>
								<th>Core J2EE</th>
								<th>Fowler</th>
						</tr>
						<tr>
								<td>Client</td>
								<td>Presentation that runs on client (eg rich client systems)</td>
						</tr>
						<tr>
								<td>Presentation</td>
								<td>Presentation part that runs on server (eg http handers, server pages</td>
						</tr>
						<tr>
								<td>Business </td>
								<td>Domain</td>
						</tr>
						<tr>
								<td>Integration</td>
								<td>Data source</td>
						</tr>
						<tr>
								<td>Resource</td>
								<td>The external resource that the data source in communication with</td>
						</tr>
				</tbody>
		</table>
		<p>The <a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.amazon.com/exec/obidos/ASIN/0735605238?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.amazon.com/exec/obidos/ASIN/0735605238'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.amazon.com/exec/obidos/ASIN/0735605238">Microsoft DNA </a>architecture defines three layers: presentation, business, and data access. They correspond pretty directly to the three layers I use here. The biggest shift occurs in the way that data is passed up from the data access layers. In Microsoft DNA all the layers operate on record sets that are the results of SQL queries issued by the Data Access layer. This introduces an apparent coupling in that both the business and presentation layers know about the database. </p>
		<p>The way I look at this is that in DNA the record set acts as <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/dataTransferObject.html" webstripperlinkwas="./dataTransferObject.html"><i>Data Transfer Object</i></a> between layers. The business layer can modify the record set on its way up to the presentation, or even create one itself (although that is rarer). Although this form of communication is in many ways unwieldy it has the big advantage of allowing the presentation to use data-aware GUI controls, even on data that's been modified by the business layer. </p>
		<table cellpadding="3" border="1">
				<tbody>
						<tr>
								<th>Microsoft DNA</th>
								<th>Fowler</th>
						</tr>
						<tr>
								<td>Presentation</td>
								<td>Presentation</td>
						</tr>
						<tr>
								<td>Business </td>
								<td>Domain</td>
						</tr>
						<tr>
								<td>Data Access</td>
								<td>Data source</td>
						</tr>
				</tbody>
		</table>
		<p>In this case the domain layer is structured in the form of <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/tableModule.html" webstripperlinkwas="./TableModule.html"><i>Table Module</i></a>s and the data source layer uses <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/tableDataGateway.html" webstripperlinkwas="./tableDataGateway.html"><i>Table Data Gateway</i></a>s. </p>
		<hr />
		<table width="100%">
				<tbody>
						<tr>
								<td align="left">
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.martinfowler.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.martinfowler.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.martinfowler.com">
												<img height="51" src="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/mflogo.gif" width="147" border="0" webstripperlinkwas="mflogo.gif" />
										</a>
								</td>
								<td align="right">
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.thoughtWorks.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.thoughtWorks.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.thoughtWorks.com">
												<img src="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/twlogo.gif" border="0" webstripperlinkwas="twlogo.gif" />
										</a>
								</td>
						</tr>
				</tbody>
		</table>
		<hr />
© <small>Copyright <a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.martinfowler.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.martinfowler.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.martinfowler.com">Martin Fowler</a>, all rights reserved</small><img src ="http://www.cnitblog.com/scofield/aggbug/12611.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/scofield/" target="_blank">scofield</a> 2006-06-21 10:41 <a href="http://www.cnitblog.com/scofield/archive/2006/06/21/12611.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UML</title><link>http://www.cnitblog.com/scofield/archive/2006/06/21/12608.html</link><dc:creator>scofield</dc:creator><author>scofield</author><pubDate>Wed, 21 Jun 2006 02:38:00 GMT</pubDate><guid>http://www.cnitblog.com/scofield/archive/2006/06/21/12608.html</guid><wfw:comment>http://www.cnitblog.com/scofield/comments/12608.html</wfw:comment><comments>http://www.cnitblog.com/scofield/archive/2006/06/21/12608.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/scofield/comments/commentRss/12608.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/scofield/services/trackbacks/12608.html</trackback:ping><description><![CDATA[
		<br />
		<table cellpadding="5" align="center">
				<tbody>
						<tr>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.martinfowler.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.martinfowler.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.martinfowler.com">
												<img height="32" src="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/mflogo.gif" width="92" align="left" border="0" webstripperlinkwas="mflogo.gif" />
										</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com">Home</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com/articles.html?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com/articles.html'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com/articles.html">Articles</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com/talks.html?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com/talks.html'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com/talks.html">Talks</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com/links.html?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com/links.html'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com/links.html">Links</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://martinfowler.com/index.html?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://martinfowler.com/index.html'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://martinfowler.com/index.html">Contact Me</a>
								</td>
								<td>
										<a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/index.html" webstripperlinkwas="http://martinfowler.com/isa/index.html">ISA</a>
								</td>
								<td>
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.thoughtworks.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.thoughtworks.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.thoughtworks.com">ThoughtWorks</a>
								</td>
						</tr>
				</tbody>
		</table>
		<hr />
		<h1 align="center">Preface</h1>
		<hr />
		<p>In the spring of 1999 I flew to Chicago to consult on a project being done by ThoughtWorks, a small but rapidly growing application development company. The project was one of those ambitious enterprise application projects: a back-end leasing system. Essentially what this system does is to deal with everything that happens to a lease after you've signed on the dotted line. It has to deal with sending out bills, handling someone upgrading one of the assets on the lease, chasing people who don't pay their bills on time, and figuring out what happens when someone returns the assets early. That doesn't sound too bad until you realize that leasing agreements are infinitely varied and horrendously complicated. The business "logic" rarely fits any logical pattern, because after all its written by business people to capture business, where odd small variations can make all the difference in winning a deal. Each of those little victories is yet more complexity to the system. </p>
		<p>That's the kind of thing that gets me excited. How to take all that complexity and come up with system of objects that can make more tractable. Developing a good <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/domainModel.html" webstripperlinkwas="./domainModel.html"><i>Domain Model</i></a> for this is difficult, but wonderfully satisfying. </p>
		<p>Yet that's not the end of the problem. Such a domain model has to persisted to a database, and like many projects we were using a relational database. We also had to connect this model to a user interface, provide support to allow remote applications to use our software, and integrate our software with third party packages. All of this on a new technology called J2EE which nobody in the world had any real experience in using. </p>
		<p>Even though this technology was new, we did have the benefit of experience. I'd been doing this kind of thing for ages now with C++, Smalltalk, and CORBA. Many of the ThoughtWorkers had a lot of experience with Forte. We already had the key architectural ideas in our heads, we just had to figure out how to apply them to J2EE. Looking back on it three years later the design is not perfect, but it's stood the test of time pretty damn well. </p>
		<p>That's the kind of situation that is where this book comes in. Over the years I've seen many enterprise application projects. These projects often contain similar design ideas which have proven to be effective ways to deal with the inevitable complexity that enterprise applications possess. This book is a starting point to capture these design ideas as patterns. </p>
		<p>The book is organized in two parts. The first part is a set of narrative chapters on a number of important topics in the design of enterprise applications. They introduce various problems in the architecture of enterprise applications and their solutions. However the narrative chapters don't go into much detail on these solutions. The details of the solutions are in the second part, organized as patterns. These patterns are a reference and I don't expect you to read them cover to cover. My intention is that you can read the narrative chapters in part one from start to finish to get a broad picture of what the book covers, then you can dip into the patterns chapters of part two as your interest and needs drive you. So this book is a short narrative book and a longer reference book combined into one. </p>
		<p>This is a book on enterprise application design. Enterprise applications are about the display, manipulation and storage of large amounts of often complex data. Examples include reservation systems, financial systems, supply chain systems, and many of the systems that run modern business. Enterprise applications have their own particular challenges and solutions. They are a different animal to embedded systems, control systems, telecoms, or desktop productivity software. So if you work in of these other fields, there's nothing really in this book for you (unless you want to get a feel for what enterprise applications are like.) For a general book on software architecture I'd recommend <a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.amazon.com/exec/obidos/ASIN/0471958697?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.amazon.com/exec/obidos/ASIN/0471958697'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.amazon.com/exec/obidos/ASIN/0471958697">[POSA]</a>. </p>
		<p>There are many architectural issues in building enterprise applications. I'm afraid this book isn't a comprehensive guide to them. In building software I'm a great believer in iterative development. At the heart of iterative development is the notion that you should deliver software as soon as you have something useful to the user, even if it's not complete. Although there are many differences between writing a book and writing software, this notion is one that I think the two share. So this book is an incomplete but (I trust) useful compendium of advice on enterprise application architecture. The primary topics I talk about are: </p>
		<ul>
				<li>layering of enterprise applications 
</li>
				<li>how to structure domain (business) logic 
</li>
				<li>the structure of a web user interface 
</li>
				<li>how to link in-memory modules (particularly objects) to a relational database 
</li>
				<li>how to handle session state in stateless environments 
</li>
				<li>some principles of distribution </li>
		</ul>
		<p>The list of things I don't talk about is rather longer. I really fancied writing about organizing validation, incorporating messaging and asynchronous communication, security, error handling, clustering, architectural refactoring, structuring rich-client user interfaces, amongst others. I can only hope to see some patterns appear for this work in the near future. However due to space, time, and lack of cogitation you won't find them in this book. Perhaps I'll do a second volume someday and get into these topics, or maybe someone else will fill these, and other, gaps. </p>
		<p>Of these dealing with message based communication is a particularly big issue. Increasingly people who are integrating multiple applications are making use of asynchronous message based communication approaches. There's much to said for using them within an application as well. </p>
		<p>This book is not intended to be specific for any particular software platform. I first came across these patterns while working with Smalltalk, C++, and CORBA in the late 80's and early 90's. In the late 90's I started to do extensive work in Java and found these patterns applied well both to early Java/CORBA systems and later J2EE based work. More recently I've been doing some initial work with Microsoft's .NET platform and find the patterns apply again. My ThoughtWorks colleagues have also introduced their experiences, particularly with Forte. I can't claim generality across all platforms that ever have been or will be used for enterprise applications, but so far these patterns have shown enough recurrence to be useful. </p>
		<p>I have provided code examples for most of these patterns. My choice of language for the code examples is based on what I think most readers are likely to be able to read and understand. Java's a good choice here. Anyone who can read C or C++ can read Java, yet Java is much less complex than C++. Essentially most C++ programmers can read Java but not vice versa. I'm an object bigot, so inevitably lean to an OO language. As a result most of the code examples are in Java. As I was working on the book Microsoft started stabilizing their .NET environment, and their C# language has most of the same properties as Java for an author. So I did some of the code examples in C# as well, although that does introduce some risk since developers don't have much experience with .NET yet and so the idioms for using it well are less mature. Both are C-based languages so if you can read one you should be able to read both, even if you aren't deeply into that language or platform. My aim was to use a language that the largest amount of software developers can read, even if it's not their primary or preferred language. (My apologies to those who like Smalltalk, Delphi, Visual Basic, Perl, Python, Ruby, COBOL or any other language. I know you think you know a better language than Java or C#, all I can say is I do too!) </p>
		<a name="N43">
		</a>
		<h2>Who this book is for</h2>
		<p>I've written this book for programmers, designers, and architects who are building enterprise applications and who want to either improve their understanding of these architectural issues or improve their communication about them. </p>
		<p>I'm assuming that most of my readers will fall into two groups: either those with modest needs who are looking to build their own software to handle these issues, or readers with more demanding needs who will be using a tool. For those of modest needs, my intention is that these patters should get you started. In many areas you'll need more than the patterns will give you, but my intention is to provide more of a head start in this field than I got. For tool users I hope this book will be useful to give you some idea of what's happening under the hood, but also help you in making choices between which of the tool supported patterns to use. Using, say, an object-relational mapping tool still means you have to make decisions about how to map certain situations. Reading the patterns should give you some guidance in making the choices. </p>
		<p>There is a third category, those with demanding needs who want to build their own software for these problems. The first thing I'd say here is look carefully at using tools. I've seen more than one project get sucked into a long exercise at building frameworks which weren't what project was really about. If you're still convinced, go ahead. Remember in this case that many of the code examples in this book are deliberately simplified to help understanding, and you'll find you'll need to do a lot tweaking to handle the greater demands that you'll face. </p>
		<p>Since patterns are common solutions to recurring problems, there's a good chance that you'll have already come across some of them. If you've been working in enterprise applications for a while, you may well know most of them. I'm not claiming to have anything new in this book. Indeed I claim the opposite - this is a book of (for our industry) old ideas. If you're new to this field I hope you'll like this book to help you learn about these techniques. If you're more familiar with the techniques I hope you'll like this book because it helps you communicate and teach these ideas to others. An important part of patterns is trying to build a common vocabulary, so you can say that this class is a <a href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/distributedFacade.html" webstripperlinkwas="./distributedFacade.html"><i>Remote Facade</i></a> and other designers know what you mean. </p>
		<a name="N58">
		</a>
		<h2>Acknowledgements</h2>
		<p>As with any book, what's written here has a great deal to do with the many people that have worked with me in various ways over the years. Lots of people have helped me in lots of ways. Often I don't recall important things that people have said that go into this book. But in this section I can acknowledge those contributions I do remember. </p>
		<p>I'll start with my contributors: David Rice and Matt Foemmel, colleagues of mine at ThoughtWorks. Not just have they given me many good ideas that are key to this book, they have also contributed some chapters and examples to the content. Their direct and recent project experience adds a great deal to those sections. </p>
		<p>I could almost list the ThoughtWorks telephone directory here, for so many of my colleagues have helped this project by talking over their designs and experiences with me. Many patterns formed in my mind by having the opportunity to the many talented designers that we have, so I have little choice but to thank the whole company. </p>
		<p>Kyle Brown, Rachel Reinitz, and Bobby Woolf have gone out of their way to have long and detailed review sessions with me in North Carolina. Their fine tooth-comb has injected all sorts of wisdom, not including this particularly heinous mixed metaphor. In particular I've enjoyed several long telephone calls with Kyle that contributed more than I can list. </p>
		<p>As usual I owe more than I can say to my first class panel of official reviewers: </p>
		<ul>
				<li>John Brewer 
</li>
				<li>John Crupi 
</li>
				<li>Alan Knight 
</li>
				<li>Rob Mee 
</li>
				<li>Gerard Meszarios 
</li>
				<li>David Siegel 
</li>
				<li>Kai Yu </li>
		</ul>
		<p>Early in 2000 I prepared a talk for Java One with Alan Knight and Kai Yu which was the earliest genesis of this material. As well as thanking them for their help in that, I should also thank Josh Mackenzie, Rebecca Parsons, and Dave Rice for their help refining these talks, and the ideas, later on. Jim Newkirk did a great deal in helping me get used to the new world of .NET. </p>
		<p>As I was writing this book, I put drafts on the web. During this time many people sent me emails pointing out problems, asking questions, or talking about alternatives. These people include (in rough order of hearing from them) Ivan Mitrovic, Bjorn Beskow, Daniel Drasin, Eric Kaun, Thomas Neumann, Peter Gassmann, Russell Freeman, Brad Wiemerslage, John Coakley, Mark Bernstein, Chester Chen, Christian Heller, Jesper Ladegaard, Kyle Hermenean, Don Dwiggins, Knut Wannheden, Akira Hirasawa, Volker Termath, Christopher Thames, Bryan Boreham, Michael Yoon, Tobin Harris, Kirk Knoernschild, Matthew Roberts, Joel Rieder, Jeremy Miller, Russel Healey, Pascal Costanza, Paul Campbell, Bob Corrick, Graham Berrisford, Trevor Pinkney, Peris Brodsky, Volker Turau, Dan Green, Ken Rosha, Michael Banks, Paolo Marino, Jason Gorman. </p>
		<p>There are many others who've given input whose names I either never knew or can't remember, but my thanks is no less heartfelt. </p>
		<p>An important part of my sanity for the code examples in this book was being able to easily run automated tests. Although I'm sure there are errors which got past them, they certainly caught plenty. So I'd like to thank Kent Beck and Erich Gamma for writing JUnit (junit.org), and Philip Craig for his work on NUnit (nunit.sourceforge.net). </p>
		<a name="N8E">
		</a>
		<h2>Colophon</h2>
		<p>This was the first book that I've written using XML and related technologies. The master text was written as XML documents using trusty TextPad. I used a home grown DTD. While I was working I used XSLT to generate the web pages for the web site. For the diagrams I relied on my old friend Visio using Pavel Hruby's wonderful UML templates (much better than those that come with the tool, I have a link on my web site if you want them.) I wrote a small program that automatically imported the code examples into the output, this saved me from the usual nightmare of code cut and paste. For my first draft I tried XSL-FO with Apache FOP. It wasn't quite up to the job (yet) so for later work I wrote scripts in XSLT and Ruby to import the text into FrameMaker. </p>
		<hr />
		<table width="100%">
				<tbody>
						<tr>
								<td align="left">
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.martinfowler.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.martinfowler.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.martinfowler.com">
												<img height="51" src="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/mflogo.gif" width="147" border="0" webstripperlinkwas="mflogo.gif" />
										</a>
								</td>
								<td align="right">
										<a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.thoughtWorks.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.thoughtWorks.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.thoughtWorks.com">
												<img src="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/twlogo.gif" border="0" webstripperlinkwas="twlogo.gif" />
										</a>
								</td>
						</tr>
				</tbody>
		</table>
		<hr />
© <small>Copyright <a onclick="if(confirm('WebStripper Dial Protection\n\nAre you sure you want to open the page http://www.martinfowler.com?\n\n(To alter dial protection settings open Options|options in WebStripper)')) {href='http://www.martinfowler.com'}else{href='javascript:void(0)'}" href="mk:@MSITStore:C:\UserData\Library\EBook\EnterpriseArchPattern.chm::/Dial%20Protected" webstripperwas="http://www.martinfowler.com">Martin Fowler</a>, all rights reserved</small><img src ="http://www.cnitblog.com/scofield/aggbug/12608.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/scofield/" target="_blank">scofield</a> 2006-06-21 10:38 <a href="http://www.cnitblog.com/scofield/archive/2006/06/21/12608.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>