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

Build Laravel Application using DDD and CQRS with Ecotone

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:

  1. We mark our Model with #[Aggregate] so Ecotone can find it
  2. We enable factory method under "issue.report" routing key.
  3. As we want to publish event which contains Id we need to save Issue inside the factory method, so the identifier will be assigned
  4. We publish IssueWasReported using recordThat method, which comes from WithAggregateEvents trait.
  5. 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.