I read (everything on) your site, Tony, and found it very enlightening - thank you.
A problem I continually have, when reading various literature on the subject or perusing sitepoint's forums or various informative web sites, is with the seperation between the Business Logic and Data Access Logic.
Don't get me wrong - I get it; its a very worthwhile ideal to be able to switch the way you store your data and maintain the same business logic, or use your data in a new way by simply creating new components in the business logic without having to change the data access Logic.
But the two seem inexorably tied together in a practical and realistic sense. After all, what is your business logic without data to work with, and at that, should your business logic really be able to handle ANY data you pass to it? Is that healthy?
Let me use the example of the User class, something I'm sure we're all familiar with. Suppose one of the properties of a "user" is "username". You whip up your User class, then you whip up a UserMapper class, which has a method insert(User). The internals of this method inevitably make specific reference to the properties of a User object AND inevitably make specific reference to your method of storing the data (even with a DAO you need to specifically state where you are putting the info... "INSERT ... WHERE username = etc).
So now, instead of releasing your data access from the tied-down clutches of your business logic, you've tied it down to both the business logic AND the extremely specific data storage mechanism.
Perhaps what I am trying to understand is whether or not people are really serious when they talk about seperation of logic. When you add a new property to your theoretical user, you're going to have to add it in the User class, then add a place to put it in the data storage mechanism, and lastly, you've got to change the functions that map your class to your data storage.
Perhaps I'm missing the point here - so please, set my disillusioned mind to rest by clarifying what I'm getting wrong here.
I do see the benefits in being able to use the User class in any number of ways and always being able to instantiate a UserMapper to insert(User), but what is the difference from having this code internal to the User class in a practical sense (as opposed to the "because OOP is right" reasoning).
Posted by: JNKlein Nov 4th, 2004 @ 1:58 PM MST
Originally Posted by Tony Marston
In reply to the previous post from JNKlein here are my (arrogant?) views on the subject of separating business logic from data access logic:
The primary purpose of having a separate object in the data access layer (sometimes known as a Data Access Object or DAO) is that is should be possible to switch the entire application from one data source to another simply by changing this one component. Thus if I want to switch my application from MySQL to PostgreSQL (or whatever) I simply change my DAO.
In order to make this work in practice my own implementation is as follows:
(a) Each business entity (eg: customer, product, invoice) has its own class. This identifies the structure of the associated database table plus all the business rules required by that entity. Each of these is actually a subclass of a generic table class which contains sharable code that can be applied to any database table.
(b) When the business object gets instructions to update the database it does so via an insertRecord(), updateRecord(), or deleteRecord() method which contains the entire $_POST array. This is validated according to whatever rules have been defined within that particular subclass. If there are no errors it will talk to the relavant DAO in order to perform the database update.
(c) The DAO also has the insertRecord(), updateRecord() and deleteRecord() methods, but as well as the validated contents of the $_POST array it is also given a second array which contains all the table structure details. Using these two arrays it is easy to construct the relevant SQL query string before calling the relevant database API.
In this way my business object contains business rules, but no calls to database APIs, and my DAO contains calls to database APIs but no business rules. This is clear separation of logic.
Switching from one DBMS to another is simple to achieve in my infrastructure. In my generic table superclass I have a variable called $dbms_engine which is set to 'mysql' or 'postgresql' or whatever. This will then apply to all database tables unless overridden in any individual subclass. When the business object wants to talk to the data access object it first needs to instantiate an object from a class which is defined within a separate include() file. The name of this file is in the format 'dml.<engine>.class.inc' where <engine> is replaced by the contents of variable $dbms_engine. I have a separate version of this include() file for every DBMS that I use. All I need to do before accessing a new DBMS is to create a new version of the 'dml.<engine>.class.inc' file and I'm up and running.
Another advantage of this mechanism is that it would even be possible to talk to different database tables through different DBMS engines within the same transaction. Hows that for flexibility?
In case you want to see these (arrogant?) theories put into practice I have created a sample application which is described in http://www.tonymarston.net/php-mysql...plication.html. This contains links where you can run the application online as well as download all the source code and run it on your own machine. You can then examine the source code and tell me what I am doing wrong.
BTW, in your example you mentioned have a User class and a UserMapper class. Why two? I can put everything I need into a single class, which is what encapsulation is supposed to be about.
Posted by: Tony Marston from tonymarston.net Nov 5th, 2004 @ 4:29 AM MST
Originally Posted by Cochambre
I 've considered the logic layers separation in a Web Application many times. And the only thing that keeps me from efectively using it is that it's main function (independence of user interfase, business rules and data storage/retrieval) only helps when migrating or extending to other script-language/data engine/platform. But this only happens very very few times in an Application Lifetime. In the other hand, this versatility has the invonvenience of not taking advantage of each platform/engine/language optimization benefits (which usually are not compatible between them), and this lowers the application performance, affecting the consecuences directly to the users. So the question here is Versatility vs Performance. I believe that we must not punish the users by using this "development shurtcuts". This, of course, is considering that you care about your application performance. (i'm sorry if I misspelled some words. I'm from Argentina)
Posted by: Cochambre Nov 5th, 2004 @ 2:18 PM MST
Originally Posted by Tony Marston
Oochambre said: <quote> The separation of presentaion, business and data access logic only helps when migrating or extending to another script-language/data engine/platform. But this only happens very very few times in an Application Lifetime. </quote>
Your view of the benefits of the 3 tier architecture are very narrow as in reality they are not restricted to changes in the scripting language, database engine or platform.
As my article is about building web applications with PHP, and PHP can run on many platforms, any argument about not being optimized for a particular platform is rather empty.
Being able to change from one database engine to another by changing just one component is not just a fancy expensive option that is rarely used. Take the case of MySQL, for example. For versions up to 4.0 you must use the mysql_* functions, but for 4.1 and above you must use the mysqli_* functions. How complicated would that be if you had hundreds of scripts to change instead of just one? You must also consider the case where a supplier creates an application which is then run on customers own machines with the database of their choice. If it is coded so that it only runs with MySQL but they actually want PostreSQL or Oracle or whatever then how difficult would it be to cater for the customer's needs?
In my infrastructure all my XHTML output is produced from a small set of generic XSL stylesheets, which means that should I need to make changes to my 350+ screens that cannot be done by altering the CSS file then all I have to do is change my generic XSL stylesheets, which are currently about 10 in number. You may think that such changes are rare, but what about when the time comes to convert your existing web application from HTML forms to XFORMS, the latest W3C standard? I can do that by changing 10 XSL stylesheets. Can you?
Posted by: Tony Marston from tonymarston.net Nov 6th, 2004 @ 5:24 AM MST
Now, to continue ...
To respond to Tony's question about why I seperated the User and UserMapper class; if you have a User class that performs some business logic that doesn't interact with the database - suppose a hypothetical printUserName() method that just spits out the current $user->username, wouldn't you want a seperate class that mapped a user to the database, either inserting or deleting or what-have-you? Then, if you needed to add more functionality on the business logic end, you would only change the User class (not the UserMapper class) to have another method, suppose printUserEmail(). This way, you can extend or refactor your User business logic (maintaining the same interface), without changing anything about the data access.
This is how I interpret the "seperation of data access and business logic".
Maybe I'm not doing a good job of explaining my logic here - George Schlossangle says it well in his "Advanced PHP" chapter on this very subject (which, again, I recommend). Maybe someone else can clarify?
Regardless, my question to Tony is this - if you have your business logic and data access logic in the same class, can they be seperate? In reference to the business logic, Tony said "Each business entity (eg: customer, product, invoice) has its own class. This identifies the structure of the associated database table..." - I think this is the part I'm having trouble understanding, because where is the seperation of logic if the business entity knows both the business logic and must know the structure of the associated data storage mechanism.
And now, a seperate question - what is the point of a DAO (chose your favorite - ADODB or PEAR DB) that probably won't make it any easier to change what database you're using, since there is inevitably still hardcoded some query that doesn't work the same in mySQL, msSQL, PostreSQL, and Oracle, let alone just two of the above. Just because you execute($query) doesn't mean $query will actually work.