By David Bernal
On June 23rd, 2008
Today we’re going to talk about PHP enterprise patterns and all the fun they bring to the party. We’ll get into front controllers and take a close look at MVC as well.
Towns, Buildings, Construction
So let’s talk about patterns. Specifically, I’d like to talk about the MVC architecture pattern, and some thoughts I’ve had recently regarding its application. Let’s first clear the air a bit, make sure everyone is limber, and define our terms. It may be unknown to some that the seminal work in patterns, A Pattern Language: Towns, Buildings, Construction, was actually about architecture (the kind with buildings) and urban planning, not software design. One of the key observations the author makes in the book is that many cities which are laid out in a practical and attractive manner follow a similar pattern, which was varied depending on the exact situation. This is exactly how patterns in software architecture work; Patterns are elegant solutions to common problems that are flexible enough to be applied to differing scenarios.
The Venerable MVC
MVC is one of the most common patterns, especially in web development. MVC is an architecture pattern, which means that it defines several distinct responsibilities in an application, and describes a set of objects, each of which fulfills one of those responsibilities. Specifically the MVC in MVC stands for model, view and controller. Different implementations define the responsibilities differently, but roughly they are defined as follows: (that’s right, I’m so semantic I’m about to use the <dl> tag)
- Represents the data used by the application, tends to also hold some amount of business logic. Also facilitates persistence (often by using or encapsulating some kind of data abstraction layer).
- Handles events, may make changes to the model.
- Generates an appropriate interface (such as an HTML page).
This isn’t quite the whole story, and I’m not really interested in telling the whole story, but I’d like to add a little bit more. Most MVC applications will use a front controller to dispatch incoming requests. The front controller will itself use a variety of helpers to actually invoke Controllers and Views, but the diagram below represents a simplified lifecycle diagram for a typical MVC application implemented in PHP.
Fear and Doubt
A question that ought to arise after a brief period of thought and maybe a few sips of your coding-beverage of choice (mine is usually a latte or Dr. Pepper) is, “Where in the HELL am I supposed to put my business and application logic?” There has been a fair bit of discussion and ensuing misinformation about this one on the interwebs.
Some recommend that business logic be placed in the controllers. Controllers (both Front and Application), however, exist to control program flow; they do things like delegating routing, creating views, and invoking models. Furthermore, if a developer implements application logic in their controllers, they have essentially coupled the method of accessing a feature to the implementation of that feature. This means that changing URL patterns, or allowing different URLs for accessing the data in different ways requires code duplication, or ugly hacks to the routing system.
From there, then, it would appear to make sense to implement business logic on the model itself. Recall, however, that the model is really about encapsulating the data. Putting business logic in the model allows direct access to that data, rather than access to an interface. This results in a tight-coupling between the model and application logic. Reuse is hurt, because functionality specific to an application gets built into the model. Later users who require a different set of functionality but wish to access the same model are forced to either rewrite the models, or live with a bunch of crufty code written for the previous project. Logic which works on multiple objects clearly does not belong in any of the objects it uses. It becomes clear that we must take a different tack.
A Different Tack
Since we want to decouple application features from program flow, as well as from the underlying data they access, the solution is to implement some fourth class that allows access to those features, rather than to program flow, or to data. In Core J2EE Patterns, which is a great read and the inspiration for this post, this is referred to as the application service pattern. An application service object allows access to application features, hiding the underlying logic and abstracting the data access further. Note that use of an application service does not exclude direct access to models when it makes sense; it merely provides another option when logic should be decoupled from the model. We now modify the above sequence diagram to demonstrate the application service pattern in action.
Note that addition of the ApplicationService, which sits between the Controller and the Models. When the Controller needs access to a feature provided by an application service, it accesses that ApplicationService. On the other hand, when more direct access to the data is required, the option to access the model directly still exists.
Handling Online Payments: An Example
By now, I’m sure you’re thinking one thing: “What is all this hibbity-jibbity and gobbledygook?” Let’s do an example. Suppose you’re handling payments on your website. You have a transactions table which stores the payment status, the email addresses of the parties involved, and transaction id received from the gateway, and an invoices table, which stores the amount due, invoice due dates, and so on. You also have the corresponding models, which encapsulate simple CRUD operations on this data.
When the payment status changes, the server receives an XML formatted request. When this happens, a script should verify the data received by posting it back to the gateway server. If the data received is valid, it should store that information in the transactions table, and update the invoices table to reflect the new information. It makes sense to implement the validation functionality on the Transactions model, because that functionality operates only on the data encapsulated by that model, and because there isn’t a case where you would want to use that model without using the validation functionality.
It doesn’t make sense, however, to implement the logic that decides what to do once the data has been validated on the Transactions model. A payment gateway is something very likely to be reused, but the invoice\billing module will differ from project to project. It also doesn’t make sense to implement this logic on the Invoices model either, because that would couple the invoices to the payment gateway, and violate separation of concerns.
It therefore makes sense to implement an application service that handles this logic. The sequence diagram below represents the program flow using the application service pattern as described. Note that the controller still uses the Transaction model directly to access its data, before sending it to the view.
Blog Post Requirement: Fulfilled
So, we’ve fulfilled my blog post requi—er, defined a more elegant way of providing access to application services. To summarize the factors that lead us to the application service, we wanted to provide complex application features that operate on multiple objects without polluting those objects or increasing tight coupling between them. By abstracting these features away from the model, we enhanced reusability of the models and preserved separation of concerns. Similarly, by removing functionality from the controller, we decoupled application functionality from program flow.
Please contribute your comments and experiences with MVC and other patterns in developing for the web.
- You Suck At Programming And I Hate You: Things NEVER To Do In PHP & SQL
- TechCrunch50 Fail Boat: Yet Another Clone Wins, Innovation Is Dead
- Magento eCommerce Review: Platform Perils and Impressions, Three Months In
- A Simple mod_rewrite Tutorial: SEO-Friendly, Attractive URLs
- Thoughts on E-Government and Open-Source Software