Build Laravel Application using DDD and CQRS with Ecotone
DDD becomes more and more popular in PHP World, check how to implement DDD Application in Laravel using Ecotone Framework
DDD becomes more and more popular in PHP World and there are a lot of discussions on how to approach it.
In Laravel community it’s getting momentum too.
Have you been wondering how can it be implemented in Laravel?
In this article I will explain basics of DDD building blocks and how can we use them in Laravel Application.
It’s not required, however I encourage you to take a look on previous blog about CQRS “Going into CQRS with PHP” before starting.
Eloquent ORM and DDD
In DDD we are using Domain Models, which are different from Eloquent Models.
Domain Models are clean from infrastructure and Framework related implementation, in order to have full control over modifications and extensions.
Domain Models are meant to solve business related problems not technical ones.
Thanks to that it’s easier to maintain and extend them.
If we decided to use Eloquent and mix it with Domain Model, then we’ve introduced database dependency.
However we did it for a reason, as we want to reap benefits that Eloquent integration brings.
Fighting your own framework may bring higher complexity into the code base and make increase maintainability cost.
For building Domain Models without Eloquent there will be a separate blog post, in this one we will focus on, what we can achieve, if we decided to go hand in hand with Eloquent ORM.
Aggregates
In DDD we build Aggregates which are classes rich in behaviour
.
As an example let's take Article
, which may be be published
, however first need to be approved
by the author.
This could be Eloquent Model, that we would store in database using ->save()
method.
It's important to expose actions (methods) on the Aggregate and make use it as the only way to modify the data.
Calling insert and updates SQLs directly on the database, may introduce incorrect data or bypass our constraints.
If the only way to modify given aggregate is through his public methods and methods are protecting it’s own state inside, then we come the place where our models are always valid.
Value Objects
The part of the model are Value Objects.
VO’s are classes that are immutable. They are wrappers for given types that needs validation.
If we have Value Object like Email in system, then we can pass it around and be sure, it’s always valid one. This decrease amount of guard logic within the system.
Enable Ecotone with Laravel
To enable Ecotone with Laravel install:
composer require ecotone/laravel
For automatic serialization and deserialization:
composer require ecotone/jms-converter
Eloquent Creating new Aggregate
We will implement Issue
aggregate. Issues can be reported by our customers in case of system errors.
Let's start by defining ReportIssue Command
.
In order to execute the ReportIssue
, we need to call Command Bus
.
What comes from $request->all()
return data from HTML Form
which is:
["email" => "johnybravo@gmail.com","content" => "My PC is not working"]
The Email
inside the ReportIssue
is Value Object Class, so we need to know how to convert from string
to Email
class. In order to do it we will register Converter
.
Ecotone JMS Converter package will handle all deserialization from Arrays / JSON / XML to PHP classes and other way around.
Let’s implement our Eloquent Issue Aggregate
:
- We mark our Model with
#[Aggregate]
so Ecotone can find it - We enable factory method under
"issue.report"
routing key. - As we want to publish event which contains
Id
we need to saveIssue
inside the factory method, so the identifier will be assigned - We publish
IssueWasReported
usingrecordThat
method, which comes fromWithAggregateEvents
trait. - We need to expose public method with identifier for Ecotone
#[AggregateIdentifierMethod("id")]
Now we can execute our Controller we have defined previously to send Command and create new Issue
.
Changing The Aggregate Issue
Now we can add possibility to close the Issue.
There is no Command Class defined for Command Handler and that’s because we do not need any data in here. When needed Ecotone allows for seamless routing to given Handlers by routing name only.
Now we want to execute this Command Handler
The metadata
part are extra information, that are not part of the Command itself.
Ecotone use it to pass around framework related information.
By passing aggregate.id
we tell Ecotone which Issue Aggregate it should execute
.
You may use metadata to pass your own extra details that you would like to pass alongside with Command for example User Id
.
Event Handling
When Issue
aggregate was reported we are publishing event:
We can register subscriber, that will send Email with confirmation to the customer after Issue was reported.
To subscribe to specific Event we type hint given class as first parameter
(just like with Command Handler) and mark method with #[EventHandler]
.
Summary
Ecotone Framework
is handling glue code and providing building blocks, so we can build applications quickly and in solid way.
It integrates with Laravel and Eloquent in way those Frameworks were designed.
And in case of need allow you to extend your application with new functionalities like Event Sourcing and Asynchronous communication.
If you want to run Demo Application with Laravel and Ecotone
you can find it under this repository.