The Business Logic Wars

In an enterprise application, there are lots of choices about where to put business logic–validation rules, value calculation, and so on. You can put it in the database, in the server-side web application (and there, you have a decision about whether to put the code in the model or the controller), or as Javascript running on the client. Where should you choose to put your application’s business logic?

That’s a great question to ask in a bar full of developers of mixed backgrounds, if you like bar fights. SQL programmers will say you should put your business logic in database triggers, where it’s most secure and robust. Heavy-duty Javascript types will say that, at the least, business logic needs to be redundantly implemented in Javascript, to improve client interaction and reduce server round trips. And people from the Java EE community will tell you that the database is for data and that RIAs give you performance almost as good as Javascript without the security concerns, and that you should take the compromise (and the best-structured, most extensible option) and put the business logic into the middle tier.

I have my own very strong opinion on where to put business logic, but it isn’t any of the above. It’s this: It depends. Depends on your organization, your application, and even, sometimes, the particular business rule. Yes, it can be hard to maintain business rules scattered throughout various application tiers, but it’s worth it, because each tier has its own strengths and weaknesses.

Business Logic in the Database

Take the EIS tier, AKA the database. The DB developers are right–this is the absolute most secure and robust layer at which to put your business logic. If true disaster would result if a rule were broken even once–even by a developer with a SQL prompt who had spent a bit too much time at the aforementioned bar the night before–this is really your only option. And it’s the only option that’s really and truly 100% guaranteed to be enforced in every single application that changes data, with no additional work on the developer’s end.

There’s another very good reason to put some business rules in the database: They use a *lot* of data. I’m not talking about a rule requiring a SQL query that returns 100 rows, even if it’s from a million-row table. I’m talking about a rule that, say, will perform updates on each of a million (or even 100,000) rows. You do not want to instantiate one million entity beans (or, for you ADF-ers out there, one million entity object instances) and make changes through them. You want to have the database handle that; crunching huge amounts of data is what the database is for.

On the other hand, if none of these factors is critical to you, putting business logic in the database has some real disadvantages. First, there’s the issue of interactivity and JDBC hits. It’s not that a single JDBC hit is that expensive, but they do tend to add up, especially when you need one every single time you want to give feedback on user input, which in a rich UI situation is really quite often. Of course, if you have the sort of traditional form where an attempt is made to post data to the DB every time the user submits, this isn’t nearly as much of an issue.

Secondly, if a user violates a validation rule that’s being enforced by a DB trigger, what the application sees is a SQLException. And the message in a SQLException is not fit for public consumption. For one thing, it generally refers to database objects and columns. This is not information that will be useful to your customers, and it is the sort of information that might be useful to individuals of technical skill and questionable character. For another, even if a specific attribute caused the problem, a SQLException is unlikely to contain the sort of information that you could use to create, say, a FacesMessage bound to a particular control–so no nice link in the message box, and no nice red X beside the particular value that the user needs to fix.

There are ways to get around that–in fact, I’ll talk about them in next week’s entry–but they’re annoying; they require a fair bit of parsing and plumbing. Business logic placed in the middle tier allows you to more easily create exceptions that integrate well with JSF messages. In fact, if you’re using validation rules in ADF BC or the ADF Model, the framework will manage this process by itself.

Business Logic in the Middle Tier

Which brings us to the middle tier, or, as Jave EE developers call it, “the application.” That’s really relevant: If you or most of your team consists of ace Java developers, who might know basic SQL but aren’t database experts, your productivity, and the quality of your applications, is going to plummet if you try to pack most of your business logic into the DB. Conversely, if you’re on a team of database wizards with one good Java developer, things will be a lot easier for you if you set that one developer doing error message integration and have everybody else writing triggers. That’s an important consideration, but it isn’t the only one.

As said above, working with large reams of data in the middle tier isn’t a very good idea. And no code you deploy on your application server is going to keep a single mistake at a SQL prompt from corrupting your data. Moreover, reuse of business logic between applications isn’t automatic if the logic is enforced by the application, rather than the database. You’ll have to make sure you structure business logic in such a way that it’s easy to reuse between applications, and then you have to make sure each application does reuse it.

