The Domain Model should not depend on anything else; it is the core of an application. Classes should not extend or implement anything extraneous. I do not want User extends Doctrine_Record. I want User.Sorry to stress you, but this is one of the points of DDD and the one that gives advantages even applied in other architectures. The persistence problem can be solved by generic object-relational mappers which will act as the bridge between entities and the database used for persistence. But where do we find a generic Orm?
In php, no real generic Orms existed until 2009, since Zend_Db, Doctrine 1, Propel etc. are all implementations of the Active Record (or similar data gateway) pattern, requiring for example all your User and Post classes to subclass a base record. The only way to obtain a persistence-agnostic model was manual implementation of all the mapper classes, which translate between database rows and object graphs. When you are managing more than a few different entity classes the problem become quickly intractable.
The Data Mapper pattern describes exactly a generic Orm, but it is an even more general concept in the sense that the mapping is not limited to a relational database like MySql or Sql Server. You can write a Data Mapper to store your objects in plain text files or document-oriented databases if you want.
In the last summer, I encountered two in-development solutions to solve the persistence agnosticism problem: Doctrine 2 and Zend_Entity. They are implementations of the Data Mapper pattern, with reference to the Jpa specification and a similar Api. I learned that the Java guys had implemented a real Data Mapper years ago: Hibernate. Jpa is only a specification extracted as a subset of Hibernate, and it is an additional layer abstraction that decouples your mapping code (annotations or xml files) from the particular Orm.
Anyway, I contributed to the lazy loading capabilities of Doctrine 2 with php code and to Zend_Entity with some small patches before its discontinuance. I am currently waiting for a stable version of Doctrine 2 to integrate it in NakedPhp, only because I am not worrying about persistence for now. It is the power of the Data Mapper approach that decouples my work from a specific storage such as a relational database.
Fast forward to today, and Doctrine 2 is in alpha for being thoroughly tested. Zend_Entity has been dropped instead, in favor of Doctrine 2 integration in the Zend Framework. It is not useful to maintain two different code bases, with the same Api transposed from Jpa, which do the same persistence-related dirty work and developed by the same people. It's just a waste of the contributor's time.
Thus, Doctrine 2 is going to become the first production-ready Orm for php and to be favored with seamless integration in both Zend Framework and Symfony. If you have not yet tried it, you may want to give it a shot.
If you feel like helping with the integration, which involves Zend_Tool components for generation and Zend_Application resources, join the zf-doctrine mailing list. The integration also comprehends Doctrine 1 since Doctrine 2 requires php 5.3 and its adoption by hosting companies will be gradual.
The adoption of the 2.x branch, when ready, would give your design the freedom from the database you want. Doctrine 2 is for php the greatest thing since sliced bread.
Thanks for the kudos :)
ReplyDeleteIt should be noted, however, that ActiveRecord is far from useless. It can work very well for many simple applications where the domain model does not require any reuse and is pretty simple anyway. ActiveRecord works for many applications. Just not when you want a reusable, easily testable and persistence-independant domain model.
Needless to say I prefer data mappers but it just has to be said that there is no "one true way" for object persistence. Just many different options.
While we're at it, we do have plans for an object-document mapping approach with a very similar API to the Doctrine 2 ORM, the same different mapping options (annotations, xml, yaml, ...) etc.
If you or anyone else is interested in contributing, let us (the Doctrine guys) know.
I have experienced many Active Record applications where, sooner or later, classes started to incorporate logic that should be tested. :)
ReplyDeleteI am not much experienced with document-oriented database but it's in my interest to continue contributing to Doctrine 2 in the future.
Fantastic explanation, Giorgio.
ReplyDeleteI am really looking forward for mainstream PHP 5.3 and Doctrine 2.0 adoption. I am not worried about server infrastructure since I host my applications on own servers. But I would like to use Zend Framework and few libraries that aren't fully tested with PHP 5.3 yet.
On my watch list the top items are
1. Zend Framework 2.0
2. PEAR 2.0
3. Doctrine 2.0
Very web 2.0 list :)
ReplyDeleteHowever, Doctrine 2 and Pear 2 are in alpha, while ZF 2 does not exist yet.
Noted Giorgio. Thanks for posting. I'm downloading Doctrine 2.0 now - great to hear that Doctrine 2 integration is at hand with zf.
ReplyDeleteHave you looked at Torpor PHP? It's new project that seems to share similar concepts. Author describes this project in last PHP Abstract Podcast.
ReplyDeleteI looked at Torpor, but it seems that it does not implement a DataMapper pattern but dynamic Active Records and a lot of magic.
ReplyDeleteIn php, no real generic Orms existed until 2009
ReplyDeleteActually, after quick research I've found two PHP projects implementing data mapper pattern (at least naming this pattern):
phpDataMapper
Rapid Data Mapper
Additionally I'm not sure about frameworks incorporating theirs own ORM layers, maybe there are some using Data Mapper as well? The only one I spotted is Data Mapper plugin for CodeIgniter.
Haven't checked if they're good or not, just wanted to mention those for completeness.