Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
September 12, 2012 12:56 am GMT

Evolving Toward a Persistence Layer

One of the most difficult and talked about design pattern is persistence. The need for an application to persist its internal state and data is so tremendous that there are likely tens – if not hundreds – of different technologies to address this single problem. Unfortunately, no technology is a magic bullet that solves everything. Each application, and sometimes each component of said application, is unique in its own way – thus, requiring a unique solution.

In this tutorial, I will teach you some best practices to help you determine which approach to take, when working on future applications. I will briefly discuss some high level design concerns and principles, followed by a more detailed view on the Active Record design pattern, combined with a few words about the Table Data Gateway design pattern.

Of course, I will not merely teach you the theory behind the design, but I will also guide you through an example that begins as random code and transforms into a structured persistence solution.


Two Tales of a Single Application

The Database is for Data, not for Code

Today, no programmer can understand this archaic system.

The oldest project I have to work on began in the year 2000. Back then, a team of programmers started a new project by evaluating different requirements, thought about the workloads the application will have to handle, tested different technologies and reached a conclusion: all the PHP code of the application, except the index.php file, should reside in a MySQL database. Their decision may sound outrageous today, but it was acceptable twelve years ago (OK… maybe not).

They started by creating their base tables, and then other tables for each web page. The solution worked… for a time. The original authors knew how to maintain it, but then each author left one by one–leaving the code base in the hands of other newcomers.

Today, no programmer can understand this archaic system. Everything starts with a MySQL query from index.php. The result of that query returns some PHP code that executes even more queries. The simplest scenario involves at least five database tables. Naturally, there are no tests or specifications. Modifying anything is a no-go, and we simply have to rewrite the entire module if something goes wrong.

The original developers ignored the fact that a database should only contain data, not business logic or presentation. They mixed PHP and HTML code with MySQL and ignored high level design concepts.

The HTML is for Presentation and Presentation Only

All applications should concentrate on respecting a clean, high level design.

As time passed, the new programmers needed to add additional features to the system while, at the same time, fixing old bugs. There was no way to continue using MySQL tables for everything, and everyone involved in maintaining the code agreed that its design was horribly flawed. So the new programmers evaluated different requirements, thought about the workloads the application will have to handle, tested different technologies and reached a conclusion: they decided to move as much code as possible to the final presentation. Again, this decision may sound outrageous today, but it was light years from the previous outrageous design.

The developers adopted a templating framework and based the application around it, starting every new feature and module with a new = "%s"', $title));$blogPostAsString = $SqliteResult->fetchArray(SQLITE3_ASSOC);return new BlogPost($blogPostAsString['title'], $blogPostAsString['content'], $blogPostAsString['timestamp']);}}

Note: The test for this implementation is also available in the source code, but, due to its complexity and length, I did not include it here.


Moving Toward the Active Record Pattern

Active Record is one of the most controversial patterns. Some embrace it (like Rails and CakePHP), and others avoid it. Many Object Relational Mapping (ORM) applications use this pattern to save objects in tables. Here is its schema:

Active record pattern

As you can see, Active Record-based objects can persist and retrieve themselves. This is usually achieved by extending an ActiveRecordBase class, a class that knows how to work with the database.

The biggest problem with Active Record is the extends dependency. As we all know, inheritance is the strongest type of dependency, and it’s best to avoid it most of the time.

Before we go further, here is where we are right now:

Gateway in high level schema

The gateway interface belongs to the business logic, and its concrete implementations belong to the persistence layer. Our BlogPostController has two dependencies, both pointing toward the business logic: the SqlitePost gateway and BlogPost class.

Going for Active Record

There are many other patterns, like the Proxy Pattern, that are closely related to persistence.

If we were to follow the Active Record pattern exactly as it is presented by Martin Fowler in his 2003 book, Patterns of Enterprise Application Architecture, then we would need to move the SQL queries into the BlogPost class. This, however, has the problem of violating both the Dependency Inversion Principle and the Open Closed Principle. The Dependency Inversion Principle states that:

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend upon details. Details should depend upon abstractions.

And the Open Closed Principle states: software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. We will take a more interesting approach and integrate the gateway into our Active Record solution.

If you try to do this on your own, you probably already realized that adding the Active Record pattern to the code will mess things up. For this reason, I took the option of disabling the controller and SqlitePost tests to concentrate only on the BlogPost class. The first steps are: make BlogPost load itself by setting its constructor as private and connect it to the gateway interface. Here is the first version of the BlogPostTest file:

require_once '../BlogPost.php';require_once '../InMemoryPost.php';require_once '../ActiveRecordBase.php';class BlogPostTest extends PHPUnit_Framework_TestCase {function testItCanConnectPostToGateway() {$blogPost = BlogPost::load();$blogPost->setGateway($this->inMemoryPost());$this->assertEquals($blogPost->getGateway(), $this->inMemoryPost());}function testItCanCreateANewAndEmptyBlogPost() {$blogPost = BlogPost::load();$this->assertNull($blogPost->title);$this->assertNull($blogPost->content);$this->assertNull($blogPost->timestamp);$this->assertInstanceOf('Gateway', $blogPost->getGateway());}private function inMemoryPost() {return new InMemoryPost();}}

It tests that a blog post is correctly initialized and that it can have a gateway if set. It is a good practice to use multiple asserts when they all test the same concept and logic.

Our second test has several assertions, but all of them refer to the same common concept of empty blog post. Of course, the BlogPost class has also been modified:

