One of the most common issues that must be addressed during the development of a large web application that exposes a prolific domain layer is how to optimize the operations that are performed on domain objects. Clearly, if numerous objects need to be fetched, saved to or deleted from the underlying persistence mechanism, which usually rests on a relational database, the overhead can be huge. In fact, it may bring the application to its knees. Of course, a caching system in any of its flavors may be helpful in a case like this, and I'd be the first to pick one that fits my needs. However, sooner or later the objects in question must be retrieved, persisted or even removed, which means that multiple (and expensive) trips to the database are unavoidable. There's no need to panic, though, as there exist some approaches to solving this problem, at least partially. For instance, a proper implementation of a data mapper together with an identity map can help to tackle the issue on two well-differentiated fronts: on one hand, the mapper would permit you to decouple domain objects from the persistence layer, thus addressing the so-called impedance mismatch; while on the other hand, the identity map would keep track of which objects have been fetched, and also assure that each of them would be fetched only once. The major drawback with this approach is the intrinsic nature of an identity map, as it does reduce database trips, but only for retrieval operations. What about object insertions, updates and deletions? This is where an enterprise-level pattern known as Unit of Work (UoW) can help. Simply put, a UoW is an additional layer, usually placed between the domain and the mapping ones, which allows you to hold multiple domain objects in memory and "mark" them for further insertion, update or removal. The objects are queued deliberately in RAM until the actual database operations are performed in one go through a single method call. While this transactional model permits you to save database trips, its functionality comes with an extra cost worth stressing. First off, a UoW requires you to implement an additional layer upon the existing ones, which in many cases may be overkill. And last but not least, it demands a careful manipulation of in-memory objects to maintain database integrity. With that said, you should consider whether or not your web project really needs to implement a UoW, given the hard upfront work that it demands. In short, be conscientious and evaluate its pros and cons with due care. In addition, like any other pattern available out there, a UoW is "language agnostic," which means that it can be implemented in any programming language, including PHP. Since the experience can be instructive, especially for those PHP developers wanting to be in touch with the trends imposed by modern web application development, in this article series I'll be building a simple UoW from scratch, so that you can grasp its underlying logic and see the actual benefits that it brings to the table. Ready to take the first step of this hopefully educational journey? Then let's get started! Getting started: defining an abstract Unit of Work As noted in the introduction, a UoW queues multiple domain objects in memory, which are later inserted, updated or even removed from the persistence layer in a single operation. This resembles the transactional model present in many RDBMS. The question is, how does the UoW know which objects need to be inserted, updated or deleted? In reality, there are several ways to accomplish this, but in many cases the tracking process is ruled by a simple convention that you'll find easy to follow. First, new domain objects created in memory will be marked "new," thus set for further insertion; second, objects previously fetched from the storage mechanism and then modified in some way will be marked "dirty" and set for further update. Finally, objects explicitly marked "removed" will obviously be deleted. In addition, it's possible to implement a fourth state called "clean." Objects tracked this way will be left untouched. Of course, client code consuming the UoW can swap these states at any time according to specific requirements; for now, don't feel concerned about how this will be done, as the topic will be discussed in depth in upcoming tutorials of this series. Having outlined basically how a UoW keeps track of domain objects, it's time to start creating a class capable of performing the aforementioned tasks. Below I defined a basic, yet functional abstract UoW, whose partial implementation looks like this: <?php abstract class UnitOfWorkAbstract Don't let the lengthy definition of the previous class overwhelm you; the logic implemented by its methods is easy to follow. As you can see from the above code snippet, each tracking process in domain objects is performed by four discrete methods called "markNew()," "markClean()," "markDirty()" and "markRemoved()," which store the marked objects (in this case entities) in separate arrays. Also, the methods have some complementary getters, which I'll skip over at this time, since they can be grasped in a snap. Although the definition of this abstract UoW is incomplete right now, it should be clearer for you to see how it does its thing; each domain object is marked "new," "clean," "dirty" or "removed" via plain array manipulation. It's that simple. Also, you may have noticed that the class' constructor injects an instance of an abstract mapper. Once again, don't worry about the implementation of this collaborator, since it will be covered in detail in another installment. So far, so good. In its current incarnation, the UoW is capable of assigning different states to the inputted entities, which is all well and good. You're now wondering where this class implements the transactional model, right? Well, to be frank, this functionality hasn't been added yet, but it'll be delegated to a separate method that will be discussed in the following section. So click on the link below and keep reading.
blog comments powered by Disqus |
|
|
|
|
|
|
|