It's all multi-tier, all the way down
published: Tue, 1-Mar-2005 | updated: Mon, 16-May-2005
I've been writing a bit recently on multi-tier architectures and the issues that arise when designing, developing, and testing them. Meanwhile, I've also been researching a couple of patterns for an article I'm writing for The Delphi Magazine: Inversion of Control and Dependency Injection.
Today, I suddenly realized (yes, I'm a bit slow on the uptake sometimes) that if you use the Inversion of Control principles in a standalone application, you will be essentially writing a multi-tier application with well-defined layers. These layers will just be executing within a single process, but they are (should be?) still there.
OK, let's break this up a bit. The simplest logical architecture for a multi-tier application has the following layers:
- Business Rules
- Data Access
- Data Storage
Now, it must be said that in most multi-tier apps, the Presentation and UI layers are munged together, as are the Data Access and Data Storage layers, but we're not here to talk about that.
The Presentation layer is the bit the users see, be it on a screen or on paper. The UI layer takes data from the business rules layer, transforms it to suit the presentation and passes it along; it also takes user input and passes it back to the business layer. In a WinForms app, these two layers are generally one and the same, but in a web forms app, they will be different: the presentation layer is the browser and the UI layer is the aspx page together with the code- behind stuff.
The business layer encapsulates the knowledge of the business problem being solved. The data access layer essentially contains information and knowledge about how to persist the data from the business layer and to read it back. The Data Storage layer is the bit that knows hows to physically create, retrieve, update and delete data from the persistent store.
Phew. Now it makes sense for the interactions between these layers to be defined as a set of abstractions, a set of interfaces, if you will. Why? Well, if the business layer "knows" about T-SQL statements (say it constructs some on the fly), then we're locked into SQL Server, no matter if we want to use MySQL or use a simple XML document. If it "knows" about FindFirst, FindNext and FindClose for navigating through the files in a Windows folder, then we can't use the same code for navigating through the files on an FTP site. And so on.
Another good reason for programming the layers to a set of interfaces is that we can mock the interfaces for unit testing. (No, this isn't being cruel to interfaces, heh, I'm talking about using mock objects that pretend to be the real service underneath the interface.) This is an enormous benefit. Enormous. Take the folder navigation example from above: we don't have to create the folder structure physically on disk; instead we create a mock object that returns a set of predefined results and that can check that the interface methods were called in the right order and at the right time and for the right number of times.
What's this decoupling of code into layers by interfaces called? Inversion of Control; where we came in. We can write our code as interfaces and small, easily testable classes. We then can link it all together to form the application itself, and acceptance test that. All applications are multi-tier using this strategy.