To keep things understandable and easy to follow, the repository class that I plan to construct here will only be able to retrieve user collections that match some simple criteria. It's possible to encapsulate more complex query logic, however, as the layer that does the leg work is the mapping one, which in turn uses the persistence mechanism to run the actual SQL queries behind the scenes.
But it's time to stop talking and show the initial definition of the repository. This one, not surprisingly, is a new class. It's called "UserRepository" and its source code is shown below:
While the finders exposed by the above "UserRepository" class are pretty simplistic, they show in a nutshell the relevant role that a repository plays when you're abstracting query logic within an application. Notice that the names assigned to these methods are very explicit, which makes it easy to guess what they do. In this case, however, all the complexity required to find users by their first and last names, or even by their email addresses (although additional finders can be added in a snap) is delegated down the line -- first to the mapper (injected via the repository's constructor), and then to the persistence layer.
At the risk of being repetitive, this illustrates an important concept that I've outlined from the very beginning: a repository retrieves collections of related domain objects as if they were actually residing in memory. Obviously, this is only an illusion to the eyes of client code, which consumes the repository's API, ignoring the complexities involved in each retrieval process.
Having the previous user repository comfortably seated between the domain and mapping layers of this sample application comes with a hidden cost: due to the heavy use of dependency injection (AKA Inversion of Control), its instantiation demands the creation, in turn, of a bunch of additional collaborators.
In general, it's considered a good programming habit to keep object instantiation isolated as much as possible from application logic. This means that the appropriate construction of the previous user repository should be delegated to a factory class, which would encapsulate the entire construction process in a way similar to what a dependency injection container does in more complicated scenarios. Given that, in the following section I'm going to define this repository factory, so you can quickly grasp how it works.
Now, just keep reading.
Isolating object instantiation from application logic: building a repository factory
In most use cases, the implementation of a concrete factory is a fairly straightforward process that doesn't present major difficulties. And certainly, the one that I included below, whose responsibility is to create repositories, isn't the exception to this rule. Take a close look at it, please:
Mission accomplished. As you can see above, the earlier "RepositoryFactory" class only defines a single method called "create()," which accepts the name of the repository that will be returned to client code. In addition, the factory follows a simple convention; it assumes that the associated collaborators will be named according to the task that each of them perform (a mapper will be suffixed "Mapper," while a collection will be suffixed "Collection"). This basic rule permits you to create different kinds of repositories, including the one defined in the preceding section. What's more, the process would be as easy as writing the following lines:
$repositoryFactory = new RepositoryFactory;
Did you get the point as far as defining a factory that builds only repositories? I guess you did. In doing so, it's possible to keep application logic neatly separated from the instantiation of repositories (and therefore of their dependencies), since the internals of this creation process have been encapsulated behind an isolated class.
Finally, it's worth noting that the development of the previous user repository is still unfinished. It's mandatory to provide it with the ability to add and remove user entities from the underlying MySQL database. This will be done in the forthcoming part of the series.
In this sixth part of the series, things became more interesting, since I finally started using all of the sample classes defined previously to create a basic user repository class. Even the partial construction of this class required us to do some extra work, such as defining a repository factory. In general the entire development process was pretty straightforward, and showed how useful this kind of abstraction layer can be when concentrating querying code behind an intuitive API.
As I said before, however, this example repository is still incomplete, as can't add and delete user entities from the underlying persistence layer. Don't feel concerned about this issue, though, as this extended functionality will be implemented in the coming installment of the series.
Don't miss the next tutorial!
blog comments powered by Disqus