diff --git a/.env.example b/.env.example index 14c0414..936ac66 100644 --- a/.env.example +++ b/.env.example @@ -10,7 +10,7 @@ APP_SECRET=79e3e28a27aa752548962c961aee5324 # DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" # DATABASE_URL="postgres://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8&charset=utf8mb4" DB_DATABASE=stat_an -DB_HOST=postgres +DB_HOST=stat-an-postgres DB_PORT=5432 DB_USERNAME=postgres DB_PASSWORD=postgres diff --git a/composer.json b/composer.json index 720dbc5..45d05b6 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,8 @@ "doctrine/doctrine-bundle": "^2.14", "doctrine/doctrine-migrations-bundle": "^3.4", "doctrine/orm": "^3.3", + "league/tactician-bundle": "^1.5", + "league/tactician-doctrine": "^1.2", "symfony/console": "7.2.*", "symfony/dotenv": "7.2.*", "symfony/flex": "^2", diff --git a/composer.lock b/composer.lock index e6fc8cf..9aa5f00 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0de5194aa02e73e25f4a87a916a437ef", + "content-hash": "77a2f75dd7bf69a84b35ce22200e7da3", "packages": [ { "name": "doctrine/cache", @@ -1223,6 +1223,313 @@ }, "time": "2025-01-24T11:45:48+00:00" }, + { + "name": "league/tactician", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/tactician.git", + "reference": "e79f763170f3d5922ec29e85cffca0bac5cd8975" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/tactician/zipball/e79f763170f3d5922ec29e85cffca0bac5cd8975", + "reference": "e79f763170f3d5922ec29e85cffca0bac5cd8975", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5.20 || ^9.3.8", + "squizlabs/php_codesniffer": "^3.5.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Tactician\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ross Tuck", + "homepage": "http://tactician.thephpleague.com" + } + ], + "description": "A small, flexible command bus. Handy for building service layers.", + "keywords": [ + "command", + "command bus", + "service layer" + ], + "support": { + "issues": "https://github.com/thephpleague/tactician/issues", + "source": "https://github.com/thephpleague/tactician/tree/v1.1.0" + }, + "time": "2021-02-14T15:29:04+00:00" + }, + { + "name": "league/tactician-bundle", + "version": "v1.5.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/tactician-bundle.git", + "reference": "8acdfffc227295cca945ff1a964f88e4f034cdf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/tactician-bundle/zipball/8acdfffc227295cca945ff1a964f88e4f034cdf9", + "reference": "8acdfffc227295cca945ff1a964f88e4f034cdf9", + "shasum": "" + }, + "require": { + "league/tactician": "^1.0", + "league/tactician-container": "^2.0|^3.0", + "league/tactician-logger": "^0.10|^0.11", + "php": ">=7.2", + "symfony/config": "^3.4|^4.4|^5.0|^6.0|^7.0", + "symfony/dependency-injection": "^3.4|^4.4|^5.0|^6.0|^7.0", + "symfony/http-kernel": "^3.4|^4.4|^5.0|^6.0|^7.0", + "symfony/yaml": "^3.4|^4.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "matthiasnoback/symfony-config-test": "^4.2.1", + "matthiasnoback/symfony-dependency-injection-test": "^4.2.1", + "mockery/mockery": "~1.0", + "phpspec/prophecy": "^1.18", + "phpunit/phpunit": "~8.5", + "symfony/console": "^3.4|^4.4|^5.0|^6.0|^7.0", + "symfony/framework-bundle": "^3.4.31|^4.4|^5.0|^6.0|^7.0", + "symfony/security-bundle": "^3.4|^4.4|^5.0|^6.0|^7.0", + "symfony/security-core": "^3.4|^4.4|^5.0|^6.0|^7.0", + "symfony/validator": "^3.4|^4.4|^5.0|^6.0|^7.0" + }, + "suggest": { + "league/tactician-doctrine": "For doctrine transaction middleware", + "symfony/console": "For debugging command-to-handler routing using the tactician:debug console command", + "symfony/security": "For command security middleware", + "symfony/validator": "For command validator middleware" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Tactician\\Bundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rafael Dohms", + "homepage": "http://doh.ms" + }, + { + "name": "Richard Tuin", + "homepage": "http://www.rtuin.nl/" + }, + { + "name": "Xander Smalbil", + "email": "xander@videofunk.nl" + }, + { + "name": "Ross Tuck", + "email": "me@rosstuck.com" + } + ], + "description": "Bundle to integrate Tactician with Symfony projects", + "keywords": [ + "bundle", + "symfony", + "tactician" + ], + "support": { + "issues": "https://github.com/thephpleague/tactician-bundle/issues", + "source": "https://github.com/thephpleague/tactician-bundle/tree/v1.5.2" + }, + "time": "2025-02-19T08:17:44+00:00" + }, + { + "name": "league/tactician-container", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/tactician-container.git", + "reference": "5150cbf861a51b1b27eba28449b46deee18cc423" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/tactician-container/zipball/5150cbf861a51b1b27eba28449b46deee18cc423", + "reference": "5150cbf861a51b1b27eba28449b46deee18cc423", + "shasum": "" + }, + "require": { + "league/tactician": "^1.0", + "php": ">=7.4", + "psr/container": "~2.0" + }, + "require-dev": { + "league/container": "~4.0", + "phpunit/phpunit": "~6.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Tactician\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nigel Greenway", + "homepage": "http://futurepixels.co.uk" + } + ], + "description": "Tactician integration for any container implementing PSR-11", + "keywords": [ + "container", + "container-interop", + "di", + "interoperable", + "league", + "tactician" + ], + "support": { + "issues": "https://github.com/thephpleague/tactician-container/issues", + "source": "https://github.com/thephpleague/tactician-container/tree/v3.0.0" + }, + "time": "2021-12-16T20:15:05+00:00" + }, + { + "name": "league/tactician-doctrine", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/tactician-doctrine.git", + "reference": "3ae678caddd008da3b54ba77be8dbf6e46e7c06a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/tactician-doctrine/zipball/3ae678caddd008da3b54ba77be8dbf6e46e7c06a", + "reference": "3ae678caddd008da3b54ba77be8dbf6e46e7c06a", + "shasum": "" + }, + "require": { + "doctrine/dbal": "^2.4 || ^3.1.5", + "league/tactician": "^1.1", + "php": ">=7.1" + }, + "require-dev": { + "doctrine/orm": "^2.4", + "mockery/mockery": "^1.3.5", + "phpunit/phpunit": "^7.5.20 || ^9.5.10", + "squizlabs/php_codesniffer": "^3.6.2", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "doctrine/orm": "Required if you need to use ORM Transaction Middleware" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Tactician\\Doctrine\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ross Tuck" + } + ], + "description": "Plugins for Tactician commands using Doctrine, like wrapping every command in a transaction", + "homepage": "https://github.com/thephpleague/tactician-doctrine", + "keywords": [ + "command bus", + "doctrine", + "tactician", + "transactions" + ], + "support": { + "issues": "https://github.com/thephpleague/tactician-doctrine/issues", + "source": "https://github.com/thephpleague/tactician-doctrine/tree/v1.2.0" + }, + "time": "2022-01-17T17:03:37+00:00" + }, + { + "name": "league/tactician-logger", + "version": "v0.11.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/tactician-logger.git", + "reference": "586a9f9781e9481a527010561ae80c0403d3f23d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/tactician-logger/zipball/586a9f9781e9481a527010561ae80c0403d3f23d", + "reference": "586a9f9781e9481a527010561ae80c0403d3f23d", + "shasum": "" + }, + "require": { + "league/tactician": "^1.1", + "php": ">=7.3", + "psr/log": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Tactician\\Logger\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ross Tuck" + } + ], + "description": "Adds PSR-3 logging support to the Tactician command bus", + "homepage": "https://github.com/thephpleague/tactician-logger", + "keywords": [ + "log", + "logging", + "tactician" + ], + "support": { + "issues": "https://github.com/thephpleague/tactician-logger/issues", + "source": "https://github.com/thephpleague/tactician-logger/tree/v0.11.1" + }, + "time": "2025-04-18T13:03:20+00:00" + }, { "name": "psr/cache", "version": "3.0.0", diff --git a/config/bundles.php b/config/bundles.php index de8898b..a304634 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -5,4 +5,5 @@ return [ Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], + League\Tactician\Bundle\TacticianBundle::class => ['all' => true], ]; diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index d08c1c4..9346b25 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -23,11 +23,17 @@ doctrine: Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity auto_mapping: true mappings: - App: - type: attribute + App\Domain\Entity: + type: php + dir: '%kernel.project_dir%/src/Infrastructure/Persistence/Mapping' is_bundle: false - dir: '%kernel.project_dir%/src/Entity' - prefix: 'App\Entity' + prefix: App\Domain\Entity + alias: App + App\Application\ReadModel: + type: php + dir: '%kernel.project_dir%/src/Infrastructure/Persistence/Mapping' + is_bundle: false + prefix: App\Application\ReadModel alias: App controller_resolver: auto_mapping: false diff --git a/config/packages/league_tactician.yaml b/config/packages/league_tactician.yaml new file mode 100644 index 0000000..248ac9a --- /dev/null +++ b/config/packages/league_tactician.yaml @@ -0,0 +1,15 @@ +# Library documentation: http://tactician.thephpleague.com/ +# Bundle documentation: https://github.com/thephpleague/tactician-bundle/blob/v1.0/README.md +tactician: + commandbus: + default: + middleware: +# Security middleware - https://github.com/thephpleague/tactician-bundle#security-middleware-tacticianmiddlewaresecurity +# - tactician.middleware.security +# Validator middleware - https://github.com/thephpleague/tactician-bundle/tree/v1.0#validator-middleware-tacticianmiddlewarevalidator +# - tactician.middleware.validator +# Locking middleware - http://tactician.thephpleague.com/plugins/locking-middleware/ + - tactician.middleware.locking +# Doctrine transactional middleware, requires additional package - https://github.com/thephpleague/tactician-doctrine + - tactician.middleware.doctrine + - command_handler diff --git a/config/routes.yaml b/config/routes.yaml index 41ef814..17a5e55 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,5 +1,4 @@ -controllers: - resource: - path: ../src/Controller/ - namespace: App\Controller - type: attribute +healthcheck: + path: / + controller: App\Infrastructure\Controller\HealthCheck + methods: GET \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 2d6a76f..db34fe6 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -16,9 +16,30 @@ services: App\: resource: '../src/' exclude: - - '../src/DependencyInjection/' - - '../src/Entity/' + - '../src/Domain' + - '../src/Infrastructure/Persistence/Mapping' + - '../src/Application/ReadModel' - '../src/Kernel.php' + App\Application\UseCase\: + resource: '../src/Application/UseCase' + exclude: + - '../src/Application/UseCase/**/*Request.php' + - '../src/Application/UseCase/**/*Context.php' + public: true + + League\Tactician\Handler\Locator\HandlerLocator: '@App\Application\CommandBus\ContainerCommandHandlerLocator' + + App\Infrastructure\CommandBus\CommandBusCaller: '@App\Infrastructure\CommandBus\UnmanagedErrorsCommandBusCaller' + + command_handler: + class: League\Tactician\Handler\CommandHandlerMiddleware + arguments: + $commandNameExtractor: '@tactician.handler.command_name_extractor.class_name' + $handlerLocator: '@App\Application\CommandBus\ContainerCommandHandlerLocator' + $methodNameInflector: '@tactician.handler.method_name_inflector.handle' + + Psr\Container\ContainerInterface: '@service_container' + # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/src/Controller/.gitignore b/src/Application/CommandBus/.gitignore similarity index 100% rename from src/Controller/.gitignore rename to src/Application/CommandBus/.gitignore diff --git a/src/Application/CommandBus/ContainerCommandHandlerLocator.php b/src/Application/CommandBus/ContainerCommandHandlerLocator.php new file mode 100644 index 0000000..39d6c60 --- /dev/null +++ b/src/Application/CommandBus/ContainerCommandHandlerLocator.php @@ -0,0 +1,27 @@ +container->has($commandHandler)) { + throw MissingHandlerException::forCommand($commandName); + } + + return $this->container->get($commandHandler); + } +} diff --git a/src/Entity/.gitignore b/src/Application/ReadModel/.gitignore similarity index 100% rename from src/Entity/.gitignore rename to src/Application/ReadModel/.gitignore diff --git a/src/Repository/.gitignore b/src/Application/UseCase/.gitignore similarity index 100% rename from src/Repository/.gitignore rename to src/Application/UseCase/.gitignore diff --git a/src/Application/UseCase/HealthCheck.php b/src/Application/UseCase/HealthCheck.php new file mode 100644 index 0000000..a1005cf --- /dev/null +++ b/src/Application/UseCase/HealthCheck.php @@ -0,0 +1,13 @@ +test; + } +} diff --git a/src/Application/UseCase/HealthCheckRequest.php b/src/Application/UseCase/HealthCheckRequest.php new file mode 100644 index 0000000..201a421 --- /dev/null +++ b/src/Application/UseCase/HealthCheckRequest.php @@ -0,0 +1,13 @@ +commandBus->handle($request); + } +} diff --git a/src/Infrastructure/Console/.gitignore b/src/Infrastructure/Console/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/Infrastructure/Controller/.gitignore b/src/Infrastructure/Controller/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/Infrastructure/Controller/AController.php b/src/Infrastructure/Controller/AController.php new file mode 100644 index 0000000..4b92be8 --- /dev/null +++ b/src/Infrastructure/Controller/AController.php @@ -0,0 +1,21 @@ +commandBusCaller->callCommandBus($request); + } +} diff --git a/src/Infrastructure/Controller/HealthCheck.php b/src/Infrastructure/Controller/HealthCheck.php new file mode 100644 index 0000000..4e3e8d8 --- /dev/null +++ b/src/Infrastructure/Controller/HealthCheck.php @@ -0,0 +1,24 @@ +callCommandBus(new HealthCheckRequest('ok')); + + return new JsonResponse( + [ + 'status' => $healthcheck, + ] + ); + } +} diff --git a/src/Infrastructure/Persistence/Mapping/.gitignore b/src/Infrastructure/Persistence/Mapping/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/Infrastructure/Persistence/Type/.gitignore b/src/Infrastructure/Persistence/Type/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/symfony.lock b/symfony.lock index 193c772..df86b52 100644 --- a/symfony.lock +++ b/symfony.lock @@ -38,6 +38,18 @@ ".php-cs-fixer.dist.php" ] }, + "league/tactician-bundle": { + "version": "1.5", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "1.0", + "ref": "222c3d39d38378bc6a9790a0b5baf841ba6679b9" + }, + "files": [ + "config/packages/league_tactician.yaml" + ] + }, "phpstan/phpstan": { "version": "2.1", "recipe": {