Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions .php-cs-fixer.dist.php

This file was deleted.

26 changes: 7 additions & 19 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
FROM php:8.1.6-fpm-alpine
FROM php:8.2-fpm-alpine
WORKDIR /app

RUN wget https://github.com/FriendsOfPHP/pickle/releases/download/v0.7.9/pickle.phar \
&& mv pickle.phar /usr/local/bin/pickle \
&& chmod +x /usr/local/bin/pickle

RUN apk --update upgrade \
&& apk add --no-cache autoconf automake make gcc g++ bash icu-dev libzip-dev rabbitmq-c rabbitmq-c-dev \
&& docker-php-ext-install -j$(nproc) \
&& apk add --no-cache autoconf automake make gcc g++ bash icu-dev libzip-dev rabbitmq-c rabbitmq-c-dev linux-headers

RUN pecl install apcu-5.1.22 && pecl install amqp-2.1.0 && pecl install xdebug-3.2.2

RUN docker-php-ext-install -j$(nproc) \
bcmath \
opcache \
intl \
zip \
pdo_mysql

RUN pickle install apcu@5.1.21

ADD etc/infrastructure/php/extensions/rabbitmq.sh /root/install-rabbitmq.sh
ADD etc/infrastructure/php/extensions/xdebug.sh /root/install-xdebug.sh
RUN apk add git
RUN sh /root/install-rabbitmq.sh
RUN sh /root/install-xdebug.sh

RUN docker-php-ext-enable \
amqp \
apcu \
opcache
RUN docker-php-ext-enable amqp apcu opcache

RUN curl -sS https://get.symfony.com/cli/installer | bash -s - --install-dir /usr/local/bin

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ static-analysis: composer-env-file

.PHONY: lint
lint:
docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/php-cs-fixer fix --config .php-cs-fixer.dist.php --allow-risky=yes --dry-run
docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/ecs check

.PHONY: run-tests
run-tests: composer-env-file
Expand Down
28 changes: 21 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ some Symfony and Laravel implementations.

### ⛱️ Bounded Contexts

* [Mooc](src/Mooc): Place to look in if you wanna see some code 🙂. Massive Open Online Courses public platform with users, videos, notifications, and so on.
* [Backoffice](src/Backoffice): Here you'll find the use cases needed by the Customer Support department in order to manage users, courses, videos, and so on.
- [Mooc](src/Mooc): Place to look in if you wanna see some code 🙂. Massive Open Online Courses public platform with users, videos, notifications, and so on.
- [Backoffice](src/Backoffice): Here you'll find the use cases needed by the Customer Support department in order to manage users, courses, videos, and so on.

### 🎯 Hexagonal Architecture

