Chinaunix首页 | 论坛 | 博客
  • 博客访问: 589932
  • 博文数量: 207
  • 博客积分: 10128
  • 博客等级: 上将
  • 技术积分: 2440
  • 用 户 组: 普通用户
  • 注册时间: 2004-10-10 21:40
文章分类

全部博文(207)

文章存档

2009年(200)

2008年(7)

我的朋友

分类:

2009-04-06 16:38:10

Some of the Domain-driven design concepts explained in my previous posts are applied in this sample application. I’m also going to use the Zend Framework infrastructure to speed up some development tasks.

Directory Structure

app/     -> Application and UI Layers
domain/  -> Domain Layer
lib/     -> Infrastructure Layer

Application and UI Layers

app/
    controllers/
        UserController.php
    views/
        layouts/
        scripts/

Domain Layer

This layer should be well separated from the other layers and it should have few dependencies on the framework(s) you are using. I’m going to group all the classes and interfaces under the namespace “Project” and add it to my include path:

domain/
    Project/
        Dao/
            Db/
                IUser.php
                IUserProfile.php
                User.php
                UserProfile.php
        Model/
            User/
                IRepository.php
                Repository.php
            User.php
            UserProfile.php
            Users.php

Infrastructure Layer

This layer acts as a supporting library for all the other layers:

lib/
    Zend/
        ...
    Zf/
        Persistence/
            Db/
                Adapter.php
                Exception.php
                Replicated.php
        Domain/
            Collection.php
            Entity.php
            Exception.php
            Repository.php

User Entity

The User and UserProfile objects have a one-to-one relationship and form an Aggregate. An Aggregate is as a collection of related objects that have references between each other. Within an Aggregate there’s always an Aggregate Root (parent Entity), in this case User:

class Project_Model_User extends Zf_Domain_Entity
{
    /* SELECT id, name FROM user WHERE id = ? */
    private $properties = array(
        'id'      => null,
        'name'    => null
    );

    /* @var Project_Model_UserProfile */
    private $profile;

    public function __construct($array)
    {
        $this->populate($array);
    }
}

abstract class Zf_Domain_Entity
{
    private $properties = array();

    public function __call($method, $args) {}
    public function __set($key, $value) {}
    public function __get($key) {}
    public function __isset($key) {}
    public function __unset($key) {}
    public function populate($values) {}
    public function hasDependency($property) {}
    public function setDependency($property, $object) {}
    public function getDependency($property) {}
}

Usage:

$array = array('id'=>1, 'name'=>'Federico');
$user = new Project_Model_User($array);
$user->setProfile($profile);

echo $user->getId();   // Outputs 1
echo $user->getName(); // Outputs Federico

Users Collection

A collection is simply an object that groups multiple elements into a single unit.

class Project_Model_Users extends Zf_Domain_Collection
{
    public function __construct($users)
    {
        foreach ($users as $user) {
            if (!($user instanceof Project_Model_User)) {
                throw new Zf_Domain_Exception(...);
            }
            $this->append($user);
        }
    }
}

abstract class Zf_Domain_Collection implements Countable, Iterator
{
    ...
}

User DAO

The UserDAO class allows data access mechanisms to change independently of the code that uses the data:

class Project_Dao_Db_User extends Zf_Persistence_Db_Adapter
{
    public function find($id)
    {
        $db = $this->getAdapter();
        $query = $db->select();
        $query->from('user');
        $query->where('id = ?', $id);
        $result = $db->fetchRow($query);

        return $result;
    }

    public function findAll()
    {
        ...
        return $resultSet;
    }
}

User Repository

A Repository is basically a collection of Aggregate Roots. Collections are used to store, retrieve and manipulate Entities and Value objects, however, object management is beyond the scope of this post.

The UserRepository object injects dependencies on demand, making the instantiation process inexpensive. A caller method is responsible for dynamically creating the setter and getter methods. You can easily mock objects by passing a custom config array via the constructor.

class Project_Model_User_Repository extends Zf_Domain_Repository
{
    /* @var Project_Dao_Db_User */
    private $userDao;

    /* @var Project_Dao_Db_UserProfile */
    private $userProfileDao;

    /* @var array IoC Spec */
    private $inject = array(
        'userDao'        => 'Project_Dao_Db_User',
        'userProfileDao' => 'Project_Dao_Db_UserProfile'
    );

    public function getUserById($id)
    {
        $row = $this->getUserDao()->find($id);
        $user = new Project_Model_User($row);

        $row = $this->getUserProfileDao()->findByUserId($id);
        $profile = new Project_Model_Profile($row);
        $user->setProfile($profile);

        return $user;
    }

    public function getUsers()
    {
        $users = array();
        $rows = $this->getUserDao()->findAll();
        foreach ($rows as $row) {
            $users[] = new Project_Model_User($row);
        }

        return new Project_Model_Users($users);
    }
}

abstract class Zf_Domain_Repository
{
    ...
    public function __call($method, $arguments)
    {
     $property = lcfirst(substr($method, 3));
        if (!property_exists($property)) {
            throw new Zf_Domain_Exception(...);
        }
        if (null === $this->$property
            && array_key_exists($property, $this->inject)) {
         $this->$property = new $this->inject[$property];
        }
        return $this->$property;
    }
    ...
}

Usage:

$repo = new Project_Model_User_Repository();
$user = $repo->getUserById(1);
$profile = $user->getProfile();
$users = $repo->getUsers();

Links

If you’re interested in learning more about Domain-driven design, I recommend the following articles:

阅读(857) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~