Building a Domain Model – Integrating Data Mappers

Tweet

While there’s still a huge number of cases where Domain Models are considered an overkill “enterprisey” solution that doesn’t jive with the natural pragmatism that proliferates throughout the world of PHP, they’re steadily breaching the minds of many developers, even of those who cling to the Database Model paradigm like the last life jacket of a sinking ship.

There are some reasons that largely justify such a reaction. After all, building even the simplest Domain Model demands definition of the constraints, rules, and relationships among its building objects, how they will behave in a given context, and what type of data they’ll carry during their life cycle. Plus, the process of transferring model data to and from the storage will likely require to drop a set of Data Mappers at some point, a fact that highlights why Domain Models are often surrounded by a cloud of bullying.

Eager prejudgements tend to be misleading, though. The bones of a rich Domain Model will certainly be accommodated more comfortably inside the boundaries of a large application, but it’s possible to scale them down and get the most from them in smaller environments too. To demonstrate this, in my previous article I showed you how to implement a simple blog domain model composed of a few posts, comments, and user objects.

The previous article lacked a true happy ending; it merely showed the mechanics of the model, not how to put it to work in synchrony with a “real” persistence layer. So before you throw me to the lions for such an impolite attitude, in this follow-up we’ll be developing a basic mapping module which will allow you to move data easily between the blog’s model and a MySQL database, all while keeping them neatly isolated from one other.

Building a Naive DAL (or why my PDO adapter is better than yours)

The phrase may sound like an cheap cliché, I know, but I’m not particularly interested in reinventing the wheel each time I tackle a software problem (unless I need a nicer and faster wheel, of course). In this case, the situation does warrant some additional effort considering we’ll be trying to connect a batch of mapping classes to a blog’s domain model. Given the magnitude of the endeavor, the idea is to set up from scratch a basic Data Access Layer (DAL) so that domain objects can easily be persisted in a MySQL database, and in turn, retrieved on request through some generic finders.

The DAL in question will be made up of just a couple of components: the first one will be a simple database adapter interface, whose contract looks like this:

<?php
namespace LibraryDatabase;

interface DatabaseAdapterInterface
{
    public function connect();
    public function disconnect();
    
    public function prepare($sql, array $options = array());
    public function execute(array $parameters = array());
    
    public function fetch($fetchStyle = null, 
        $cursorOrientation = null, $cursorOffset = null);
    public function fetchAll($fetchStyle = null, $column = 0);
    
    public function select($table, array $bind, 
        $boolOperator = "AND");
    public function insert($table, array $bind);
    public function update($table, array $bind, $where = "");
    public function delete($table, $where = "");
}

Undeniably, the above DatabaseAdapterInterface is a tameable creature. Its contract allows us to create different database adapters at runtime and perform a few common tasks, such as connecting to the database and running CRUD operations without much fuss.

Now we need at least one implementer of the interface that does all these cool things. The proud cavalier that will assume this responsibility will be a non-canonical PDO adapter, which looks as follows:

<?php
namespace LibraryDatabase;

class PdoAdapter implements DatabaseAdapterInterface
{
    protected $config = array();
    protected $connection;
    protected $statement;
    protected $fetchMode = PDO::FETCH_ASSOC;   
    
    public function __construct($dsn, $username = null,
        $password = null, array $driverOptions = array()) {
        $this->config = compact("dsn", "username", "password", 
            "driverOptions");
    }

    public function getStatement() {
        if ($this->statement === null) {
            throw new PDOException(
              "There is no PDOStatement object for use.");
        } 
        return $this->statement;
    }
    