Expand Down Expand Up @@ -106,6 +106,7 @@ src
```

#### Repository pattern

Our repositories try to be as simple as possible usually only containing 2 methods `search` and `save`.
If we need some query with more filters we use the `Specification` pattern also known as `Criteria` pattern. So we add a
`searchByCriteria` method.
Expand All @@ -114,33 +115,46 @@ You can see an example [here](src/Mooc/Courses/Domain/CourseRepository.php)
and its implementation [here](src/Mooc/Courses/Infrastructure/Persistence/DoctrineCourseRepository.php).

### Aggregates

You can see an example of an aggregate [here](src/Mooc/Courses/Domain/Course.php). All aggregates should
extend the [AggregateRoot](src/Shared/Domain/Aggregate/AggregateRoot.php).

### Command Bus

There is 1 implementations of the [command bus](src/Shared/Domain/Bus/Command/CommandBus.php).
1. [Sync](src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus.php) using the Symfony Message Bus
1. [Sync](src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus.php) using the Symfony Message Bus.


### Query Bus

The [Query Bus](src/Shared/Infrastructure/Bus/Query/InMemorySymfonyQueryBus.php) uses the Symfony Message Bus.

### Event Bus

The [Event Bus](src/Shared/Infrastructure/Bus/Event/InMemory/InMemorySymfonyEventBus.php) uses the Symfony Message Bus.
The [MySql Bus](src/Shared/Infrastructure/Bus/Event/MySql/MySqlDoctrineEventBus.php) uses a MySql+Pulling as a bus.
The [RabbitMQ Bus](src/Shared/Infrastructure/Bus/Event/RabbitMq/RabbitMqEventBus.php) uses RabbitMQ C extension.

## 📱 Monitoring

Every time a domain event is published it's exported to Prometheus. You can access to the Prometheus panel [here](http://localhost:9999/).

## 🤔 Contributing

There are some things missing (add swagger, improve documentation...), feel free to add this if you want! If you want
some guidelines feel free to contact us :)

## 🤩 Extra

This code was shown in the [From framework coupled code to #microservices through #DDD](http://codely.tv/screencasts/codigo-acoplado-framework-microservicios-ddd) talk and doubts where answered in the [DDD y CQRS: Preguntas Frecuentes](http://codely.tv/screencasts/ddd-cqrs-preguntas-frecuentes/) video.

🎥 Used in the CodelyTV Pro courses:
* [🇪🇸 DDD in PHP](https://pro.codely.tv/library/ddd-en-php/about/)
* [🇪🇸 Arquitectura Hexagonal](https://pro.codely.tv/library/arquitectura-hexagonal/66748/about/)
* [🇪🇸 CQRS: Command Query Responsibility Segregation](https://pro.codely.tv/library/cqrs-command-query-responsibility-segregation-3719e4aa/62554/about/)
* [🇪🇸 Comunicación entre microservicios: Event-Driven Architecture](https://pro.codely.tv/library/comunicacion-entre-microservicios-event-driven-architecture/74823/about/)

- [🇪🇸 DDD in PHP](https://pro.codely.tv/library/ddd-en-php/about/)
- [🇪🇸 Arquitectura Hexagonal](https://pro.codely.tv/library/arquitectura-hexagonal/66748/about/)
- [🇪🇸 CQRS: Command Query Responsibility Segregation](https://pro.codely.tv/library/cqrs-command-query-responsibility-segregation-3719e4aa/62554/about/)
- [🇪🇸 Comunicación entre microservicios: Event-Driven Architecture](https://pro.codely.tv/library/comunicacion-entre-microservicios-event-driven-architecture/74823/about/)

## 🌐 remember to visit our courses

- [Courses codely](https://codely.com/cursos)
4 changes: 3 additions & 1 deletion apps/backoffice/backend/config/bundles.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<?php

declare(strict_types=1);

return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true],
// WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true]
];
8 changes: 5 additions & 3 deletions apps/backoffice/backend/public/index.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

use CodelyTv\Apps\Backoffice\Backend\BackofficeBackendKernel;
use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -14,7 +16,7 @@

if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? $_ENV['TRUSTED_PROXIES'] ?? false) {
Request::setTrustedProxies(
explode(',', $trustedProxies),
explode(',', (string) $trustedProxies),
Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST
);
}
Expand All @@ -23,8 +25,8 @@
Request::setTrustedHosts([$trustedHosts]);
}

$kernel = new BackofficeBackendKernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$request = Request::createFromGlobals();
$kernel = new BackofficeBackendKernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
3 changes: 2 additions & 1 deletion apps/backoffice/backend/src/BackofficeBackendKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel;

use function dirname;

final class BackofficeBackendKernel extends Kernel
class BackofficeBackendKernel extends Kernel
{
use MicroKernelTrait;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,36 @@
use CodelyTv\Shared\Domain\Bus\Query\QueryBus;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

use function Lambdish\Phunctional\map;

final class CoursesGetController
final readonly class CoursesGetController
{
public function __construct(private QueryBus $queryBus)
{
}
public function __construct(private QueryBus $queryBus) {}

public function __invoke(Request $request): JsonResponse
{
$orderBy = $request->query->get('order_by');
$order = $request->query->get('order');
$limit = $request->query->get('limit');
$limit = $request->query->get('limit');
$offset = $request->query->get('offset');

/** @var BackofficeCoursesResponse $response */
$response = $this->queryBus->ask(
new SearchBackofficeCoursesByCriteriaQuery(
(array) $request->query->get('filters'),
null === $orderBy ? null : (string) $orderBy,
null === $order ? null : (string) $order,
null === $limit ? null : (int) $limit,
null === $offset ? null : (int) $offset
$orderBy === null ? null : (string) $orderBy,
$order === null ? null : (string) $order,
$limit === null ? null : (int) $limit,
$offset === null ? null : (int) $offset
)
);

return new JsonResponse(
map(
fn (BackofficeCourseResponse $course) => [
'id' => $course->id(),
'name' => $course->name(),
'id' => $course->id(),
'name' => $course->name(),
'duration' => $course->duration(),
],
$response->courses()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class MetricsController
final readonly class MetricsController
{
public function __construct(private PrometheusMonitor $monitor)
{
}
public function __construct(private PrometheusMonitor $monitor) {}

public function __invoke(Request $request): Response
{
$renderer = new RenderTextFormat();
$result = $renderer->render($this->monitor->registry()->getMetricFamilySamples());
$result = $renderer->render($this->monitor->registry()->getMetricFamilySamples());

return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]);
}
Expand Down
6 changes: 4 additions & 2 deletions apps/backoffice/frontend/config/bundles.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<?php

declare(strict_types=1);

return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
// WouterJ\EloquentBundle\WouterJEloquentBundle::class => ['test' => true]
];
8 changes: 5 additions & 3 deletions apps/backoffice/frontend/public/index.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

use CodelyTv\Apps\Backoffice\Frontend\BackofficeFrontendKernel;
use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -14,7 +16,7 @@

if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? $_ENV['TRUSTED_PROXIES'] ?? false) {
Request::setTrustedProxies(
explode(',', $trustedProxies),
explode(',', (string) $trustedProxies),
Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST
);
}
Expand All @@ -23,8 +25,8 @@
Request::setTrustedHosts([$trustedHosts]);
}

$kernel = new BackofficeFrontendKernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$request = Request::createFromGlobals();
$kernel = new BackofficeFrontendKernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
3 changes: 2 additions & 1 deletion apps/backoffice/frontend/src/BackofficeFrontendKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel;

use function dirname;

final class BackofficeFrontendKernel extends Kernel
class BackofficeFrontendKernel extends Kernel
{
use MicroKernelTrait;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
final class ImportCoursesToElasticsearchCommand extends Command
{
public function __construct(
private MySqlBackofficeCourseRepository $mySqlRepository,
private ElasticsearchBackofficeCourseRepository $elasticRepository
private readonly MySqlBackofficeCourseRepository $mySqlRepository,
private readonly ElasticsearchBackofficeCourseRepository $elasticRepository
) {
parent::__construct();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use CodelyTv\Mooc\CoursesCounter\Application\Find\CoursesCounterResponse;
use CodelyTv\Mooc\CoursesCounter\Application\Find\FindCoursesCounterQuery;
use CodelyTv\Shared\Domain\ValueObject\Uuid;
use CodelyTv\Shared\Domain\ValueObject\SimpleUuid;
use CodelyTv\Shared\Infrastructure\Symfony\WebController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand All @@ -21,10 +21,10 @@ public function __invoke(Request $request): Response
return $this->render(
'pages/courses/courses.html.twig',
[
'title' => 'Courses',
'description' => 'Courses CodelyTV - Backoffice',
'title' => 'Courses',
'description' => 'Courses CodelyTV - Backoffice',
'courses_counter' => $coursesCounterResponse->total(),
'new_course_id' => Uuid::random()->value(),
'new_course_id' => SimpleUuid::random()->value(),
]
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ private function validateRequest(Request $request): ConstraintViolationListInter
{
$constraint = new Assert\Collection(
[
'id' => new Assert\Uuid(),
'name' => [new Assert\NotBlank(), new Assert\Length(['min' => 1, 'max' => 255])],
'id' => new Assert\Uuid(),
'name' => [new Assert\NotBlank(), new Assert\Length(['min' => 1, 'max' => 255])],
'duration' => [new Assert\NotBlank(), new Assert\Length(['min' => 4, 'max' => 100])],
]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function __invoke(Request $request): Response
return $this->render(
'pages/home.html.twig',
[
'title' => 'Welcome',
'title' => 'Welcome',
'description' => 'CodelyTV - Backoffice',
]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class MetricsController
final readonly class MetricsController
{
public function __construct(private PrometheusMonitor $monitor)
{
}
public function __construct(private PrometheusMonitor $monitor) {}

public function __invoke(Request $request): Response
{
$renderer = new RenderTextFormat();
$result = $renderer->render($this->monitor->registry()->getMetricFamilySamples());
$result = $renderer->render($this->monitor->registry()->getMetricFamilySamples());

return new Response($result, 200, ['Content-Type' => RenderTextFormat::MIME_TYPE]);
}
Expand Down
Loading