Home Articles Talks Links Contact Me ISA ThoughtWorks

Putting it all Together


So far these narratives have looked at one aspect of a system and explored the various options that you can use in handling these issues. Now it's time to sweep all of these issues together and start to answer the tricky question of what patterns you should use together when designing an enterprise application.

The advice in this chapter is in many ways a repeat of the advice given in the earlier narrative chapters. I must admit I've had prevarications on whether this chapter was needed, but I felt it was good to put all the discussion in context, now that I hope you have at least an outline of the full scope of the patterns in this book.

As I write this, I'm very conscious of the limitations of my advice. Frodo said of the elves "go not to the Elves for counsel, for they will say both no and yes". While I'm not claiming any immortal knowledge I certainly understand their answer that advice is often a dangerous gift. If you're reading this to make architectural decisions for your project, you know far more about the issues of your project than I do. One of the biggest frustrations in being a pundit is that people will often come up to me at a conference, or in an email message, and ask for advice on their architectural or process decisions. There's no way you can give particular advice on the basis of a five minute description. I write this chapter with even less knowledge of your predicament.

So read this chapter in the spirit that it's given. I don't know all the answers, and I certainly don't know your questions. Use this advice to prod your thinking, but don't use it as a replacement for your thinking. In the end you have to make, and live with, the decisions yourself.

But one good thing is that your decisions are not cast forever in stone. Architectural refactoring is hard, and we are still ignorant of its full costs. But it isn't impossible. Here the best advice I can give is that even if you dislike the full story of extreme programming, you should still consider seriously three technical practices: continuous integration, test driven development, and refactoring. These will not be a panacea, but they will make it much easier to change your mind when you discover you need to. And you will need to, unless you are either more fortunate, or more skillful, than anyone I've met to date.

Starting With the Domain Layer

The start of the process is deciding which domain logic approach to go with. The three main contenders are: Transaction Script, Table Module, and Domain Model.

As I've indicated in that chapter, the strongest force that drives you through this trio is the complexity of the domain logic, and this is something that is currently impossible to quantify, or even qualify with any degree of reasonable precision. But other factors also play in the decision, in particular the difficulty of the connection with a database.

The simplest of the three is Transaction Script. It fits with the procedural model that most people are still most comfortable with. It nicely encapsulates the logic of each system transaction into a comprehensible script. It's easy to build on top of a relational database. It's great failing is the fact that it doesn't deal well with complex business logic, being particularly susceptible to duplicate code. If you have a simple catalog application with little more than a shopping cart running off a simple pricing structure, then Transaction Script will fill the bill perfectly, but as your logic gets more complicated then your difficulties multiply exponentially.

At the other end of the scale is the Domain Model. Hard core object bigots like myself will have an application no other way. After all if an application is simple enough to write with Transaction Scripts, why should our immense intellects bother with such an unworthy problem? And my experiences lead me to have no doubt that with really complex domain logic, there's nothing can handle this hell better than a rich Domain Model. Once you get used to working with a Domain Model even simple problems can be tackled with ease.

Yet the Domain Model carries its faults. High on the list is the difficulty of learning them. Object bigots often look down their noses at people who just don't get objects. But the consequence is that a Domain Model requires skill if it's to be done well. Done poorly it's quite a disaster. The second big difficulty of a Domain Model is the connection to a relational database. Of course a real object zealot finesses this problem with the subtle flick of an object database. But for many, mostly non-technical, reasons an object database isn't a possible choice for enterprise applications. The result is the messy connection to a relational database. Let's face it, object models and relational models don't fit well together. The complexity of many of the OR mapping patterns I describe is the result.

Table Module represents an attractive middle ground between these poles. It can handle domain logic better than Transaction Scripts, and while it can't touch a real Domain Model on handling complex domain logic it fits in really well with a relational database - and many other things too. If you have an environment, such as .NET, where many tools orbit around the all-seeing Record Set then Table Module works very nicely. It plays to the strengths of the relational database and yet represents a reasonable factoring of the domain logic.

In this argument we see that the tools you have also affect your architecture. Sometimes you're able to choose the tools based on the architecture - and in theory that's the way you should go. But in practice you often have to fit your architecture to match your tools. Of the three Table Module is the one whose star rises most when you have tools that match it. It's a particularly strong choice for .NET environments, since so much of the platform is geared around Record Set.

If you've read the discussion in the domain logic chapter, much of this will seem familiar. Yet I think it was worth repeating here because I really do think this is the central decision. From here we go downwards to the database layer, but now the decisions are shaped by the context of your domain layer choice.

Down to the Data Source