class BlogPost {private $title;private $content;private $timestamp;private static $gateway;private function __construct($title = null, $content = null, $timestamp = null) {$this->title = $title;$this->content = $content;$this->timestamp = $timestamp;}function __get($name) {return $this->$name;}function setGateway($gateway) {self::$gateway = $gateway;}function getGateway() {return self::$gateway;}static function load() {if(!self::$gateway) self::$gateway = new SqlitePost();return new self;}}

It now has a load() method that returns a new object with a valid gateway. From this point on, we will continue with the implementation of a load($title) method to create a new BlogPost with information from the database. For easy testing, I implemented an InMemoryPost class for persistence. It just keeps a list of objects in memory and returns information as desired:

class InMemoryPost implements Gateway {private $blogPosts = array();public function findByTitle($blogPostTitle) {return array('title' => $this->blogPosts[$blogPostTitle]->title,'content' => $this->blogPosts[$blogPostTitle]->content,'timestamp' => $this->blogPosts[$blogPostTitle]->timestamp);}public function persist(BlogPost $blogPostObject) {$this->blogPosts[$blogPostObject->title] = $blogPostObject;}}

Next, I realized that the initial idea of connecting the BlogPost to a gateway via a separate method was useless. So, I modified the tests, accordingly:

class BlogPostTest extends PHPUnit_Framework_TestCase {function testItCanCreateANewAndEmptyBlogPost() {$blogPost = BlogPost::load();$this->assertNull($blogPost->title);$this->assertNull($blogPost->content);$this->assertNull($blogPost->timestamp);}function testItCanLoadABlogPostByTitle() {$gateway = $this->inMemoryPost();$aBlogPosWithData = $this->aBlogPostWithData($gateway);$gateway->persist($aBlogPosWithData);$this->assertEquals($aBlogPosWithData, BlogPost::load('some_title', null, null, $gateway));}private function inMemoryPost() {return new InMemoryPost();}private function aBlogPostWithData($gateway = null) {return BlogPost::load('some_title', 'some content', '123', $gateway);}}

As you can see, I radically changed the way BlogPost is used.

class BlogPost {private $title;private $content;private $timestamp;private function __construct($title = null, $content = null, $timestamp = null) {$this->title = $title;$this->content = $content;$this->timestamp = $timestamp;}function __get($name) {return $this->$name;}static function load($title = null, $content = null, $timestamp = null, $gateway = null) {$gateway = $gateway ? : new SqlitePost();if(!$content) {$postArray = $gateway->findByTitle($title);if ($postArray) return new self($postArray['title'], $postArray['content'], $postArray['timestamp']);}return new self($title, $content, $timestamp);}}

The load() method checks the $content parameter for a value and creates a new BlogPost if a value was supplied. If not, the method tries to find a blog post with the given title. If a post is found, it is returned; if there is none, the method creates an empty BlogPost object.

In order for this code to work, we will also need to change how the gateway works. Our implementation needs to return an associative array with title, content, and timestamp elements instead of the object itself. This is a convention I’ve chosen. You may find other variants, like a plain array, more attractive. Here are the modifications in SqlitePostTest:

function testItCanRetrieveABlogPostByItsTitle() {[...]//we expect an array instead of an object$this->assertEquals($this->blogPostAsArray, $gateway->findByTitle($this->blogPostAsArray['title']));}private function aBlogPostWithValues() {//we use static load instead of constructor callreturn $blogPost = BlogPost::load($this->blogPostAsArray['title'],$this->blogPostAsArray['content'],$this->blogPostAsArray['timestamp']);}

And the implementation changes are:

public function findByTitle($title) {$SqliteResult = $this->dbhandle->query(sprintf('SELECT * FROM BlogPosts WHERE title = "%s"', $title));//return the result directly, don't construct the objectreturn $SqliteResult->fetchArray(SQLITE3_ASSOC);}

We are almost done. Add a persist() method to the BlogPost and call all the newly implemented methods from the controller. Here is the persist() method that will just use the gateway’s persist() method:

private function persist() {$this->gateway->persist($this);}

And the controller:

class BlogPostController {function save($data) {$blogPost = BlogPost::load($data['title'], $data['content'], $data['timestamp']);$blogPost->persist();}function findByTitle($title) {return BlogPost::load($title);}}

The BlogPostController became so simple that I removed all of its tests. It simply calls the BlogPost object’s persist() method. Naturally, you’ll want to add tests if, and when, you have more code in the controller. The code download still contains a test file for the BlogPostController, but its content is commented.


Conclusion

This is just the tip of the iceberg.

You’ve seen two different persistence implementations: the Gateway and Active Record patterns. From this point, you can implement an ActiveRecordBase abstract class to extend for all your classes that need persistence. This abstract class can use different gateways in order to persist data, and each implementation can even use different logic to fit your needs.

But this is just the tip of the iceberg. There are many other patterns, such as the Proxy Pattern, which are closely related to persistence; each pattern works for a particular situation. I recommend that you always implement the simplest solution first, and then implement another pattern when your needs change.

I hope you enjoyed this tutorial, and I eagerly await your opinions and alternative implementations to my solution within the comments below.



Original Link: http://feedproxy.google.com/~r/nettuts/~3/p-gDDLtmfVE/

Share this article:    Share on Facebook
View Full Article

TutsPlus - Code

Tuts+ is a site aimed at web developers and designers offering tutorials and articles on technologies, skills and techniques to improve how you design and build websites.

More About this Source Visit TutsPlus - Code