DDD and hexagonal architecture with Symfony Flex
Tags: symfony, php, ddd, architecture
Published: 2020-09-15 11:16:09

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:

{% user victoor %}

---

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:

0. [Introduction and Controllers in a DDD Symfony Flex Project](https://dev.to/victoor/ddd-and-hexagonal-architecture-with-symfony-flex-30l9) (this article)
1. [Structure a DDD project in Symfony Flex](https://dev.to/victoor/ddd-and-hexagonal-architecture-with-symfony-flex-part-2-4ojc)
2. Create a Command and Query Bus
3. Make an Event Bus sync and asynchronous
4. How to configure PHP Unit to make unit and integration tests
5. Configure and install Behat to make feature testing in our Symfony project

Let's start with controllers.

## Controller

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. 

```php
$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);
}
```

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:

```php
$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);
}
```

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.

```yml
App\Controller\:
    resource: '../app/Controller/'
    tags: [ 'controller.service_arguments' ]
```

---

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](https://github.com/victor-falcon/php-ddd).