Once you've chose your domain layer, you now have to figure out how to connect it to your data sources. Your decisions are based on your domain layer choice, so I'll tackle this in separate sections, driven by the your choice of domain layer.

Transaction Script

The simplest Transaction Scripts contain their own database logic, but I prefer to avoid doing that even in the simplest cases. Separating the database out separates two parts that make sense separate, so even in the simplest applications I make the separation. The database patterns to choose from here are Row Data Gateway and Table Data Gateway.

The choice between them depends much on the facilities of your implementation platform, and on where you expect the application to go in the future. Using a Row Data Gateway gives you a nicely explicit interface for dealing with the data, as each record is read into an object with a clear and explicit interface. With Table Data Gateway you may have less code to write, since you don't need all the accessor code to get at the data, but you end up with a much more implicit interface that relies on accessing a record set structure that's little more than a glorified map.

The key decision, however, lies in the rest of your platform. If you have a platform that provides a lot of tools that work well with Record Set, particularly UI tools or transactional disconnected record sets, then that would tilt me decisively in the direction of a Table Data Gateway

You usually don't need any of the other OR mapping patterns in this context. All the structural mapping issues are pretty much absent since the in-memory structure maps to the database structure so well. You might consider a Unit of Work, but usually it's easy to keep track of what's changed in the script. You don't need to worry about most concurrency issues because the script often corresponds almost exactly to a system transaction, so you can just wrap the whole script in a single transaction. The common exception is where one request pulls data back for editing and the next request tries to save the changes. In this case Optimistic Offline Lock is almost always the best choice. Not just is it easier to implement, it also usually fits users expectations and avoids the problem of a hanging session leaving all sorts of things locked.

Table Module

The main reason to choose Table Module is the presence of a good Record Set framework. In this case you'll want a database mapping pattern that works well with Record Sets, so that leads you inexorably towards Table Data Gateway. These two patterns fit naturally together as if they were made in heaven for each other.

There's not really anything else you need to add on the data source side with this pattern. In the best cases the Record Set has some kind of concurrency control mechanism built in, which effectively turns it into a Unit of Work, which further reduces hair loss.

Domain Model

Now things get interesting. In many ways the big weakness of Domain Model is the fact that connection to the database is complicated. The degree of complication depends upon the complexity of the Domain Model.

If your Domain Model is fairly simple, say a couple of dozen classes which are pretty close to the database, then an Active Record makes sense. If you want to decouple things a bit you can use either Table Data Gateway or Row Data Gateway to do that. Whether you separate or not is not a huge deal either way.

As things get more complicated, then you'll need to consider Data Mapper. Data Mapper is really the approach that delivers on the promise of keeping your Domain Model as independent as possible of all the other layers. But Data Mapper is also the most complicated one to implement. Unless you either have a strong team, or you can find some simplifications that make the mapping easier to do, I'd strongly suggest getting a tool to handle the mapping.

Once you choose Data Mapper then most of the patterns in the OR mapping section come into play. In particular I heartily recommend using Unit of Work, which does a great deal to act as a focal point for concurrency control.

Up to the Presentation

In many ways the presentation is relatively independent of the choice of the lower layers. Your first question is whether to provide a rich client interface or a HTML browser interface. A rich client will give you a nicer UI, but you then have to have a certain amount of control and deployment of your clients. My preference is to pick an HTML browser if you can get away with it, and a rich client if that's not possible. A rich client will usually be more effort to program, but that's usually because rich clients tend to be more sophisticated, not so much the inherent complexities of the technology.

I haven't explored any rich client patterns in this book, so if you choose rich client I don't really have anything further to say.

If you go the HTML route then you have to decide how to structure your application. I certainly recommend the Model View Controller as the underpinning for your design. That done, you are left with two decisions, one for the controller and one for the view.

Your tooling may well make your choice for you. If you use Visual Studio, then the easiest way to go is Page Controller and Template View. If you use Java, then you have a choice of web frameworks to consider. A popular choice at the moment is Struts, that will lead you to a Front Controller and Template View.

Given a freer choice, I'd recommend Page Controller if your site is more document-oriented, particularly if you have a mix of static and dynamic pages. More complex navigation and UI leads you towards a Front Controller

On the view front, the choice between Template View and Transform View really depends on whether your team prefers to use server pages or XSLT in your programming. Template Views have the edge at the moment, although I rather like the added testability of Transform View. If you have the need to display a common site with multiple look and feels you should consider Two Step View

How you communicate with the lower layers depends on what kind of layers they are, and whether they are always going to be in the same process. My preference is to have everything all run in one process if you can, that way you don't have to worry about slow inter-process calls. If you can't do that then you should wrap your domain layer with Remote Facade and use Data Transfer Object to communicate to the web server. There's also an argument for using Remote Facades within a single process if you have a Domain Model as the Remote Facade can help hide the complexities of the Domain Model from view. That choice really depends on how comfortable you are working with the Domain Model directly, adding a Remote Facade can add a lot of extra coding.

