Domain-Driven Design with Zend Framework
Part 1: Domain-Driven Design and MVC Architectures
Part 2: Domain-Driven Design: Data Access Strategies
Part 3: Domain-Driven Design: The Repository
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/
Db/
Adapter.php
Exception.php
ReplicationAdapter.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
{
/* @var int */
private $id;
/* @var string */
private $name;
/* @var Project_Model_UserProfile */
private $profile;
public function __construct($properties)
{
$this->define($properties);
}
}
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);
}
}
}
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_Db_Adapter
{
public function find($id)
{
$db = $this->getConnection();
$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);
}
}
Usage:
$repo = new Project_Model_User_Repository(); $user = $repo->getUserById(1); $profile = $user->getProfile(); $users = $repo->getUsers();
Source Code
http://fedecarg.com/projects/show/zfdomain
http://fedecarg.com/projects/show/zfreplicationadapter
Links
If you’re interested in learning more about Domain-driven design, I recommend the following articles:
Domain Driven Design and Development In Practice
Domain-Driven Design in an Evolving Architecture
I think this is more or less what Doctrine 2 will be, I hope.
Giorgio Sironi
March 22, 2009 at 4:30 pm
Is there any reason why the “entities” need extend a class? The whole movement in Java was to turn away from entity beans to using POJOs. This also makes your business objects more portable. They should have *no* dependency on anything. The responsibility of “hydrating” those objects and persisting them should be the responsibility of a third part transparent object. This is the way its done in EJB3. Unfortunately, in the PHP world, nothing like this exists. The closest things are the Outlet and Repose projects, which don’t seem to be moving very quickly, and Doctrine 2.0 which doesn’t exists yet.
Avi
March 22, 2009 at 6:41 pm
@Avi Yes, I agree with you, but PHP doesn’t support annotations, such as Configurable, Entity and OneToOne. I’m simulating marker annotations using OO techniques. You could, however, use a DI container, add EntityManager and EntityManagerFactory, and create your own persistence package. I believe Symfony 2 is going that direction. The combination of Doctrine and DDD makes DAO’s redundant.
Federico
March 22, 2009 at 7:52 pm
@Avi: Well, Doctrine 2 is currently in development:
http://svn.doctrine-project.org/trunk/
and it requires php 5.3… I think we’ll view it together Zend Framework 2.0…
Giorgio Sironi
March 22, 2009 at 9:47 pm
Great article, I am implementing something similar with zend framework and doctrine..
Orhan SAGLAM
March 25, 2009 at 12:14 am
In your final example of the User Repository, getUserDao() and getUserProfileDao() are handled by the __call() function.
But where do you inject the actual DB adapter into the UserDao and UserProfileDao?
Matt
April 1, 2009 at 7:17 pm
I’m extending Zf_Persistence_Db_Adapter and getting the adapter from the registry. However, using Zend_Registry is not the best solution, as it introduces a hidden dependency.
Federico
April 1, 2009 at 10:48 pm
Thanks for the tutorial series.
One thing that confuses me in this example is the purpose of abstracting the data layer.
I.e. Why is it good to do it?
Lets say , for example, that you suddenly decide to add a new property to Project_Model_User. You are going to have to also add a new column to the db table.
Or, say, you come to realise that you have three types of user and hence need to make the user ID referenced by two fields UserTypeId+UserId to ensure they are unique within the app.
Or, say you suddenly decide to use xml to persist the data – the model and app still have to change to accommodate.
If a change in the db leads to a change in the model classes and a change in the model classes leads to a change in the db/persistance layer, why are we so desperate to keep them so separated?
ronny stalker
April 3, 2009 at 9:14 am
Federico,
Thanks for the explanation great series btw. I am learning a lot.
Another question
You give the example of:
$repo = new Project_Model_User_Repository();
$user = $repo->getUserById(1);
$profile = $user->getProfile();
$users = $repo->getUsers();
for retrieving a single user and a collection of users.
But could you show an example of how you would use your current framework to to create a new user or delete a user?
Would you instantiate a new user entity and populate it and pass it to createUser() on the UserRepository, or would you pass a array of new user data to createUser() on the UserRepository?
Matt
April 3, 2009 at 3:23 pm
Hi Ronny,
When you abstract you do it to reduce details so that other developers can focus on a few concepts at a time. Keep in mind that software architectures are constantly evolving and changing. I guess abstraction helps deal with those changes in a more efficient way.
Federico
April 3, 2009 at 7:39 pm
Hi Matt,
I would have an addUser() method in the Repository and a save() or saveFromArray() method in the DAO:
$array = array(‘name’=>’Jim’);
$user = new Project_Model_User($array);
$repo = new Project_Model_User_Repository();
$repo->addUser($user);
And use the Repository to pass the data to the DAO:
$this->getUserDao()->save($user);
$this->getUserDao()->saveFromArray($array);
Federico
April 3, 2009 at 7:40 pm
> If a change in the db leads to a change in the model classes and a change in the model classes leads to a change in the db/persistance layer, why are we so desperate to keep them so separated?
Hi Ronny, I’ve just realized that your question remained unanswered :)
If you remove the properties field from the Project_Model_User class, then you are not defining any class properties, and you are free to pass n amount of array elements via the constructor.
Federico
April 7, 2009 at 12:59 pm
> Hi Matt,
I would have an addUser() method in the Repository and a save() or saveFromArray() method in the DAO:
…..
Hey Federico,
With this method of adding and saving users. Where do propose to handle input validation? (ie. “This username already exists” or “Invalid email adddress”).
Within the Project_Model_User or Project_Model_User_Repository?
Matt
April 8, 2009 at 4:49 pm
Any chance of you writing a beginner level tutorials about domain driven design that doesn’t require the zend framework? Not suggesting any particular framework but I’ve read all your articles and many others and I still can’t fully grasp it all (and zend framework doesn’t work on my ancient servers). I’d like to see something that goes into the core concepts using generic and easily understandable code that even a novice can then take and re-implement in their framework of choice.
menoknow
April 11, 2009 at 2:19 am
[...] Domain-Driven Design with Zend Framework [...]
An Alternative to Zend_Controller: The Model « fede.carg ( blog )
April 13, 2009 at 2:46 pm
Thanks for your answer Federico.
Since writing those previous questions I have read the Domain Driven Design Book (Eric Evans). Now, when I come back to this tutorial series I’m reading it in a whole new light and these explanations are even more useful as i try to carve out my fist DDD bits of PHP code. So, thanks for pointing me in the direction of DDD- its a revelation.
Amongst that, gushing, compliment is the answer menoknow’s question – read that DDD book for good intro – its worth it.
ronny stalker
May 15, 2009 at 5:11 am
Hi Federico,
In your example you have a profile Object stored in the User Aggregate. As I understand it, you are currently always loading the profile data in the user object when you retreive a user object. Suppose this User Aggregate would extend and thus, would have extra entity’s/value objects related to it. In this example you would always load all the data for the User Aggregate but this seems not the best solution because you will create overhead (i.e. extra query’s to a database for example) where the data you are querying is not necesarry.
It seems more logical to just load the “basic” data in the user aggregate and only load the extra data when it’s necesarry (i.e. only load the profile data when you actually call it $user->getProfile()). However when I follow this approach I would add something like this to the getProfile method:
if(null == ($this->_profile)) {
//retreive data and store it in profile field
}
return $this_profile;
This implementation however is not valid because you would couple a db class or a repository to the User class.
How would you solve this issue, or am I mistaking on this one?
Bert
May 16, 2009 at 3:38 pm
Hi Federico,
First of all, your tutorials provide great insight into Domain Driven Design and Thanks.
I am also interesting in finding out how to implement what Bert has mentioned above.
Andho
May 18, 2009 at 8:34 am
Yes Bert, that’s right. Zf_Domain_Entity is responsible for returning a single instance of the profile object.
Line 130:
http://svn.fedecarg.com/repo/Zf/Domain/Entity.php
Federico
May 18, 2009 at 10:57 am
Federico,
I think we are talking about two different things. The profile is indeed an entitiy but that was not really my question.
Suppose the Person Aggregate would have a Profile entity, an Address Entity, … and some other stuff related to the Person Aggregate. How would you manage the loading of those objects.
Would you load all the entities of the Aggregate at once (so in fact when you retreive the aggregate from the repository, you load all the data). This implementation seems to have lots of overhead when you only need one field from the root Aggregate for example.
Another implementation could be that you only initiate the main properties of the root Aggregate (=Person) and that you would only load the entities like Profile or Address when you need them (so in fact you would lazy load these entities with a proxy pattern for example).
What is your opinion on this
Bert
May 18, 2009 at 12:13 pm
Gotcha. I guess that, if you are working with DAOs, there are different ways of doing it. You already mentioned 2 of them I think. You could also have a repository that represents the relationship between both entities, for example, UserProfileRepository. And then use UserProfileDAO to query the database, join the User and Profile tables and return the data to the repository so it can populate the User and Profile objects.
I don’t know if this is allowed in DDD, but I haven’t develop a Repository that manages this complexity for me yet.
Federico
May 18, 2009 at 4:37 pm
Federico,
As far as I know, only Aggregate Root is recomended to have a Repository. I’m really stuck with such implementation details. DDD concepts are obviosly interesting, but I’ve lots of misunderstandings relative to implementation :\
Roman
October 15, 2009 at 7:24 am
[...] Read this article: Domain-Driven Design with Zend Framework « Federico Cargnelutti [...]
Domain-Driven Design with Zend Framework « Federico Cargnelutti | My Web Development Bookmarks
October 22, 2009 at 4:23 am
[...] Por otra parte los mappers heredarán de Zend_Mapper. A esta nueva orientación se le denomina Domain Driven Design (DDD). Estoy siguiendo el tema con este libro y la verdad es que todo tiene muy buena pinta, aunque [...]
DataMapper pattern: una mirada hacia el futuro « Olagato’s blog
November 3, 2009 at 10:28 am
Hi,
I’m confused about domain and service, can someone provide a sample code for domain and service in action?
Thanks
Juan
February 6, 2010 at 12:41 am
Repositories are not part of the Service Layer, they are part of the Domain Layer. The Repository provides a higher level of data manipulation, and acts as a link between the Domain and Persistence Layers (Entity – Repository – DAO).
Service classes are not part of the Domain or Persistence Layers and therefore do not provide methods to access data, such as finders. Finders are usually found in the Repository.
Federico
February 8, 2010 at 10:20 am