This project follow the Event Sourcing pattern described on Microsoft Site.
This project have the follow features
1. The Event Store is the authoritative data source. All events are stored using an append-only operation.
2. Subscribers build materialized views.
3. External systems and applications have available all domain events by message queue.
PHP 7.4
MySQL 8
RabbitMQ 3.7
- Install Docker
- Clone this project:
git clone https://github.com/pgrau/task
- Move to the project folder:
cd task
- All params are defined on
.env
file
-
Install all the dependencies and bring up the project with Docker executing:
./build.sh start
-> run docker images./build.sh configure
-> configure all dependencies project -
This app is only available by Command Interface Line You can see all availables command executing:
docker exec -it task-php bin/console
You can see all domain event published on RabbitMQ - exchange
domain_events
onhttp://localhost:8090 (guess/guess)
This project tries to be a Task Manager App.
You can:
-
Create users
docker exec -it task-php bin/console user:create
-
Create Tasks
docker exec -it task-php bin/console task:create
-
Get Tasks assigned to user today
docker exec -it task-php bin/console task:find:user:today
This repository follow the Ports and Adapters / Hexagonal Architecture pattern.
src
├── Application
│ ├── Task
│ │ ├── CreateTask
│ │ │ ├── CreateTaskCommand.php
│ │ │ └── CreateTaskHandler.php
│ │ ├── GetTask
│ │ │ ├── GetTaskHandler.php
│ │ │ └── GetTaskQuery.php
│ │ └── GetTasksByUserAndDate
│ │ ├── GetTasksByUserAndDateHandler.php
│ │ └── GetTasksByUserAndDateQuery.php
│ └── User
│ ├── CreateUser
│ │ ├── CreateUserCommand.php
│ │ └── CreateUserHandler.php
│ └── GetUsers
│ ├── GetUsersHandler.php
│ └── GetUsersQuery.php
├── Domain
│ ├── Event
│ │ ├── Task
│ │ │ └── CreatedTaskSubscriber.php
│ │ └── User
│ │ └── CreatedUserSubscriber.php
│ └── Model
│ ├── Common
│ │ ├── Aggregate
│ │ │ └── AggregateRoot.php
│ │ ├── Event
│ │ │ ├── DomainEvent.php
│ │ │ ├── DomainEventPublisher.php
│ │ │ ├── DomainEventSubscriber.php
│ │ │ ├── EventBus.php
│ │ │ └── EventStore.php
│ │ ├── Exception
│ │ │ ├── ConflictException.php
│ │ │ └── NotFoundException.php
│ │ └── ValueObject
│ │ └── UuidIdentifier.php
│ ├── Task
│ │ ├── Description.php
│ │ ├── MaxCharactersAllowedException.php
│ │ ├── Priority.php
│ │ ├── PriorityNotAllowedException.php
│ │ ├── Summary.php
│ │ ├── Task.php
│ │ ├── TaskCreatedV1.php
│ │ ├── TaskId.php
│ │ ├── TaskNotFoundException.php
│ │ ├── TaskProjection.php
│ │ ├── TaskRead.php
│ │ ├── TaskReadRepository.php
│ │ └── TaskTrait.php
│ └── User
│ ├── User.php
│ ├── UserCreatedV1.php
│ ├── UserId.php
│ ├── UserProjection.php
│ ├── UserRead.php
│ ├── UserReadRepository.php
│ └── UserTrait.php
└── Infrastructure
├── Bus
│ ├── Command
│ │ └── ThePhpLeague
│ │ └── ThePhpLeagueCommandBus.php
│ ├── Event
│ │ └── CommandEventBus.php
│ └── Query
│ └── ThePhpLeague
│ ├── QueryBus.php
│ └── ThePhpLeagueQueryBus.php
├── DI
│ └── ThePhpLeague
│ ├── CommandHandlerProvider.php
│ ├── Container.php
│ ├── ProjectionProvider.php
│ ├── QueryHandlerProvider.php
│ ├── RepositoryProvider.php
│ ├── ServiceProvider.php
│ └── SubscriberProvider.php
├── MessageBroker
│ ├── NullPublisher.php
│ └── RabbitMq
│ ├── RabbitMqConfigurer.php
│ ├── RabbitMqConnection.php
│ └── RabbitMqPublisher.php
├── Persistence
│ ├── EventStore
│ │ └── MySql
│ │ └── MySqlDoctrineDbalEventStore.php
│ ├── Task
│ │ └── MySql
│ │ └── MySqlDoctrineDbalTaskReadRepository.php
│ └── User
│ └── MySql
│ └── MySqlDoctrineDbalUserReadRepository.php
├── Projection
│ ├── Task
│ │ └── MySql
│ │ └── MySqlDoctrineDbalTaskProjection.php
│ └── User
│ └── MySql
│ └── MySqlDoctrineDbalUserProjection.php
└── UI
└── Command
├── Database
│ ├── CreateDatabaseCommand.php
│ └── CreateSchemaCommand.php
├── Message
│ └── CreateExchangeCommand.php
├── Task
│ ├── GetTasksByUserAndSheduledTodayCommand.php
│ └── TaskCreateCommand.php
└── User
└── UserCreateCommand.php
This project separate WRITE / READ models.
WRITE models have behavior (we avoid setters)
READ models are immutable because they are simple, cacheable and predictable.
All models must be always VALID
We avoid auto-generated identifiers. We use UIIDs
We use command bus for all use cases need write
All commands are executed with transactional mode
We use query bus for all use cases need only read
All queries are executed without transaction
We use event bus for
- Append the domain events to Event Store
- Execute subscribers subscribed to domain events
- Publish all domain event to message queue
This project contain unit and integration test and we cover 100% of code on domain and application layers.
You can execute the code coverage with the follow command:
./build.sh metrics
The report is generated on metrics
folder with HTML format