Some Technology Specific Advice

In most of this book I'm trying to bring out the common experience of doing projects in many different platforms. Experience with Forte, CORBA, and Smalltalk translates very effectively into developing with Java and .NET. The only reason I've concentrated on Java and .NET environments in this book is because they look like they will be the most common platforms for enterprise application development in the future. (Although I'd like to see the dynamically typed scripting languages, in particular Python and Ruby, give them a run for their money.)

So in this section I want to apply the above advice to these two platforms. As soon as I do this I'm danger of dating my advice. Technologies change much more rapidly than these patterns. So as you read this, remember I'm writing this in early 2002 when everyone is saying that economic recovery is just around the corner.

Java and J2EE

As I write this the big debate in the Java world is exactly how valuable is Enterprise Java Beans? The EJB spec has finally appeared and tools are coming out. But you don't need EJB to build a good J2EE application, despite what EJB vendors tell you. You can do a great deal with POJOs (Plain Old Java Objects) and JDBC

The design alternatives for J2EE vary on what kind of patterns you are using, and again they break out driven by the domain logic.

If you use Transaction Script on top of some form of Gateway, then with EJB the common approach at the moment is to use session beans as Transaction Script and entity beans as Row Data Gateway. This is a pretty reasonable architecture if your domain logic is sufficiently modest to only need Transaction Scripts. The problem with this strongly beany approach is that it's hard to get rid of the EJB server if you find you don't need it and you don't want to cough up the license fees. The non EJB approach is a POJO for the Transaction Script on top of either a Row Data Gateway or a Table Data Gateway. If JDBC 2.0 row sets get more accepted, then that's a reason to use them as Record Sets and that leads to a Table Data Gateway. If you're not sure about EJB you can use the non EJB approach and wrap them with session beans acting as Remote Facades.

If you are using a Domain Model then the current orthodoxy is to use entity beans as your Domain Model. If your Domain Model is pretty simple and matches your database well, then that makes reasonable sense and your entity beans will then be Active Records. It's still good practice to wrap you entity beans with session beans acting as Remote Facades. However if you Domain Model is more complex, then I would want the Domain Model to be entirely independent of the EJB structure, so that you can write, run, and test your domain logic without having to deal with the vagaries of the EJB container. In that model I would use POJOs for the Domain Model and wrap them with session beans acting as Remote Facades. If you choose not to use EJB I would run the whole app in the web server and avoid any remote calls between presentation and domain. If you're using POJO Domain Model I'd also use POJOs for the Data Mappers - either using an OR mapping tool or rolling something myself if I felt really up to it.

At the moment, the Table Module isn't terribly common in the Java world. It'll be interesting to see if more tooling surrounds the JDBC row set - if it does it could become a viable approach. In this case the POJO approach fits best, although you can also wrap the Table Module with session beans acting as Remote Facades and returning Record Sets.

.NET

Looking at .NET, Visual Studio, and the history of application development in the Microsoft world, the dominant pattern in Table Module. Although object bigots tend to dismiss this as just meaning that Microsofties don't get objects, the Table Module does present a valuable compromise between Transaction Script and Domain Model, with an impressive set of tools that take advantage of the ubiquitous data set acting as a Record Set

As a result the Table Module has to be the default choice for this platform. Indeed I see no point at all in using Transaction Scripts except in the very simplest of cases, and even then they should act on and return data sets.

This doesn't mean that you can't use Domain Model, indeed you can build a Domain Model just as easily in .NET as you can in any other OO environment. But the tools don't give you the extra help that they do for Table Modules, so I'd tolerate more complexity before I felt the need to shift to a Domain Model.

The current hype in .NET is all about web services, but I would not use web services inside an application, I'd use it, as in Java, as a presentation to allow applications to integrate. There's no real reason to separate the web server and domain logic into separate processes in a .NET application, so Remote Facade is less useful within the application.

Web Services

As I write this, the general consensus amongst pundits is that web services will make reuse a reality and drive system integrators out of business. I'm not holding my breath for either of those. Within these patterns web services don't play a huge role because they are about application integration rather how you build a simple application. You shouldn't try to break a single application up into web services talking to each other unless you really need to. Rather build your application and expose various parts of it as web services treating the web services as Remote Facades. Above all don't let all the buzz about how easy it is to build web services let you forget about the First Rule of Distributed Object Design.



© Copyright Martin Fowler, all rights reserved