Domain Driven Design in Laravel 4 - Fourth & Final
I am going to bring this series of posts to a close as I am going to be tied up for a while on a front end single page app written in Angular, so my backend work with Laravel in the near future is going to be at a very simplified REST API level, so not much time to explore further the best architecture for DDD in Laravel.
However, while not coming to the perfect solution in Laravel 4 for implementing DDD, I have come to the following conclusions:
This is a very basic Doctrine setup for testing only - if taken further you would probably want to abstract out the config to a separate configuration file, or use one of the existing Doctrine integrations mentioned above.
I chose to use the xml metadata option, and these are stored in '/Infrastructure/Repository/Metadata' folder, with the view that these persistence config files are a matter of infrastructure rather than being related to the Domain.
With the above in place the Doctrine Entity Manager can be injected into controller constructors, etc.
At this point, or even prior to this, you can start building your application Domain from an Object Oriented perspective, and ignore the database. Rather then starting with database tables and then thinking how you can bring the data from these into your application you can think about the Objects that make up your specific domain, the state and behaviour they need to encapsulate, and the messages that need to pass between them.
These domain objects, or entities, can then be created as simple php classes, with no need to inherit from any persistence infrastructure. For instance in the sample github repo the Project entity is a simple php class with some fields, basic getters and setters, but also a some simple behaviour (very basic, just a method to calculate hours worked based on the related project team members).
Taking this code first approach, rather than database first, also makes it much easier to apply Responsibility Driven Design principles (DDD, RDD, TDD - what next!) - eg. asking which Objects should be responsible for which bits of behaviour.
You can then worry about how the objects are persisted by setting up Doctrine metadata files for each entity.
From within your application, eg. in a controller, you can then create, and persist these entities:
Or recreate them from the persistence store and interact with them:
Not a lot different to what you could do with Eloquent models - the difference being that the entities, and therefore your Domain/Business logic are completely framework, and storage infrastructure independent. This may or may not have testing, maintenance and portability advantages that make the extra learning curve and work of Doctrine versus Eloquent worth the effort - this will depend on the size and complexity of your project.
This article, and the sample repo barely touch the surface of Doctrine configuration and functionality. There is plenty of information about its setup and capabilities on the project website if you are interested in using it with Laravel.
Obviously I have only touched on one very small aspect of DDD, ie. the clean domain model separated from persistence infrastructure concerns. There are plenty of other aspects that can be applied to system development in Laravel 4 regardless of which persistence layer you might be using, eg. ubiquitous language, entities versus value objects, etc.
If you are interested in the topic keep an eye on Chris Fidao's blog as he is doing a bit of work in this area, particularly around hexagonal architecture. Shawn McCool is also worth following with his work around a use case architecture.
However, while not coming to the perfect solution in Laravel 4 for implementing DDD, I have come to the following conclusions:
- DDD principles are fantastic in encouraging good design, pushing you towards good Object Oriented techniques, and forcing you into remembering that Objects should encapsulate both state and behaviour
- I agree with Chris Fidao's conclusion in his article on Anaemic Domain Models, ie. if you want to be a purist in the application of DDD principles, and build rich domain models, then switching from Eloquent to Doctrine is probably the first step
- But .... you need to seriously consider if the size and complexity of your application warrants giving up the simplicity of Eloquent
I did get as far as integrating Doctrine with Laravel 4 and playing with some basic Entity creation, etc.
There is a repo at https://github.com/glendaviesnz/laravel-ddd with a suggested project folder structure, Doctrine integration, and some initial entities to give a basic idea of one way you could structure a Laravel 4 app with layered DDD architecture.
Below are some of the details of the setup used.
Firstly Doctrine is added by adding the following lines to the composer.json file and running composer update
"require": {
"laravel/framework": "4.2.*",
"doctrine/orm": "2.4.*"
},
There are a number of Doctrine/Laravel integrations that do a lot of the hard work for you, but I wanted to get familiar with Doctrine in the process so integrated it manually by creating a MyApp folder and in here adding a MyAppServiceProvider file which makes the Doctrine entity manager available as a singleton via the Laravel IoC, so it can be easily injected into constructors as needed.
public function register()
{
\App::singleton('Doctrine\ORM\EntityManager', function() {
$isDevMode = true;
$paths = array(__DIR__.'/Infrastructure/Repository/Metadata');
$config = Setup::createXMLMetadataConfiguration($paths, $isDevMode);
$conn = array(
'driver' => 'pdo_mysql',
'host' => 'localhost',
'dbname' => 'laravelddd',
'user' => 'root',
'password' => '',
);
return \Doctrine\ORM\EntityManager::create($conn, $config);
});
}
This is a very basic Doctrine setup for testing only - if taken further you would probably want to abstract out the config to a separate configuration file, or use one of the existing Doctrine integrations mentioned above.
I chose to use the xml metadata option, and these are stored in '/Infrastructure/Repository/Metadata' folder, with the view that these persistence config files are a matter of infrastructure rather than being related to the Domain.
With the above in place the Doctrine Entity Manager can be injected into controller constructors, etc.
use Doctrine\ORM\EntityManager;
class ProjectsController extends \Controller {
public function __construct(EntityManager $entity_manager)
{
$this->entity_manager = $entity_manager;
}
At this point, or even prior to this, you can start building your application Domain from an Object Oriented perspective, and ignore the database. Rather then starting with database tables and then thinking how you can bring the data from these into your application you can think about the Objects that make up your specific domain, the state and behaviour they need to encapsulate, and the messages that need to pass between them.
These domain objects, or entities, can then be created as simple php classes, with no need to inherit from any persistence infrastructure. For instance in the sample github repo the Project entity is a simple php class with some fields, basic getters and setters, but also a some simple behaviour (very basic, just a method to calculate hours worked based on the related project team members).
Taking this code first approach, rather than database first, also makes it much easier to apply Responsibility Driven Design principles (DDD, RDD, TDD - what next!) - eg. asking which Objects should be responsible for which bits of behaviour.
You can then worry about how the objects are persisted by setting up Doctrine metadata files for each entity.
From within your application, eg. in a controller, you can then create, and persist these entities:
$project = new \MyApp\Domain\Entity\Project();
$project->setName('Megashop website build');
//persist the new project
$this->entity_manager->persist($project);
$this->entity_manager->flush();
$projectRepository = $this->entity_manager->getRepository('MyApp\Domain\Entity\Project');
$projects = $projectRepository->findAll();
$hours_worked = array();
foreach ($projects as $project) {
$hours_worked[$project->getName()] = $project->calculateTotalHoursWorked();
}
This article, and the sample repo barely touch the surface of Doctrine configuration and functionality. There is plenty of information about its setup and capabilities on the project website if you are interested in using it with Laravel.
Obviously I have only touched on one very small aspect of DDD, ie. the clean domain model separated from persistence infrastructure concerns. There are plenty of other aspects that can be applied to system development in Laravel 4 regardless of which persistence layer you might be using, eg. ubiquitous language, entities versus value objects, etc.
If you are interested in the topic keep an eye on Chris Fidao's blog as he is doing a bit of work in this area, particularly around hexagonal architecture. Shawn McCool is also worth following with his work around a use case architecture.
Comments
Post a Comment