    public function connect() {
        // if there is a PDO object already, return early
        if ($this->connection) {
            return;
        }
 
        try {
            $this->connection = new PDO(
                $this->config["dsn"],
                $this->config["username"],
                $this->config["password"],
                $this->config["driverOptions"]);
            $this->connection->setAttribute(PDO::ATTR_ERRMODE,
                PDO::ERRMODE_EXCEPTION);
            $this->connection->setAttribute(
                PDO::ATTR_EMULATE_PREPARES, false); 
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
    
    public function disconnect() {
        $this->connection = null;
    }
    
    public function prepare($sql, array $options = array() {
        $this->connect();
        try {
            $this->statement = $this->connection->prepare($sql, 
                $options);
            return $this;
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
    
    public function execute(array $parameters = array()) {
        try {
            $this->getStatement()->execute($parameters);
            return $this;
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
    
    public function countAffectedRows() {
        try {
            return $this->getStatement()->rowCount();
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }

    public function getLastInsertId($name = null) {
        $this->connect();
        return $this->connection->lastInsertId($name);
    }
    
    public function fetch($fetchStyle = null,
        $cursorOrientation = null, $cursorOffset = null) {
        if ($fetchStyle === null) {
            $fetchStyle = $this->fetchMode;
        }
 
        try {
            return $this->getStatement()->fetch($fetchStyle, 
                $cursorOrientation, $cursorOffset);
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
     
    public function fetchAll($fetchStyle = null, $column = 0) {
        if ($fetchStyle === null) {
            $fetchStyle = $this->fetchMode;
        }
 
        try {
            return $fetchStyle === PDO::FETCH_COLUMN
               ? $this->getStatement()->fetchAll($fetchStyle, $column)
               : $this->getStatement()->fetchAll($fetchStyle);
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
    
    public function select($table, array $bind = array(), 
        $boolOperator = "AND") {
        if ($bind) {
            $where = array();
            foreach ($bind as $col => $value) {
                unset($bind[$col]);
                $bind[":" . $col] = $value;
                $where[] = $col . " = :" . $col;
            }
        }
 
        $sql = "SELECT * FROM " . $table
            . (($bind) ? " WHERE "
            . implode(" " . $boolOperator . " ", $where) : " ");
        $this->prepare($sql)
            ->execute($bind);
        return $this;
    }
    
    public function insert($table, array $bind) {
        $cols = implode(", ", array_keys($bind));
        $values = implode(", :", array_keys($bind));
        foreach ($bind as $col => $value) {
            unset($bind[$col]);
            $bind[":" . $col] = $value;
        }
 
        $sql = "INSERT INTO " . $table
            . " (" . $cols . ")  VALUES (:" . $values . ")";
        return (int) $this->prepare($sql)
            ->execute($bind)
            ->getLastInsertId();
    }
    
    public function update($table, array $bind, $where = "") {
        $set = array();
        foreach ($bind as $col => $value) {
            unset($bind[$col]);
            $bind[":" . $col] = $value;
            $set[] = $col . " = :" . $col;
        }
 
        $sql = "UPDATE " . $table . " SET " . implode(", ", $set)
            . (($where) ? " WHERE " . $where : " ");
        return $this->prepare($sql)
            ->execute($bind)
            ->countAffectedRows();
    }
    
    public function delete($table, $where = "") {
        $sql = "DELETE FROM " . $table . (($where) ? " WHERE " . $where : " ");
        return $this->prepare($sql)
            ->execute()
            ->countAffectedRows();
    }
}

Feel free to curse me for throwing such a fat code snippet at you, but it was a necessary evil. What’s more, even while the PdoAdapter class looks somewhat tangled, it’s actually a simple wrapper which exploits much of the functionality that PDO offers right out the box without exposing client code to a verbose API.

Now that PdoAdapter is doing the dirty database work for us, let’s create a few MySQL tables which will store the data corresponding to blog posts, comments, and users:

CREATE TABLE posts (
  id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  title VARCHAR(100) DEFAULT NULL,
  content TEXT,

  PRIMARY KEY (id)
);

CREATE TABLE users (
  id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(45) DEFAULT NULL,
  email VARCHAR(45) DEFAULT NULL,

  PRIMARY KEY (id)
);

CREATE TABLE comments (
  id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  content TEXT,
  user_id INTEGER DEFAULT NULL,
  post_id INTEGER DEFAULT NULL,

  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES users(id),
  FOREGIN KEY (post_id) REFERENCES posts(id)
);

The above database schema defines a one-to-many relationship between posts and comments, and a one-to-one relationship between comments and users (the blog’s commentators). If you’re feeling adventurous, you’re free to tweak the schema at will to suit your specific needs. For the sake of brevity, however, I’ve kept it that simple.

At this point we’ve implemented a simple DAL which we can use for persisting the blog’s domain model in MySQL without sweating too much during the process. Now we need to add the middle men to the picture, that is the aforementioned data mappers, so any impedance mismatches can be handled quietly behind the scenes.

Implementing a Bi-directional Mapping Layer

It depends on the context of course, but most of the time building a mapping layer (and specifically a bi-directional relational one) is quite a ways away from being trivial. The process doesn’t boil down to just say… hey, I’ll get these relational mappers up and running during a coffee break. That’s why ORM libraries like Doctrine live and breathe after all. In this case, however, we want to leverage the bold coder living inside of us and create our own set of mappers to l massage the blog’s domain objects without facing the learning curve of a third-party package.

Let’s begin with encapsulating as much mapping logic as possible within an abstract class, like the below one:

<?php
namespace ModelMapper;
use LibraryDatabaseDatabaseAdapterInterface;

abstract class AbstractDataMapper
{
    protected $adapter;
    protected $entityTable;

    public function __construct(DatabaseAdapterInterface $adapter) {
        $this->adapter = $adapter;
    }

    public function getAdapter() {
        return $this->adapter;
    }

    public function findById($id)
    {
        $this->adapter->select($this->entityTable,
            array('id' => $id));

        if (!$row = $this->adapter->fetch()) {
            return null;
        }

        return $this->createEntity($row);
    }

    public function findAll(array $conditions = array())
    {
        $entities = array();
        $this->adapter->select($this->entityTable, $conditions);
        $rows = $this->adapter->fetchAll();

        if ($rows) {
            foreach ($rows as $row) {
                $entities[] = $this->createEntity($row);
            }
        }

        return $entities;
    }

    // Create an entity (implementation delegated to concrete mappers)
    abstract protected function createEntity(array $row);
}

The class abstracts away behind a couple of generic finders all of the logic required for pulling in data from a specified table, which is then used for reconstituting domain objects in a valid state. Because reconstitutions should be delegated down the hierarchy to refined implementations, the createEntity() method has been declared abstract.

Let’s now define the set of concrete mappers that will deal with blog posts, comments, and users. Here’s the first one, along with the interface it implements:

<?php
namespace ModelMapper;
use ModelPostInterface;

interface PostMapperInterface
{
   public function findById($id);
   public function findAll(array $conditions = array());

   public function insert(PostInterface $post);
   public function delete($id);
}
<?php
namespace ModelMapper;
use LibraryDatabaseDatabaseAdapterInterface,
    ModelPostInterface,
    ModelPost;

class PostMapper extends AbstractDataMapper implements PostMapperInterface
{
    protected $commentMapper;
    protected $entityTable = "posts";

    public function __construct(DatabaseAdapterInterface $adapter,
        CommentMapperInterface $commenMapper) {
        $this->commentMapper = $commenMapper;
        parent::__construct($adapter);
    }

    public function insert(PostInterface $post) {
        $post->id = $this->adapter->insert($this->entityTable,
            array("title"   => $post->title,
                  "content" => $post->content));
        return $post->id;
    }

    public function delete($id) {
        if ($id instanceof PostInterface) {
            $id = $id->id;
        }

        $this->adapter->delete($this->entityTable, "id = $id");
        return $this->commentMapper->delete("post_id = $id");
    }

    protected function createEntity(array $row) {
        $comments = $this->commentMapper->findAll(
            array("post_id" => $row["id"]));
        return new Post($row["title"], $row["content"], $comments);
    }
}

Notice that the implementation of PostMapper follows a fairly logical path. Simply put, not only does it extend its abstract parent, but it injects in the constructor a comment mapper (still undefined), in order to handle in sync both posts and comments without revealing to the outside world the complexities of creating the whole object graph. Of course, we shouldn’t deny ourselves the joy of seeing how the still veiled comment mapper looks, hence here’s its source code, coupled to the corresponding interface:

<?php
namespace ModelMapper;
use ModelCommentInterface;

interface CommentMapperInterface
{
    public function findById($id);
    public function findAll(array $conditions = array());

    public function insert(CommentInterface $comment, $postId,
        $userId);
    public function delete($id);
}
<?php
namespace ModelMapper;
use LibraryDatabaseDatabaseAdapterInterface,
    ModelCommentInterface,
    ModelComment;

class CommentMapper extends AbstractDataMapper implements CommentMapperInterface
{
    protected $userMapper;
    protected $entityTable = "comments";

    public function __construct(DatabaseAdapterInterface $adapter,
        UserMapperInterface $userMapper) {
        $this->userMapper = $userMapper;
        parent::__construct($adapter);
    }

    public function insert(CommentInterface $comment, $postId, 
        $userId) {
        $comment->id = $this->adapter->insert($this->entityTable, 
            array("content" => $comment->content,
                  "post_id" => $postId,
                  "user_id" => $userId));
        return $comment->id;
    }

    public function delete($id) {
        if ($id instanceof CommentInterface) {
            $id = $id->id;
        }

        return $this->adapter->delete($this->entityTable,
            "id = $id");
    }

    protected function createEntity(array $row) {
        $user = $this->userMapper->findById($row["user_id"]);
        return new Comment($row["content"], $user);
    }
}

The CommentMapper class behaves quite similar to its sibling PostMapper. In short, it asks for a user mapper in the constructor, so that a specific comment can be tied up to the corresponding commenter. Considering the easy-going nature of CommentMapper, let’s make a final effort and define another which will handle users:

<?php
namespace ModelMapper;
use ModelUserInterface;

interface UserMapperInterface
{
    public function findById($id);    
    public function findAll(array $conditions = array());
    
    public function insert(UserInterface $user);
    public function delete($id);
}
<?php
namespace ModelMapper;
use ModelUserInterface,
    ModelUser;

class UserMapper extends AbstractDataMapper implements UserMapperInterface
{    
    protected $entityTable = "users";

    public function insert(UserInterface $user) {
        $user->id = $this->adapter->insert($this->entityTable,
            array("name"  => $user->name,
                  "email" => $user->email));
        return $user->id;
    }

    public function delete($id) {
        if ($id instanceof UserInterface) {
            $id = $id->id;
        }
 
        return $this->adapter->delete($this->entityTable,
            array("id = $id"));
    }

    protected function createEntity(array $row) {
        return new User($row["name"], $row["email"]);
    }
}

Now that the UserMapper class is set, we’ve finally reached the goal we were committed to from the very start: build up from scratch an easy-to-massage mapping layer, capable of moving data back and forward between a simplistic blog domain model and MySQL. But let’s not pat ourselves in the back yet, as the best way to see if the mappers are as functional as they look at first blush is by example.

Mapping the Blog’s Domain Objects to and from the DAL

As you might expect, consuming the blog domain model in an efficient manner is pretty straightforward, as the mappers’ APIs do the actual hard work and hide the underlying database from the model itself. This ability, though, is best appreciated from the application layer’s perspective. Let’s wire up all the mapper graphs together:

<?php
use LibraryLoaderAutoloader;
require_once __DIR__ . "/Autoloader.php";
$autoloader = new Autoloader();
$autoloader->register();

// create a PDO adapter
$adapter = new PdoAdapter("mysql:dbname=blog", "myfancyusername",
    "myhardtoguesspassword");
 
// create the mappers
$userMapper = new UserMapper($adapter);
$commentMapper = new CommentMapper($adapter, $userMapper);
$postMapper = new PostMapper($adapter, $commentMapper);

So far, so good. At this point, the mappers have been initialized by dropping their collaborators into the corresponding constructors. Considering that they’re ready to get some real action, now let’s use the post mapper and insert a few trivial posts into the database:

<?php
$postMapper->insert(
    new Post(
        "Welcome to SitePoint",
        "To become yourself a true PHP master, you must first master PHP."));

$postMapper->insert(
    new Post(
        "Welcome to SitePoint (Reprise)",
        "To become yourself a PHP Master, you must first master... Wait! Did I post that already?"));

If all works as expected, the posts table should be nicely populated with the previous entries. But, is it just me or do they look a little lonely? Let’s fix that add a few comments:

<?php
$user = new User("Everchanging Joe", "joe@example.com");
$userMapper->insert($user);

// Joe's comments for the first post (post ID = 1, user ID = 1)
$commentMapper->insert(
    new Comment(
        "I just love this post! Looking forward to seeing more of this stuff.",
        $user),
    1, $user->id);

$commentMapper->insert(
    new Comment(
        "I just changed my mind and dislike this post! Hope not seeing more of this stuff.",
        $user),
    1, $user->id);

// Joe's comment for the second post (post ID = 2, user ID = 1)
$commentMapper->insert(
    new Comment(
        "Not quite sure if I like this post or not, so I cannot say anything for now.", 
        $user),
    2, $user->id);

Thanks to Joe’s remarkable eloquence, the first blog post should have now two comments and second should have one. Notice that the foreign keys used to sustain the bound between comments and users have been just picked up at runtime. In production, however, they most likely should be gathered inside the user interface.

Now that the blog’s database has been finally hydrated with a couple of posts, comments, and a chatty user’s info, the last thing we should do is pull in all the data and dump it on screen. Here we go:

<?php
$posts = $postMapper->findAll();

Even when this one-liner is… well just a one-liner, it’s actually the workhorse that creates blog domain object graphs on request from the database and put them in memory for further processing. On the other hand, the graphs in question can be easily decomposed back through a skeletal view, as follows:

<!doctype html>
<html>
<head>
 <meta charset="utf-8">
 <title>Building a Domain Model in PHP</title>
</head>
<body>
 <header>
  <h1>SitePoint.com</h1>
 </header>
 <section>
  <ul>
<?php
foreach ($posts as $post) {
?>
   <li>
    <h2><?php echo $post->title;?></h2>
    <p><?php echo $post->content;?></p>
<?php
	if ($post->comments) {
?>
    <ul>  
<?php
		foreach ($post->comments as $comment) {
?>
     <li>
      <h3><?php echo $comment->user->name;?> says:</h3>
      <p><?php echo $comment->content;?></p>
     </li>
<?php
		}
?>
    </ul>
<?php
	}
?>
    </li>
<?php
}
?>
   </ul>
  </section>
 </body>
</html>

Indeed, looping through a few posts, comments, and users is a boring task that doesn’t deserve any further explanation. Of course if you’re an insightful, hawk-eye observer, you might have noticed that this seemingly harmless view is in fact a hog eater which blatantly pulls in the entire database and throws it into the user interface’s heart. Let’s no rush to judgments since there are a few common tricks that can be used for tackling this issue, including caching (in any of its multiple forms), lazy-loading, and so forth.

Spicing up the data mappers with some of these goodies will be left as homework for you, something that surely will keep you entertained for quite a long time.

Closing Remarks

Very few will disagree that the implementation of a rich Domain Model is far away from being an overnight, high school-like task, even when using an easy-going language like PHP. While it’s safe to say that forwarding model data to and from the DAL can be delegated in many cases to a turnkey mapping library or framework (assuming there exists such a thing), defining the relationships between domain objects, as well as their own rules, data, and behavior is up to the developer.

Despite this, the extra effort in general causes a beneficial wave effect, as it helps out in pushing actively the use of a multi-tier design along with good OOP practices where the involved objects have just a few, well-defined responsibilities, and the model doesn’t get its pristine ecosystem polluted with database logic. Add to this that shifting the model from one infrastructure to another can be done in a fairly painless fashion, and you’ll get to see why this approach is very appealing when developing applications that must scale well.

Image via kentoh/ Shutterstock

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://fomigo.net fomigo

    Thank you for these articles, Alex!

    • Alex Gervasio

      Hey, glad you liked them indeed :-) . Thanks for the feedback.

  • Cristian David Grajales

    Great articles, I found them very interesting and usefull.
    I would like to know if there is a way to model left joins easy.

    thanks

    • Alex Gervasio

      Hi Christian,

      Nice to know the goodies of Domain Models have caught your attention. Whist it’s somewhat hard to get them up and running along with a decent mapping layer coupled to them, definitively the exercise is worth the hassle, specially when deploying large applications. Also, you raised an interesting question: while I wouldn’t say it’s actually easy to deal with all sort of joins, at least when appealing to plain data mapper injection, in general it’s possible to model them pretty accurately just by encapsulating query logic in a separated “relation” class, where its instances can be assigned to the fields of domain objects.
      This relation class behaves pretty much like a Virtual Proxy, which certainly knows how to create the joins, and additionally, can be clever enough for lazy-loading single or collections of domain objects from the database. Of course, if your domain model defines all sort of complex relationships, picking up a third-party library or framework that does the hard leg work for you is definitively the way to go.
      Thanks.

      Thanks.

  • Jiri Fornous

    Hello Alex, thanks for article. It’s always nice to see the same thing from different point of view.

    Doesn’t the way you use the Data mapper resulting in Anemic model? It seems to me you only renamed the service to Data mapper. The mapper fills the entity objects and stores them back. Isn’t the desired state to hide mapper behind the entity objects?

    • Alex Gervasio

      Hello Jiri,
      Well, I can see you’re also stepping hard on the DDD soil as well. I thought we were just a few mad guys enclosed in the attic :-). Now seriously, regarding your question, the data mappers are just there, doing what they’re meant to do: mediate neatly between the domain model and the data access layer, while keeping them isolated from each other. Not only the model is unaware of the storage, but it doesn’t even have a single clue about the mappers too. If its rather anaemic (which isn’t true, at least in this case), it’s because its design is horribly clunky in the first place, and certainly the mappers don’t have nothing to do with this.
      Also, notice the lower-level, close to the database API the mappers expose to client code, and hopefully you’ll catch in a snap why they just can’t be viewed as services. Usually, a service is placed on top of the model and can encapsulate pretty much anything you can think of, excepting domain logic of course (that would definitively degrade the model to an anaemic structure, by the way). What’s more, with a pinch of patience and willpower, you can even sit down a nice repository upon the mappers, which could fetch aggregate roots (AKA blog posts) from the storage. Still, a repository isn’t a service, as it doesn’t contain application logic, or in theory it shouldn’t.

      I hope that answer your questions.

  • Jiri Fornous

    Hi Alex,

    Ok, I agree the mapper is not the same as service in your case. But what I point at is the main goal of working with model. (It is also the point we have reached in our enterprise application) I feel the main goal is the separation of concern when developping in team. The model should give a presentation tier team work only on presentation layer communicate with the data (model) tier team through entity objects. Thus they are totaly separeted from the data layer. The example you have showed us forces the presentation team to know which data adapter to use and how to create the net of mappers. This should be the responsibilty of the model team and should be hidden to presentation team in my opinion.

    Sorry if it seems like I do philosophize :) (it’s far from it). We have large enterprise project with 10 years old roots. Because it was built on top of component framework instead of more often used MVC it tended to use something very similar to transaction scripts with anemic model. We worked a lot with model state which was dependent on position in routing system (DAG). This gave us the ability to everytime go back in DAG and always have proper state of model and it also let us use common subflows very easily. This connection of presentation tier with model was the main reason we used the anemic model.
    Now we are at the point we must separate teams and refactor whole application. So we study all possibilties and are trying to figure out the best way for our project.

    • Alex Gervasio

      Hey Jiri,
      To make things a bit clearer, the fact that I used just a few mappers to hydrate the model doesn’t mean that the presentation layer is aware of them, or even worse, that it should be forced to deal with them at some point. The bones of Domain Models fit very into with enterprise-level applications (petty much like yours, I guess), where the model and the DAL can be implemented in isolation from each other, thus evolving at different paces. In the middle of that schema, the mappers are responsible for interplaying with each layer, all while preserving that isolation, even at expenses of breaking model’s encapsulation.
      Now, shifting to your particular use case, if you already have a working model, even though I’m pretty clueless why it became anemic, or why it rotted to that state after all), and on the flip side, there’s a bunch of transactions scripts, most likely you’ll want to take the big plunge and migrate to a rich domain model/mapper approach. Hypothetically speaking of course, once the model is put in memory through one or more mappers, you can have it happily passed along onto the presentation layer for rendering, or anything else you want to do further down the road. In such a flexible, multi-tiered design, you can have separate teams putting their hands on the model and on the user interface; still, the latter doesn’t have to know anything about the mappers.
      I hope that helps you out to make the right choice for your project.

  • http://objectic.cc/ Niko Kivelä

    Great article, once again! Especially in articles like this it would actually be nice to see the actual object graph: class or sequence diagram. It helps people to understand the concept as a whole and you can sell them your ideas much better.

    About what @Jiri said: ” The model should give a presentation tier team work only on presentation layer communicate with the data (model) tier team through entity objects”. What I tend to do, is to build even lighter model fo presentation layer that that are populated (using parts of Model-View-Presentation) or adapted/proxyed/prototyped via/from domain models. Models presented in view are usually way more lighter that the actual domain models and domain models should never make any concessions towards view and vice versa. This makes presentation “unit-testable” and changes to domain models doesn’t directly affect view models. If the code should be compiled, it would make even more sense: why changing datamodel should cause view to be re-compiled?

    Some solutions might sound more bloat than others, but as you said: working with a large team with different tiers and skills, layering in the name of separation of concerns is way to go.

    • Alex Gervasio

      Agreed with your comment definitively. A UML diagram would certainly make a little bit easier to grasp the dependencies that exist between the involved classes, and how each object graph is created as well, specially from a factory’s perspective. The expressiveness of the code itself, however, is one of the best ways to catch how this happens, not to mention the fact that it shows in a nutshell why Dependency Injection is such a simple –yet powerful pattern.
      Also, I found interesting your concepts on multi-tiered design. You brought to the table a couple of nice things, like adapters and proxies. It’d be really good to see how you drop them within the boundaries of a service layer. Just on behalf of a didactical cause, of course :-)

  • http://www.icomefromthenet.net Lewis Dyer

    I’ve really enjoyed using auraphp marshal component https://github.com/auraphp/Aura.Marshal . I ported it back to php 5.2.*, only took half day.

    I’ve used the extension to avoid joins as much as possible. The component is a relation aware identity map. I load 100 uses into the map then run query to fetch 100 comments for those users, In 2 queries I’ve brought down just the data I need no wasteful joins.

    Less joins means less mappers to change when table schema get’s modified.

    When designing a domain model for my latest project I decide to try to separate as many concerns as possible. I generally ended up with the following structure.

    (1) Adapter , which had the CRUD Queries and any bulk actions like deletes and a generic query method that took a query object. Returned only data in an array

    (2) Query object, Sql Query builder has default methods like limit() and domain methods like filterByUser().

    (3) Repo, this object would hold my identity map, adapters and mappers and implement query methods. Hides the data source

    (4) Entity, which all derive from base type (the identity map see Marshal component above) related objects are accessed though the identity map and all properties of the entity hidden behind accessors and setters.

    (5)Mapper, which provided the actual mapping from entity to database representation and from database representation into php representation (convert timestamps and bool to correct php type).

    (6) Builder, which would map the php representation from the mapper into the domain entity and I find very useful for setting value types that require configuration for example a currency objects.

    (7) Service, I like having a domain to access relations but I still prefer to call a service and force an operation. I’m not a fan of patterns like the unit of work which manage persistence at the end of the request.

    • Alex Gervasio

      Personally I haven’t dug way too deep into Aura’s Marshal component, but it looks pretty good at first blush, considering the fact it’s a product of Paul Jones’ skills. Also, your application structure seems to be the right one, even though I definitively would put the mappers closer to the Data Access Layer, cause they’re part of the infrastructure. But, this is just a biased opinion, mostly based on what I can see in general from your layout.
      Thanks for the feedback.

  • José

    Gracias, pero ni un gráfico ni un diagrama descriptivo de apoyo.
    José
    Thanks, but neither a graph nor a descriptive diagram for support.
    Joe

    • Alex Gervasio

      As I said before, a complementary diagram would fit just well with the narratives and the code samples. Even though, the code is in general fairly self-explanatory, considering it just uses plain data mapper injection to construct a set of aggregate roots. If you’re familiar with basic OOP concepts, like Composition/Aggregation and Inheritance, the examples should be pretty easy to assimilate.
      Thanks for the feedback.

  • Charles

    I have found both these tutorials really great examples to work from. Coming from a PHP developer role, with in a web agency, it is most common to take an open source project and customize rather than building n tier architecture from the ground up.

    I am hoping there will be many more tutorials covering these kind of topics.

    • Alex Gervasio

      Hey Charles,
      Glad to read that. Well, it fact there will be (hopefully) a few more articles going alive discussing in depth some additional DDD-related topics, including the implementation of services and repositories, and how to put them to work nicely orchestrated in a multi-tiered design. So, feel free to stay around.

  • http://timoshenko.livejournal.com Igor Timoshenko

    Hi!

    Thank you for article! I have a question about relationships. Can you give an example how to implement `many-to-many` relationship?

    • Alex Gervasio

      Hi Igor,
      Well, dropping an example that shows how to perform CRUD operations on generic many-to-many relationships is quite a burdening, complex task, certainly out of the article’s scope. Nevertheless, if you just need to handle at a basic level just a few specific many-to-many relationships, you might want to implement some data mappers which first would pull in data from the corresponding linking tables, then create/hydrate the proper domain objects, and finally would pass them along onto the domain layer for further processing. The whole task wouldn’t be radically different from massaging traditional one-to-one/one-to-many relationships. In more complex use cases, though, you probably will want to resort to libraries like Doctrine, Aura Marshal, RedBeanPHP and the like.
      Thanks for the feedback.

      • http://timoshenko.livejournal.com Igor Timoshenko

        How about do `many-to-many` relationship like in Yii Framework? I mean add a param to the method `find` or `findAll` like `with` and define there the name of the model or something like this and after that build joins? Would it be correct?

        As I understood from the example above the `one-to-many` relationship between posts and comments is hard. If we have some part of the code which can build joins, it will be cool :)

        • Alex Gervasio

          To be frank, if you’ve already climbed the learning curve of Yii (or any other framework out there, of course) and want to use its ORM, it’s just fine. Though not a Yii user myself, I sniff it uses an Active Record implementation beneath the ORM. Rich domain models, on the flip side, are suitable to work with data mappers, and in simpler use cases, with Table Data Gateway. That’s why I put before on the list of possible candidates standalone libraries like Doctrine and RedBeanPHP, which do a neat work when it comes to managing complex relationships between domain objects. Rolling your own full-stacked data mapper-based ORM isn’t the most pragmatic approach to take by the way, and IMO should be only used in pretty simplistic situations. If your needs are more pushing than that, then pick up a package that does the leg work for you. Thanks!

  • Brandon Buster

    Unless I’m missing something, the constructor was never called for the UserMapper class.

    Also, it seems to me all these mapper classes could be handled by a single implementation with a few added conditional checks. Or is there a greater purpose to the multiple implementations?

    Thanks. Been learning a great deal from all your articles.

    • Alex Gervasio

      Hey Brandon,
      Glad the post has been instructive. It’s always nice to hear something like that. In regards to your question on the UserMapper, in fact it inherits the constructor from the abstract mapper, just because it doesn’t need to inject anything else. All it requires in order to pull in a few authors from the database is precisely a database adapter, which is nicely supplied by the corresponding parent (reusing implementation is sweet, isn’t it?). About the second question, well yep, in certain use cases it’s possible to define some sort of mapping Layer Supertype, which would encapsulate most of the logic required by most of the child mappers. In this case in particular, IMO that’s not very appealing, as each mapper does something different, not to mention the fact they consume different collaborators.
      I hope that helps. Thanks!

  • http://25tolife.net/ Steve

    Awesome article! Just starting out with learning ZF, had got to the point where “registering a user” needed to insert in the DB, but got perplexed as another tutorial I was following was using the DbTable-derived class for DB inserts, directly in the Controller, which seemed a bit strange. Your way of just passing the $_POST params into a new Model, passing that into the Mapper for storage, seems *much* more sensible. So, thanks very much!

    • Alex Gervasio

      Honestly, implementing a rich domain model is quite a complex task, where most of the times the constituent domain objects just don’t have a isomorphic relation with tables in the database. That’s exactly where the data mappers come into play, as they let you keep the model neatly isolated from the data access layer, while resolving internally the aforementioned impedance mismatch. Thanks for the insights.

      • http://25tolife.net/ Steve Yet Again

        Can you offer any pointers on how one would achieve this nice Data Mapper structure inside ZF2? Your PdoAdapter seems like it’s doing a similar job to what ZendDbTableGateway does, but I’m really struggling with figuring out how to utilise TableGateway (or something else in Zend) within your DataMapper structures in a way which *doesn’t* need a new extension of TableGateway writing for every single entity. Any points would be amazing! Thanks again :)

        • Alex Gervasio

          Steve
          There’re several ways to implement a data mapper, according to specific needs. You can design it as a unidirectional structure, a bidirectional one (when it has the nice ability for persisting domain objects and reconstituting them back for being consumed easily within the domain model) and even as a relational, full-fledged monster, exposing all the bells and whistles of Doctrine. Honestly, it’s rather hard to find real world implementations of functional bidirectional mappers that offer a “pretty seamless” integration with ZF asides from Doctrine. Even though, you may want to check here http://site.svn.dasprids.de/trunk/ the nice work Ben Scholzen has done by integrating on his blog’s source code data mappers, services, dependency injection containers and the like with ZF1. There’s a lot of juicy things to digest there, specially how to integrate the data mappers with Zend_Db_Adapter_Abstract. I hope that points you in the right direction. Thanks!

  • Dmytry

    Hi, Alex, first of all thanks for the articles which are really educational. Couple of days ago I’ve read your series of articles on devshed – the one about creating units of work via the factory, which is passed to the entity manager. Unfortunately the series lack the final part, where everything is put together, so I would really appreciate if you provided the link if it have been posted somewhere else.

    • Alex Gervasio

      Hey Dmytry,
      Sorry for the delay in getting back to you. Yes, for some reason the series on devshed on the UoW wasn’t published in its entirety. Considering that, you may want to check another article I wrote on the pattern right here at PHPMaster http://phpmaster.com/implementing-a-unit-of-work/
      I hope that helps.

      • Dmytry

        Thanks, Alex, I’ve read pretty much everything you’ve posted on DDD on this site, as I consider it a great way not only to learn DDD itself but also to really understand some OOP techniques and principles. As a result I have some questions:
        1) Database adapter – what are the best practices ? I mean if you have something like unit of work, you create an instance of db adapter at some point, pass it to unit of work and forget about it, letting UoW do all the work. But what if the architecture is simpler and you just have several entities with corresponding data mappers, how do you make only one instance of db adapter ? Make it a singleton ? Put it in some service container (which implements registry pattern) ? Something else ?
        2) Is it a common practice to separate entity fields that have db representation from ones that doesn’t ? Let’s say I use a data mapper to get User entity from persistence , which not only has fields ‘id’ and ‘name’, stored in db, but also field ‘comments’ that is a result of some comments data mapper work and doesn’t have corresponding db field. So should I make 2 arrays in my User entity – the one for native db fields and the second for other, so that when I update User it doesn’t try to update not existing ‘comments’ field in db ?
        Thanks in advance.

  • Stan

    Your tutorials are awesome.
    Is there a reason why you don’t prefix your protected variables names with an underscore ? Do you think it’s a useless naming convention ?

    • http://zaemis.blogspot.com Timothy Boronczyk

      Different coding standards dictate different conventions. It’s not important exactly what those convents are, rather that you are consistent when you apply them to your code.

  • Caio

    It really helped me. Thank you, thank you. THANK YOU SOOO MUUUCH !

  • Sankalp bohra

    Hi, I really like your articles as well appreciate your efforts for this. I would can you provide whole of this code on github or somewhere else ?

  • Bart

    Great article.
    I have one question however.
    In your prepare function you connect to the database with $this->connect();
    But let’s say there is an error, the connection is never closed?
    Or do you just put disconnect() always at the end of the script?