That means keeping your code properly factored, moving reusable code up in the class hierarchy or out into object members. For people using ADF BC in particular, this means:

  • Making liberal use of the “Business Components Base Classes” page of JDeveloper’s Project Settings dialog, and the “Class Extends” button on the Java page of most business component editors, so that you can factor common code into custom framework classes.
  • Maintaining a single project of frequently used entity object definitions and associations, and importing those entity object definitions and associations into other projects, rather than recreating them. To be honest, this is kind of an annoying process now; the Business Components Library functionality in 11g will help matters here immensely.
  • Creating, sharing, and using validation domains where appropriate.
  • Creating, sharing, and using custom validation rules where appropriate.

All of these options are mentioned, but sort of pushed to the side or into “Advanced Topics” in the ADF documentation and developer’s guide, and to tell the truth, I’ve given them pretty short shrift in my earlier books (I’m hoping that Oracle JDeveloper Fusion Development – A Handbook for 4GL Developers will fix this problem). Don’t let that treatment fool you; these are topics that, at the very least, a lead developer on any sizable project that use(s) ADF BC for business rules enforcement should be familiar with.

And finally, business logic in the middle tier is just plain not going to be as responsive as business logic implemented in Javascript, because it requires a server round trip. Yes, yes; blah blah AJAX blah. Asynchronous or not, a server round trip is still a server round trip, and no matter how small the messages are, you still have to contend with connection latency. They’re good to avoid when it’s practicable. Moreover, if you use ADF, you might want to take a look at what I said here; I don’t think that even the remarkable rich components in 11g are as AJAX-y as their billing would imply. And the PPR functionality of the current production release, while great for the occasional use, is nowhere near as fast or responsive enough over a typical user’s connection (unless your users are all on a LAN) to use all the time.

Business Logic in Javascript

So, Javascript, the ultimate in user responsiveness. Let’s start out by making sure we’re all on the same page about one thing: Javascript-enforced rules can be bypassed by anyone with minimal Javascript knowledge who has 10 minutes and the will to do so. Implementing rules that are for anything more than the convenience of the user (such as rules validating zip codes for shipping addresses; if you really want your package to not get delivered so badly that you’ll indulge in a bit of Javascript-cracking to ensure it, that’s your problem) in Javascript still means you’re going to have to implement them again somewhere else. If that’s a huge problem for you in terms of maintainability, then you probably want to save Javascript for convenience rules only.

Personally, I think writing rules in Javascript is often worth the maintenance cost. While users can get around your Javascript rules, the ones who don’t (that is, the vast majority of them) will get almost instantaneous response time and save you a server hit. But there’s one big thing you have to be careful of: Business rules that require additional data.

As I said above, if you need to update a million rows as part of a business rule, you’d better do that in your database, but if you just need to query a hundred rows or so from a million-row table using a parametrized query, you can do that fine in your middle tier. You can’t, however, do that in pure Javascript, because the database, of course, is back on the server. If the point of client-side business logic is to avoid server hits, you need to load all the data you might conceivably need for validation with the page. Even if any one parametrized query returns only a hundred rows, if all possible parameters can make the query span a million rows, then a pure, server-hit-free Javascript solution is just not feasible.

You can, of course, write your own AJAX calls as part of a non-pure Javascript solution, either as a way of firing business rules implemented on the middle tier or as a way of retrieving data that will then be used in Javascript business rules. But like I said above, these are still server hits, and a true AJAX (as opposed to merely very good synchronous Javascript and XML messaging) is not really out-of-the-box even in JDeveloper 11g, and the PPR capabilities of 10g don’t even come close. I talk a little bit about implementing your own AJAX calls here; it’s actually not trivial to integrate such things with ADF.

So, in sum: it’s time to end the holy wars of business logic location. There’s a time for business logic in the DB, and a time for business logic in the middle tier. There are times where redundantly (or exclusively, in the case of convenience-only logic) implementing business logic in Javascript is very worthwhile, and times when it just isn’t.