Part 1: Domain-Driven Design and MVC Architectures
The Domain Model
Here are some of the features a Domain-driven design framework should support:
- A domain model that is independent and decoupled from the application.
- A reusable library that can be used in many different domain-specific applications.
- Dependency Injection in order to inject Repositories and Services into Domain Objects.
- Integration with unit testing frameworks, such as PHPUnit.
- Good integration with other frameworks, such as Zend, Symfony, Doctrine, etc.
The Zend Framework, for example, is part of the infrastructure layer and acts as a supporting library for all the other layers. However, the domain layer should be well isolated from the other layers of the application and should not be dependent on the framework you are using.
Data Access Strategies
When accessing data from a data source, you have to decide how your application will communicate with a data source. Each data access strategy has its own advantages and disadvantages. Here are some design patterns that support DDD:
Generic DAO’s
Data Access Objects (DAO’s) and Repositories play an important role in DDD. The goal of a DAO is to abstract and encapsulate all access to the data and provide an interface. The DAO always connects, reads and saves data to a data source. From the applications point of view, it makes no difference when it accesses a database, XML file or Web service:
$user = new UserDbDao(); $row = $user->findUserById(456); echo $row->name; $user = new UserXmlDao(); $row = $user->findUserById(456); echo $row->name;
Table Data Gateway
An object that acts as a Gateway to a database table. One instance handles all the rows in the table. The Zend_Db_Table solution is an implementation of the Table Data Gateway pattern:
$user = new User(); $rows = $user->find(456); $row = $rows->current(); echo $row->name;
More info: Table Data Gateway
Row Data Gateway
An object that acts as a Gateway to a single record in a data source. There is one instance per row. Zend_Db_Table_Row is an implementation of the Row Data Gateway pattern:
$user = new User(); $row = $user->fetchRow($user->select()->where('user_id = ?', 1)); echo $row->name;
More info: Row Data Gateway
Active Record
An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data. The Mad_Model component is an ORM layer that follows the Active Record pattern where tables map to classes, rows map to objects, and columns to object attributes:
$user = new User(); $userFinder = new UserFinder(); $row = $userFinder->find(456);
More info: Active Record
Data Mapper
A layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself:
$user = new User(); $userMapper = new UserMapper(); $row = $userMapper->findByEmail($email);
More info: Data Mapper
Data Transfer Objects
A Data Transfer Object (DTO) in Domain-driven design is not the same as a Value Object. A DTO is often used in combination with a DAO to encapsulate data. It allows you to reduce communication effort when dealing with a lot of small data entities. For example:
class UserData { public function setId($id) {} public function getId() {} public function setFirstName($firstName) {} public function getFirstName() {} public function setLastName($lastName) {} public function getLastName() {} } $user = new UserData(); $user->setFirstName('Jim'); $user->setLastName('Morrison'); $userDao = new UserDbDao(); $userDao->insert($user);
A DTO is a simple container for a set of aggregated data. It should contain no business logic and limit its behaviour to activities such as internal consistency checking and basic validation. Be careful not to make the DTO depend on any new classes as a result of implementing these methods.
14 responses to “Domain-Driven Design: Data Access Strategies”
great post, thank you.
“Each data access strategy has its own advantages and disadvantages.”
can you explain them?
thanks, cirpo
I think only the last two are patterns that implements persistance ignorance and can be used in a DDD framework.
@cirpo There’s no one best strategy for persistence. Some strategies can help reduce complexity, others improve performance. Use the strategy that’s most appropriate for your team and your project.
@Giorgio To achieve PI in DDD you can use the Repository pattern (that’s my next post). In order to implement a strategy correctly, you need to use design patterns and take advantage of OOP concepts.
@federico I known that ” There’s no one best strategy for persistence[…]” that’s why i asked if you can explain or show deeply the differences/advantages/disadvantages not the best one or maybe point me to some online resource for real case studies.
Anyway IMHO it’s a very good post, can’t wait for the next one.
thanks
@cirpo Yes, I tried finding an article or forum discussion showing the pros and cons, but with no luck.
@federico thanks anyway, it’s time to buy “Patterns of Enterprise Application Architecture”… :D
[…] March 15, 2009 Part 2: Domain-Driven Design: Data Access Strategies […]
If you want to learn more about Data Access Objects, I’ve written a post on implementing DAO in PHP including examples etc.
http://codeutopia.net/blog/2009/01/05/decoupling-models-from-the-database-data-access-object-pattern-in-php/
Thanks Jani :)
[…] read an interesting post on Domain Driven Design on Fede.carg’s […]
[…] Domain-Driven Design: Data Access Strategies – a look at some of the different data access methods (like Active Record, Data Transfer Objects and generic Data Access Objects) […]
I like your site. It seems most people don’t really bother writing articles that lack substance nowadays.
If you have an Aggregate that has an Entity with several fields who are Value Objects, but then also the Entity has several fields that are Maps, Sets, or Lists, how would you design a DTO (I believe aka Specification) for using within a Repository?
For example: A CreditApplication has a field type Money called loanAmount (I want to query the DB on loans of a certain value or range). It also has a Map of Applicants (I only need the Applicant ID – SSN – to perform this query). If I move the Map of Applicants into the DTO, it complicates the DTO when I only need one Applicant’s id (let say there’s 2 people applying for credit). It also complicates the code if I have the DTO (Specification) object as a field in the Root Entity CreditApplication because I don’t get much value of encapsulating data into the DTO (Specication) making it much easier to extend the specification without affecting code in multiple locations.
I have the same question above…..