This repository serves as a foundational Symfony Clean Architecture template, drawing inspiration from the ideas of Robert C. Martin (Uncle Bob) on Clean Architecture and Domain-Driven Design (DDD) by Robert C. Martin.
The template demonstrates an exemplary implementation of a users API using Symfony Messenger, league/oauth2-server-bundle and knpuniversity/oauth2-client-bundle bundles.
It includes the implementation of the social
grant, designed specifically to handle social login functionalities. By utilizing the social
grant, users gain the ability to log in to the application using any OAuth2 client provider, such as Telegram. If you wish to integrate additional OAuth2 client providers, you can easily do so by modifying the configuration in config/packages/knpu_oauth2_client.yaml.
The project follows a Clean Architecture structure to ensure a clear separation of concerns. The main directories are:
- api: Contains the application source code.
- src/Domain: The core domain logic and entities of the application.
- src/Application:
Bounded Context
/UseCase: Use Cases represent the actions which encapsulate the business logic and interact with the domain layer to achieve specific tasks.Bounded Context
/DomainEventHandler: Domain event handlers are responsible for handling domain events raised within the domain layer.Bounded Context
/IntegrationEventHandler: Integration event handlers are responsible for handling integration events that are received from other bounded contexts or external systems.
- src/Infrastructure: External tools and frameworks integration, such as databases, third-party services, etc.
- docker: Contains configs related to docker containers
- shared: Shared files
If you like or are using this project to learn or start your solution, please give it a star. Thanks!
Ensure you have the following installed:
and these ports are open:
80
,443
,8080
for Traefik8082
for Nginx9000
for PHP-FPM3306
for MariaDB1025
,8025
for MailHog (an SMTP-testing server)
- Clone this repository to your local machine:
git clone https://github.com/pikaso443/retrans-live
- Create
.env
file in the root folder and set your variables by copying .env.dev_local
TRAEFIK_TOKEN
- Traefik tokenTRAEFIK_API_HOST
- Traefik will use the variable to create a certificate with Let's Encrypt. Make sure DNS records are set correctly.
- Create secrets in docker/shared/secrets/:
db_root_password
,db_password
- DB root and application user passwordsoauth2_encryption.key
,oauth2_private.key
,oauth2_public.key
- OAuth2 Server Encryption, Private and Public keystelegram_bot_token
- OAuth2 Client Telegram Bot token
- Now you can run the project, install dependencies, run migrations and fixtures:
docker-compose up -d
docker-compose exec php-cli bash -ilc "cd /var/www/api && composer install && bin/console do:mi:mi --no-interaction"
docker-compose restart
docker-compose exec php-cli bash -ilc "/var/www/api/bin/console do:fi:lo --no-interaction"
- Done! ⭐
- Traefik starts on port
80
,443
,8080
. Accesshttps://TRAEFIK_API_HOST
using any browser - Nginx starts on port
8082
: localhost:8082/doc - Mailhog HTTP server starts on port
8025
: localhost:8025
In the context of Domain-Driven Design (DDD) and event-driven architectures, Domain Events, Integration Events and Infrastructure Events are mechanisms for handling events and interactions within a system. However, they serve different purposes and are used in different contexts.
Domain Events are a core concept in DDD and represent significant state changes or business occurrences within the domain model. They are used to communicate important business events or facts that have happened within the application's domain. Domain Events are raised from within the domain entities or aggregate roots and are typically used to trigger side effects or update other parts of the system.
Example of a Domain Event: UserCreatedEvent, which is raised when a new user is created. Other DomainEventHandlers within the same domain model can listen to this event (e.g. SendEmailOnUserCreatedEventHandler).
Integration Events are events that facilitate communication and integration between different bounded contexts or external systems. They are used to signal changes or facts to other parts of the system, often outside the core domain.
Example of a Integration Event: UserCreatedIntegrationEvent, which is mapped from UserCreatedEvent in IntegrationEventMapper and dispatched by DomainEventSubscriber. An IntegrationEventHandler from another bounded context is subscribed to it.
Infrastructure Events are events which are raised by the framework (e.g. Doctrine's LifecycleEventArgs
which is handled by DomainEventSubscriber).
Integration Events are asynchronous by default. In order to make a Domain Event asynchronous, modify messenger.php:
'routing' => [
\Symfony\Component\Mailer\Messenger\SendEmailMessage::class => ['async'],
\Symfony\Component\Notifier\Message\MessageInterface::class => ['async'],
\App\Application\Common\IntegrationEvent\IntegrationEventInterface::class => ['async'],
\App\Domain\User\Event\UserCreatedEvent::class => ['async'],
],
Also, you may want to make a Handler to work only with asynchronous events:
#[AsMessageHandler(fromTransport: 'async')]
readonly class YourEventHandler implements HandlerInterface
{
public function __invoke(Input $input): void {}
}