Archive for the ‘Software Architecture’ Category
Implementing Dynamic Finders and Parsing Method Expressions
Most ORMs support the concept of dynamic finders. A dynamic finder looks like a normal method invocation, but the method itself doesn’t exist, instead, it’s generated dynamically and processed via another method at runtime.
A good example of this is Ruby. When you invoke a method that doesn’t exist, it raises a NoMethodError exception, unless you define “method_missing”. Rails ActiveRecord::Base class implements some of its magic thanks to this method. For example, find_by_title(title) and find_by_title_and_date(title, date) are turned into:
find(:first, :conditions => ["title = ?", title]) find(:first, :conditions => ["title = ? AND date = ?", title, date])
What’s nice about Ruby is that the language allows you to define methods dynamically using the “define_method” method. That’s how Rails defines each dynamic finder in the class after it is first invoked, so that future attempts to use it do not run through the “method_missing” method.
Method Expressions
GORM, Grails ORM library, introduces the concept of dynamic method expressions. A method expression is made up of the prefix such as “findBy” followed by an expression that combines one or more properties. Grails takes advantage of Groovy features to provide dynamic methods:
findByTitle("Example")
findByTitleLike("Exa%")
Method expressions can also use a boolean operator to combine two criteria:
findAllByTitleLikeAndDateGreaterThan("Exampl%", '2010-03-23')
In this case we are using AND in the middle of the query to make sure both conditions are satisfied, but you could equally use OR:
findAllByTitleLikeOrDateGreaterThan("Exampl%", '2010-03-23')
Parsing Method Expressions
MethodExpressionParser is a PHP library for parsing method expressions. It’s designed to quickly and easily parse method expressions and construct conditions based on attribute names and arguments.
Description
[finderMethod]([attribute][expression][logicalOperator])?[attribute][expression]
Expressions
- LessThan: Less than the given value
- LessThanEquals: Less than or equal a give value
- GreaterThan: Greater than a given value
- GreaterThanEquals: Greater than or equal a given value
- Like: Equivalent to a SQL like expression
- NotEqual: Negates equality
- IsNotNull: Not a null value (doesn’t require an argument)
- IsNull: Is a null value (doesn’t require an argument)
Examples
findByTitleAndDate('Example', date('Y-m-d'));
SELECT * FROM book WHERE title = ? AND date = ?
findByTitleOrDate('Example', date('Y-m-d'))
SELECT * FROM book WHERE title = ? OR date = ?
findByPublisherOrTitleAndDate('Name', 'Example', date('Y-m-d'))
SELECT * FROM book WHERE publisher = ? OR (title = ? AND date = ?)
findByPublisherInAndTitle(array('Name1', 'Name2'), 'Example')
SELECT * FROM book WHERE publisher IN (?, ?) AND date = ?
findByTitleLikeAndDateNotNull('Examp%')
SELECT * FROM book WHERE title LIKE ? AND date NOT NULL
findByIdOrTitleAndDateNotNull(1, 'Example')
SELECT * FROM book WHERE (id = ?) OR (title = ? AND date NOT NULL)
Example 1:
findByTitleLikeAndDateNotNull('Examp%');
Outputs:
array
0 =>
array
0 =>
array
'attribute' => string 'title'
'expression' => string 'Like'
'format' => string '%s LIKE ?'
'placeholders' => int 1
'argument' => string 'Examp%'
1 =>
array
'attribute' => string 'date'
'expression' => string 'NotNull'
'format' => string '%s IS NOT NULL'
'placeholders' => int 0
'argument' => null
Example 2:
findByTitleAndPublisherNameOrTitleAndPublisherName('Title', 'a', 'Title', 'b');
Outputs:
array
0 =>
array
0 =>
array
'attribute' => string 'title'
'expression' => string 'Equals'
'format' => string '%s = ?'
'placeholders' => int 1
'argument' => string 'Title'
1 =>
array
'attribute' => string 'publisher_name'
'expression' => string 'Equals'
'format' => string '%s = ?'
'placeholders' => int 1
'argument' => string 'a'
1 =>
array
0 =>
array
'attribute' => string 'title'
'expression' => string 'Equals'
'format' => string '%s = ?'
'placeholders' => int 1
'argument' => string 'Title'
1 =>
array
'attribute' => string 'publisher_name'
'expression' => string 'Equals'
'format' => string '%s = ?'
'placeholders' => int 1
'argument' => string 'b'
See more examples: Project Wiki
Usage
class EntityRepository
{
private $methodExpressionParser;
// Return a single instance of MethodExpressionParser
public function getMethodExpressionParser() {
}
// Finder methods
public function findBy($conditions) {
var_dump($conditions);
}
public function findAllBy($conditions) {
var_dump($conditions);
}
// Invoke finder methods
public function __call($method, $args) {
if ('f' === $method{0}) {
try {
$result = $this->getMethodExpressionParser()->parse($method, $args);
$finderMethod = key($result);
$conditions = $result[$finderMethod];
} catch (MethodExpressionParserException $e) {
$message = sprintf('%s: %s()', $e->getMessage(), $method);
throw new EntityRepositoryException($message);
}
return $this->$finderMethod($conditions);
}
$message = 'Invalid method call: ' . __METHOD__;
throw new BadMethodCallException($message);
}
}
Performance
PHP doesn’t allow you to define methods dynamically, this means that every time you invoke a finder method the parser has to search, extract and map all the attribute names and expressions. To avoid introducing this performance overhead you can cache the attribute names. For example:
class EntityRepository
{
private $methodExpressionParser;
private $classMetadata;
// Return a single instance of MethodExpressionParser
public function getMethodExpressionParser() {
}
// Return a single instance of ClassMetadata
public function getClassMetadata() {
}
// Invoke finder methods
public function __call($method, $args) {
if ('f' === $method{0}) {
$parser = $this->getMethodExpressionParser();
$classMetadata = $this->getClassMetadata();
try {
$finderMethod = $parser->determineFinderMethod($method);
if ($classMetadata->hasMissingMethod($method)) {
$attributes = $classMetadata->getMethodAttributes($method);
$conditions = $parser->map($args, $attributes);
} else {
$expressions = substr($method, strlen($finderMethod));
$attributes = $this->extractAttributeNames($expressions);
$conditions = $parser->map($args, $attributes);
$classMetadata->setMethodAttributes($method, $attributes);
}
} catch (MethodExpressionParserException $e) {
$message = sprintf('%s: %s()', $e->getMessage(), $method);
throw new EntityRepositoryException($message);
}
return $this->$finderMethod($conditions);
}
$message = 'Invalid method call: ' . __METHOD__;
throw new BadMethodCallException($message);
}
}
The Expression objects are lazy-loaded, depending on the expressions found in the method name.
Extensibility
The MethodExpressionParser class was designed with extensibility in mind, allowing you to add new Expressions to the library.
abstract class Expression {
}
class EqualsExpression extends Expression {
}
Source Code
http://fedecarg.com/repositories/show/expressionparser
Check out the current development trunk with:
$ svn checkout http://svn.fedecarg.com/repo/Zf/Orm
Zend Framework DAL: DAOs and DataMappers
A Data Access Layer (DAL) is the layer of your application that provides simplified access to data stored in persistent storage of some kind. For example, the DAL might return a reference to an object complete with its attributes instead of a row of fields from a database table.
A Data Access Objects (DAO) is used to abstract and encapsulate all access to the data source. The DAO manages the connection with the data source to obtain and store data. Also, it implements the access mechanism required to work with the data source. The data source could be a persistent store like a database, a file or a Web service.
And finally, the DataMapper pattern is used to move data between the object and a database while keeping them independent of each other. The DataMapper main responsibility is to transfer data between the two and also to isolate them from each other.
Here’s an example of the DataMapper pattern:
Database Table Structure
CREATE TABLE `user` ( `id` int(11) NOT NULL auto_increment, `first_name` varchar(100) NOT NULL, `last_name` varchar(100) NOT NULL, PRIMARY KEY (`id`) )
The User DAO
The DAO pattern provides a simple, consistent API for data access that does not require knowledge of an ORM interface. DAO does not just apply to simple mappings of one object to one relational table, but also allows complex queries to be performed and allows for stored procedures and database views to be mapped into data structures.
A typical DAO design pattern interface is shown below:
interface UserDao
{
public function fetchRow($id);
public function fetchAll();
public function insert($data);
public function update($id, $data);
public function delete($id);
}
class UserDatabaseDao implements UserDao
{
public function fetchRow($id)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('slave');
$query = $db->select()->from('user')->where('id = ?', $id);
return $db->fetchRow($query);
}
public function fetchAll()
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('slave');
$query = $db->select()->from('user');
return $db->fetchAll($query);
}
public function insert($data)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('master');
$db->insert('user', $data);
return $db->lastInsertId();
}
public function update($id, $data)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('master');
$condition = $db->quoteInto('id = ?', $id);
return $db->update('user', $data, $condition);
}
public function delete($id)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource();
$db = $dataSource->getConnection('master');
$condition = $db->quoteInto('id = ?', $id);
return $db->delete('user', $condition);
}
}
The User Entity
An Entity is anything that has continuity through a life cycle and distinctions independent of attributes that are important to the application’s user.
class User
{
protected $id;
protected $firstName;
protected $lastName;
public function setId() {
}
public function getId() {
}
public function setFirstName() {
}
public function getFirstName() {
}
public function setLastName() {
}
public function getLastName() {
}
public function toArray() {
}
}
The User DataMapper
class UserDataMapper extends Zf_Orm_DataMapper
{
public function __construct()
{
$this->setMap(
array(
'id' => 'id',
'first_name' => 'firstName',
'last_name' => 'lastName'
)
);
}
}
Source Code: http://fedecarg.com/repositories/show/datamapper
The User Repository
Repositories play an important part in DDD, they speak the language of the domain and act as mediators between the domain and data mapping layers. They provide a common language to all team members by translating technical terminology into business terminology.
Lets create a UserRepository class to isolate the domain object from details of the DAO:
class UserRepository
{
private $databaseDao;
public funciton setDatabaseDao(UserDao $dao)
{
$this->databaseDao = $dao;
}
public function getDatabaseDao()
{
if (null === $this->databaseDao) {
$this->setDatabaseDao(new UserDatabaseDao());
}
return $this->databaseDao;
}
public function find($id)
{
$row = $this->getDatabaseDao()->fetchRow($id);
$mapper = new UserDataMapper();
$user = $mapper->assign(new User(), $row);
return $user;
}
}
The User Controller
class UserController extends Zend_Controller_Action
{
public function viewAction()
{
$userRepository = new UserRepository();
$user = $userRepository->find($this->_getParam('id'));
if ($user instanceof User) {
$id = $user->getId();
$firstName = $user->getFirstName();
$lastName = $user->getLastName();
// get an array of key value pairs
$row = $user->toArray();
}
}
}
That’s all, I hope you’ve found this post useful.
Towards Community Cloud Computing
Cloud Computing is rising fast, with its data centers growing at an unprecedented rate. However, this has come with concerns of privacy, efficiency at the expense of resilience, and environmental sustainability, because of the dependence on Cloud vendors such as Google, Amazon, and Microsoft. Community Cloud Computing makes use of the principles of Digital Ecosystems to provide a paradigm for Clouds in the community, offering an alternative architecture for the use cases of Cloud Computing. It is more technically challenging to deal with issues of distributed computing, such as latency, differential resource management, and additional security requirements. However, these are not insurmountable challenges, and with the need to retain control over our digital lives and the potential environmental consequences, it is a challenge we must pursue.
Towards Community Cloud Computing (Visit Site | Download PDF)
Java, C, Python and nested loops
Java has no goto statement, to break or continue multiple-nested loop or switch constructs, Java programmers place labels on loop and switch constructs, and then break out of or continue to the block named by the label. The following example shows how to use java break statement to terminate the labeled loop:
public class BreakLabel
{
public static void main(String[] args)
{
int[][] array = new int[][]{{1,2,3,4},{10,20,30,40}};
boolean found = false;
System.out.println("Searching 30 in two dimensional int array");
Outer:
for (int intOuter = 0; intOuter < array.length ; intOuter++) {
Inner:
for (int intInner = 0; intInner < array[intOuter].length; intInner++) {
if (array[intOuter][intInner] == 30) {
found = true;
break Outer;
}
}
}
if (found == true) {
System.out.println("30 found in the array");
} else {
System.out.println("30 not found in the array");
}
}
}
Use of labeled blocks in Java leads to considerable simplification in programming effort and a major reduction in maintenance.
On the other hand, the C continue statement can only continue the immediately enclosing block; to continue or exit outer blocks, programmers have traditionally either used auxiliary Boolean variables whose only purpose is to determine if the outer block is to be continued or exited; alternatively, programmers have misused the goto statement to exit out of nested blocks.
What’s interesting is that Python rejected the labeled break and continue proposal a while ago. And here’s why:
Guido van Rossum wrote:
I’m rejecting it on the basis that code so complicated to require this feature is very rare. While I’m sure there are some (rare) real cases where clarity of the code would suffer from a refactoring that makes it possible to use return, this is offset by two issues:
1. The complexity added to the language, permanently.
2. My expectation that the feature will be abused more than it will be used right, leading to a net decrease in code clarity (measured across all Python code written henceforth). Lazy programmers are everywhere, and before you know it you have an incredible mess on your hands of unintelligible code.
But what’s more interesting is that the idea of adding a goto statement was never mentioned. Common sense perhaps?
The Cost of Hosting on Amazon
Mather Corgan, president of HotPads, gave a great talk on how HotPads uses AWS to run their real estate search engine. HotPads abandoned their managed hosting in December and took the leap over to EC2 and its siblings. The presentation has a lot of detail on costs and other things to watch out for, so if you’re currently planning your “cloud” architecture, you’ll find some of this really helpful.
The Little Manual of API Design
This manual gathers together the key insights into API design that were discovered through many years of software development on the Qt application development framework at Trolltech (now part of Nokia). When designing and implementing a library, you should also keep other factors in mind, such as efficiency and ease of implementation, in addition to pure API considerations. And although the focus is on public APIs, there is no harm in applying the principles described here when writing application code or internal library code.
An Alternative to Zend_Controller
Zend Framework is very flexible and one of its strengths is that it allows developers to implement their own components. The Zend_Controller component, for example, is very powerful. Of course, it’s not my intention to replace it, but to offer an alternative that decreases the number of decisions a developer needs to make when developing an application.
Meet Zf_Controller. The Zf_Controller component has the following goals:
- Abstract complexity: Try to reduce the level of details so the developer can focus on a few concepts at a time.
- Emphasize Convention over Configuration: Emphasize CoC, meaning that the user only needs to specify unconventional aspects of the application.
- Maintain backwards compatibility: Allow the developer to replace Zf_Controller with Zend_Controller in case the application grows in size or complexity.
- Improve performance: Load less classes, execute less code.
- Remove circular references: Avoid circular references.
- Remove Singleton classes: Avoid implementing the Singleton pattern.
- Research: Learn more about the framework, what it does, how it works.
Project Structure
Zend_Controller allows you to use the project structure that best suits your needs. On the other hand, Zf_Controller is more rigid, it only allows you to use the standard project structure:
project/
app/
config/
controllers/
ErrorController.php
IndexController.php
views/
layouts/
scripts/
error/
index/
index.phtml
domain/
Model/
Example.php
lib/
Zend/
Zf/
Zf_Controller classes:
Zf/
Controller/
Action/
Helper/
Layout.php
Action.php
Front.php
Bootstrap File
Zend_Controller:
define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../app'));
set_include_path(APPLICATION_PATH . '/../lib'
. PATH_SEPARATOR . get_include_path());
require_once "Zend/Loader.php";
Zend_Loader::registerAutoload();
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$routes = include_once APPLICATION_PATH . '/config/routes.php';
$router->addRoutes($routes);
$layout = Zend_Layout::startMvc();
$layout->setLayoutPath(APPLICATION_PATH . '/views/layouts');
$frontController->dispatch();
Zf_Controller (no Router):
define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../app'));
set_include_path(APPLICATION_PATH . '/../lib'
. PATH_SEPARATOR . APPLICATION_PATH . '/../domain);
require_once "Zend/Loader.php";
Zend_Loader::registerAutoload();
$frontController = new Zf_Controller_Front();
$frontController->setLayoutPath(APPLICATION_PATH . '/views/layouts');
$frontController->dispatch();
Action Controller
The default action controller and the default action are named “index”:
class IndexController extends Zf_Controller_Action
{
public function indexAction()
{}
}
Error Controller
Zend_Controller:
class ErrorController extends Zend_Controller_Action
{
// Action used by Zend_Controller
public function errorAction()
{
$error = $this->_getParam('error_handler');
echo $error->exception->getMessage();
}
}
Zf_Controller:
class ErrorController extends Zf_Controller_Action
{
// Action used by Zf_Controller
public function indexAction($e)
{
echo $e->getMessage();
}
}
Rendering a View Script
Zf_Controller does not use the ViewRenderer helper class. To make the code more readable and testable, you need to call the render() method and return a value:
class IndexController extends Zf_Controller_Action
{
public function indexAction()
{
$view = $this->initView();
$view->message = 'Hello';
// Renders views/scripts/index/index.phtml
return $this->render();
}
public function testAction()
{
$view = $this->initView();
$view->message = 'Goodbye';
// Renders views/scripts/index/index.phtml
return $this->render('index');
}}
index.phtml file:
<p><?php echo $this->message ?></p>
Using a Layout
By setting the path to your layouts in the Bootstrap file, you automatically enable the default layout “layout.phtml”:
$frontController = new Zf_Controller_Front(); $frontController->setLayoutPath(APPLICATION_PATH . '/views/layouts');
To disable the layout:
class IndexController extends Zf_Controller_Action
{
protected $_isLayoutEnabled = false;
}
To use a different layout:
class IndexController extends Zf_Controller_Action
{
protected $_layoutScript = 'main.phtml';
}
main.phtml file:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <?php echo $this->headTitle() ?> <?php echo $this->headScript() ?> <?php echo $this->headStyle() ?> </head> <body> <div id="content"><?php echo $this->layout()->content ?></div> </body> </html>
Source Code:
http://svn.fedecarg.com/repo/Zf/Controller/
Implementing your own Front Controller in Zend Framework
There’s no doubt that the additional complexity of implementing the default Front Controller in ZF results in a number of benefits. The most important ones are flexibility and extensibility. The Front Controller implementation takes into consideration the future growth of your application and its design reduces the level of effort required to extend it. A good example of this is the plugin architecture.
But, what if after evaluating the requirements of your system, you determine that there’s not sufficient complexity to implement the default Front Controller? What if you don’t need all that flexibility, a URL mapper, a ViewRendered plugin or an ActionStack helper. Maybe all you need is just a couple of controllers, and that’s it. It’s clear that the standard Front Controller does provide more options and meets every possible use case requirement, but at the cost of complexity and a lot of classes.
So, what to do? Do you choose a different tool for the job, or replace the default Front Controller with your own implementation? How hard can it be? Is it really that difficult to replace the most important component of the Zend Framework and maintain backwards compatibility?
You are about to find out.
In this series of posts, I’ll try to cover a few things I’ve learned about implementing my own Front Controller in Zend Framework. Most of the ideas and code are based on my previous posts:
- Zend Framework: The Cost of Flexibility is Complexity
- Zend Framework Automatic Dependency Tracking
- Zend Framework Controller: 22% Drop in Responsiveness
- Refactoring the Front Controller of the Zend Framework
- Improving the performance of Zend_Controller
- Zend Framework Architecture
Will simplicity finally meet power?
Domain-Driven Design: Sample Application
Last updated: 15 Feb, 2010
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 above are applied in this sample application.
Directory Structure
app/
config/
controllers/
UserController.php
domain/
entities/
User.php
UserProfile.php
repositories/
UserRepository.php
views/
lib/
public/
The domain layer should be well separated from the other layers and it should have few dependencies on the framework you are using.
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 User
{
private $id;
private $name;
/* @var UserProfile */
private $profile;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function setProfile(UserProfile $profile)
{
$this->profile = $profile;
}
public function getProfile()
{
return $this->profile;
}
}
class UserProfile
{
private $id;
public function __construct($id)
{
$this->id = $id;
}
}
Users Collection
A collection is simply an object that groups multiple elements into a single unit.
class Users
{
private $elements = array();
public function __construct(array $users)
{
foreach ($users as $user) {
if (!($user instanceof User)) {
throw new Exception();
}
$this->elements[] = $user;
}
}
public function toArray()
{
return $this->elements;
}
}
User DAO
The UserDAO class allows data access mechanisms to change independently of the code that uses the data:
interface UserDao
{
public function find($id);
public function findAll();
}
class UserDatabaseDao implements UserDao
{
public function find($id)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource()
$db = $dataSource->getConnection('slave');
$query = $db->select()->from('user')->where('id = ?', $id);
return $db->fetchRow($query);
}
public function findAll()
{
...
return $db->fetchAll($query);
}
}
interface UserProfileDao
{
public function find($id);
public function findByUserId($id);
}
class UserProfileDatabaseDao implements UserProfileDao
{
public function find($id)
{
...
}
public function findByUserId($id)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource()
$db = $dataSource->getConnection('slave');
$query = $db->select()->from('user_profile')->where('user_id = ?', $id);
return $db->fetchRow($query);
}
}
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 Repository object can inject dependencies on demand, making the instantiation process inexpensive.
class UserRepository
{
/* @var UserDatabaseDao */
private $userDao;
/* @var UserProfileDatabaseDao */
private $userProfileDao;
public function __construct()
{
$this->userDao = new UserDatabaseDao();
$this->userProfileDao = new UserProfileDatabaseDao();
}
public function find($id)
{
$row = $this->userDao->find($id);
$user = new User($row['id'], $row['name']);
$row = $this->userProfileDao->findByUserId($id);
if (isset($row['id'])) {
$profile = new UserProfile($row['id']);
$user->setProfile($profile);
}
return $user;
}
public function findAll()
{
$users = array();
$rows = $this->userDao->findAll();
foreach ($rows as $row) {
$users[] = new User($row['id'], $row['name']);
}
return new Users($users);
}
}
Usage:
$repository = new UserRepository(); $user = $repository->find(1); $profile = $user->getProfile(); $users = $repository->findAll();
Source Code
http://svn.fedecarg.com/repo/Zf/Orm
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
Domain-Driven Design: The Repository
Part 2: Domain-Driven Design: Data Access Strategies
The Ubiquitous Language
The ubiquitous language is the foundation of Domain-driven design. The concept is simple, developers and domain experts share a common language that both understand. This language is set in business terminology, not technical terminology. This ubiquitous language allows the technical team become part of the business.
The Repository
Repositories play an important part in DDD, they speak the language of the domain and act as mediators between the domain and data mapping layers. They provide a common language to all team members by translating technical terminology into business terminology.
In a nutshell, a Repository:
- Is not a data access layer
- Provides a higher level of data manipulation
- Is persistence ignorance
- Is a collection of aggregate roots
- Offers a mechanism to manage entities
Data Access Objects
In the absence of an ORM framework, the Data Access Object (DAO) handles the impedance mismatch that a relational database has with object-oriented techniques. In DDD, you inject Repositories, not DAO’s in domain entities. For example, imagine you have an entity named User that needs to access the database to retrieve the User details:
class User
{
private $id;
private $name;
public function __construct(UserDao $dao, $id)
{
$row = $dao->find($id);
$this->setId($row['id']);
$this->setName($row['name']);
}
public function setId($id) {}
public function getId() {}
public function setName($name) {}
public function getName() {}
}
The User DAO class will look something like this:
interface UserDao
{
public function fetchRow($id);
}
class UserDatabaseDaoImpl implements UserDao
{
public function fetchRow($id)
{
...
$query = $db->select();
$query->from('user');
$query->where('id = ?', $id);
return $db->fetchRow($query);
}
}
$dao = new UserDatabaseDaoImpl();
$user = new User($dao, 1);
$userId = $user->getId();
But, what about separation of concerns? DAO’s are related to persistence, and persistence is infrastructure, not domain. The main problem with the example above is that we have lots of different concerns polluting the domain. According to DDD, an object should be distilled until nothing remains that does not relate to its meaning or support its role in interactions. And that’s exactly the problem the Repository pattern tries to solve.
Injecting Repositories
Lets create a UserRepository class to isolate the domain object from details of the UserDatabaseDaoImpl class:
class User
{
private $id;
private $name;
public function __construct(UserRepository $repository, $id)
{
$row = $repository->find($id);
$this->setId($row['id']);
$this->setName($row['name']);
}
public function setId($id) {}
public function getId() {}
public function setName($name) {}
public function getName() {}
}
It’s the responsibility of the UserRepository to work with all necessary DAO’s and provide all data access services to the domain model in the language which the domain understands.
interface UserRepository
{
public function find($id);
}
class UserRepositoryImpl implements UserRepository
{
private $databaseDao;
public function setDatabaseDao(UserDao $dao) {}
public function getDatabaseDao() {}
public function find($id)
{
return $this->getDatabaseDao()->find($id);
}
}
$userRepository = new UserRepositoryImpl();
$userRepository->setDatabaseDao(new UserDatabaseDaoImpl());
$user = new User($userRepository, 1);
$userId = $user->getId();
$userName = $user->getName();
The main difference between the Repository and the DAO is that the DAO is at a lower level of abstraction and doesn’t speak the ubiquitous language of the domain.
So, what’s next?
The process of creating an entity is complex, because an entity always has relationship with other objects in your domain model. When creating an entity, we have to initialize its relationships as well. Therefore, it’s a good practice to delegate this task to another object.
We’ve seen how to write a persistence-ignorant domain model. Next, I’ll explain how to automate the creation and injection of dependencies.