DEV Community

Cover image for DDD and hexagonal architecture with Symfony Flex
Víctor Falcón
Víctor Falcón

Posted on • Updated on

DDD and hexagonal architecture with Symfony Flex

I wrote a small project to test how can we follow hexagonal architecture and DDD in an empty Symfony Flex project. The idea was to keep all our code agnostic from Symfony as much as possible.

Before getting into the content, follow me and you will get notice when this series continue:

victoor image

Because this is just an example, I made only one endpoint to create Leads. But I implemented an Event Bus one sync and another asynchronous using MySql, and we also have a Command Query and a Command Bus because we are also following CQRS.

In this first article I'm going to explain how are our controllers because they are the layer that it's near Symfony because we are using the framework to process request and routes.

In the future we will cover more topics like:

  1. Introduction and Controllers in a DDD Symfony Flex Project (this article)
  2. Structure a DDD project in Symfony Flex
  3. Create a Command and Query Bus
  4. Make an Event Bus sync and asynchronous
  5. How to configure PHP Unit to make unit and integration tests
  6. Configure and install Behat to make feature testing in our Symfony project

Let's start with controllers.


We have just one controller for each API action we implemented. Right now I only have one action to create Leads and you can find the controller in app/Controller/Leads/LeadsPostController.php. We are storing controllers in the app folder because it's our only code fully attached to the framework. The rest of our code it's on src.

As you can see in the controller, it's very simple. We only get the request from primitives types and create a command that we fully understand, and it doesn't depend on the request type.

If in the future, we want to create leads from the command line, for example, we can use this same command.

Then, if everything it's ok we return a HTTP_CREATED or, if not, the response with the message to clarify what it's happening.

$name = $request->get('name');
$email = $request->get('email');

try {
    $this->commandBus->dispatch(new CreateLeadCommand($name, $email));
    return new Response(null, Response::HTTP_CREATED);
} catch (DuplicatedLeadException $e) {
    return new ErrorResponse($e, Response::HTTP_BAD_REQUEST);
Enter fullscreen mode Exit fullscreen mode

We don't have yet any example on this project but if we want to get some data from the API we can use the Query Bus that it's already created in the project.

The result will be something similar to this:

$id = $request->get('id');

try {
    /** @var Lead $lead */
    $lead = $this->queryBus->dispatch(new GetLeadCommand($id));
    return new Response($lead);
} catch (NotFoundException $e) {
    return new ErrorResponse($e, Response::HTTP_BAD_REQUEST);
Enter fullscreen mode Exit fullscreen mode

Following this simple structure we can have small Controllers that do whatever we want.

It's important that our controller just transform and validate the request to something that we control and fully understand. We need to avoid passing primitives to our src folder.

Change controllers path in Symfony

To move our controllers to a new path, in Symfony, we have to configure them in the file config/services.yaml. With something like this is enough.

    resource: '../app/Controller/'
    tags: [ 'controller.service_arguments' ]
Enter fullscreen mode Exit fullscreen mode

In a few days I will talk about how I structure the project in folders and how to configure Symfony to work with this structure.

All the code it's public available in this repository.

Top comments (2)

j4r3kb profile image
Jarosław Brzychcy

Waiting for more...

victoor profile image
Víctor Falcón