{"id":33997084,"url":"https://github.com/darkorsa/cordo","last_synced_at":"2025-12-13T08:53:31.211Z","repository":{"id":41949255,"uuid":"220642817","full_name":"darkorsa/cordo","owner":"darkorsa","description":"PHP microframework for robust API development with CQRS","archived":false,"fork":false,"pushed_at":"2025-04-10T08:48:26.000Z","size":823,"stargazers_count":19,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-10T09:44:20.535Z","etag":null,"topics":["api","command-bus","cqrs","events","layered-architecture","micro-framework","php","queues"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/darkorsa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-09T13:01:31.000Z","updated_at":"2025-04-10T08:48:30.000Z","dependencies_parsed_at":"2025-03-21T16:15:29.740Z","dependency_job_id":null,"html_url":"https://github.com/darkorsa/cordo","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/darkorsa/cordo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkorsa%2Fcordo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkorsa%2Fcordo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkorsa%2Fcordo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkorsa%2Fcordo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/darkorsa","download_url":"https://codeload.github.com/darkorsa/cordo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkorsa%2Fcordo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27702790,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-13T02:00:09.769Z","response_time":147,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","command-bus","cqrs","events","layered-architecture","micro-framework","php","queues"],"created_at":"2025-12-13T08:53:30.379Z","updated_at":"2025-12-13T08:53:31.197Z","avatar_url":"https://github.com/darkorsa.png","language":"PHP","readme":"# cordo\n\nCordo is a microframework designed for efficient development of REST APIs using principles such as:\n\n- Layered architecture\n- CQRS (Command Query Responsibility Segregation)\n- Event Dispatcher\n- Repository pattern\n- Queues\n- OAuth2\n- UUIDs\n- Package by feature\n- Minimal config approach\n\nIt's compliant with PSRs: `PSR-1`, `PSR-2`, `PSR-3`, `PSR-4`, `PSR-7`, `PSR-11`, `PSR-15`, `PSR-18`.\n\nMain goal to create this framework was to have an efficient tool to build API based applications with good architectural foundations and as little \"magic\" and configuration as possible.\n\n**Note:** Cordo is still in development. Some of the basic features are implemented but tests are still missing. Please keep that in mind.\n\n## Requirements\n\n- PHP 8.1 or newer\n- Apache/Nginx\n\n### Optional requirements\n- MySql 5.7 (default DB driver)\n- PHP Redis extension (driver for queues)\n\n## Install\n\nTo create a new project go to your project folder and within this folder type:\n\n``` bash\n$ composer create-project darkorsa/cordo ./ -s dev\n```\n\nNext copy `.env_example` file and rename it to `.env`. Then fill it with your configuration data.\n\nOptionally you can run console command:\n\n``` bash\n$ php cordo core:init --withOAuth --withUuid\n```\n\nIt will create:\n- All the neccessary DB tables for OAuth2\n- Uuid DB helper functions\n\n## Still missing\n- Caching - for caching you can check [Cordo Gateway](https://github.com/darkorsa/cordo-gateway)\n\n## How things work\n\nCordo does not reinvent the wheel. It's basically a set of popular PHP libraries put together and configured in order to create a simple framework that is in compliance with good programming practices for modern PHP.\n\nSome of the used libraries:\n\n- [Doctrine](https://www.doctrine-project.org/)\n- [OAuth2](https://bshaffer.github.io/oauth2-server-php-docs/)\n- [Fast Route](https://github.com/nikic/FastRoute)\n- [Tactician](https://tactician.thephpleague.com/)\n- [Fractal](https://fractal.thephpleague.com/)\n- [Plates](http://platesphp.com/)\n- [Monolog](https://github.com/Seldaek/monolog)\n- [PHP-DI](http://php-di.org/)\n- [Bernard](https://bernard.readthedocs.io/)\n- [Whoops](http://filp.github.io/whoops/)\n- [Relay](https://relayphp.com/)\n- [Guzzle](http://docs.guzzlephp.org/en/stable/)\n- [Symfony Console](https://symfony.com/doc/current/components/console.html)\n- [Laminas ACL](https://docs.laminas.dev/laminas-permissions-acl/)\n- [Laminas Mail](https://docs.laminas.dev/laminas-mail/)\n\nThis documentation does not focus on describing in detail how to deal with Routes, Command Bus, DI Container, querying DB, etc., for that use the documentation of the relevant library.\n\nYou are also encouraged to find for yourself how things work under the hood, check the [Cordo Core](https://github.com/darkorsa/cordo-core) library where abstract classes and interfaces are located.\n\nIf you want to see how the code can be organizen within all the layers, how to utilize of `CQRS`, `Repository Pattern`, `Events`, `Queues`, etc. take a look at the [Users Bundle](https://github.com/darkorsa/cordo-bundle-users). Check the installation instructions [here](https://github.com/darkorsa/cordo-bundle-users/blob/master/README.md).\n\n### Entry points\n\nEntry points to the application:\n\n#### Web\n\nEntry point for HTTP requests is `public/index.php`. Your apache/nginx configuration should point to the `public` folder.\n\n#### Console\n\nCommand-line commands are handled with use of [Symfony Console](https://symfony.com/doc/current/components/console.html) component.\n\nYou can fire your commands through the command-line with:\n``` shell\nphp cordo [command_name]\n```\n\nList currently registered commands:\n``` shell\nphp cordo list\n```\n\nGlobal commands should be registered in `./cordo` file by adding them to the application object:\n\n``` php\n$application-\u003eadd(new SecurityCheckerCommand(new SecurityChecker()));\n```\n\nFeature commands should be registered in `app/[Context]/[PackageName]/UI/Console/commands.php` file.\n\n``` php\nreturn [\n    YourConsoleCommand::class,\n    AnotherConsoleCommand::class\n];\n```\n\n### Registering new package\n\nThis framework uses package by feature approach. It means that you organize your code in packages placed in `app/` folder.\n\nJust add your package folder name to the `app/Register.php`:\n\n``` php\nprotected static $register = [\n    'Shop\\Users',\n    // add you packages here\n];\n```\n\nOnce you package is registered, framework will have access to defined routes, DI container definitions, configs, commands, etc.\n\n### Package structure\n\nUsers package is shipped by default with implemented basic CRUD actions.\n\nHere's how the code is organised:\n\n``` bash\napp/Shop/Users/\n.\n├── Application\n│   ├── Acl\n│   │   └── UsersAcl.php\n│   ├── Command\n│   │   ├── CreateNewUser.php\n│   │   ├── DeleteUser.php\n│   │   ├── Handler\n│   │   │   ├── CreateNewUserHandler.php\n│   │   │   ├── DeleteUserHandler.php\n│   │   │   ├── SendUserWelcomeMessageHandler.php\n│   │   │   └── UpdateUserHandler.php\n│   │   ├── SendUserWelcomeMessage.php\n│   │   └── UpdateUser.php\n│   ├── Event\n│   │   ├── Listener\n│   │   │   └── UserCreatedListener.php\n│   │   └── Register\n│   │       └── UsersListeners.php\n│   ├── Query\n│   │   ├── UserFilter.php\n│   │   ├── UserQuery.php\n│   │   └── UserView.php\n│   ├── Service\n│   │   └── UserQueryService.php\n│   ├── config\n│   │   └── users.php\n│   ├── definitions.php\n│   └── handlers.php\n├── Domain\n│   ├── Event\n│   │   └── UserCreated.php\n│   ├── User.php\n│   ├── UserActive.php\n│   ├── UserEmail.php\n│   ├── UserId.php\n│   ├── UserPassword.php\n│   ├── UserPasswordHash.php\n│   └── UserRepository.php\n├── Infrastructure\n│   └── Persistance\n│       └── Doctrine\n│           ├── ORM\n│           │   ├── Metadata\n│           │   │   └── App.Shop.Users.Domain.User.dcm.xml\n│           │   └── UserDoctrineRepository.php\n│           └── Query\n│               ├── UserDoctrineFilter.php\n│               └── UserDoctrineQuery.php\n├── UI\n│   ├── Console\n│   │   ├── Command\n│   │   │   └── CreateNewUserConsoleCommand.php\n│   │   └── commands.php\n│   ├── Http\n│   │   ├── Auth\n│   │   │   └── OAuth2UserCredentials.php\n│   │   ├── Controller\n│   │   │   ├── UserCommandsController.php\n│   │   │   └── UserQueriesController.php\n│   │   ├── Middleware\n│   │   │   └── OAuthMiddleware.php\n│   │   └── Route\n│   │       ├── OAuthRoutes.php\n│   │       └── UsersRoutes.php\n│   ├── Transformer\n│   │   └── UserTransformer.php\n│   ├── Validator\n│   │   ├── EmailExistsValidation.php\n│   │   ├── NewUserValidator.php\n│   │   └── UpdateUserValidator.php\n│   ├── trans\n│   │   ├── mail.en.yaml\n│   │   ├── mail.es.yaml\n│   │   └── mail.pl.yaml\n│   └── views\n│       └── mail\n│           └── new-user-welcome.php\n└── UsersInit.php\n```\n\nThis structure consists of layers: **User Interface**, **Application**, **Domain** and **Infrastructure**.\n\n**Important!**\n\nIf you want to quickly boilerplate your new module there's a command for that:\n\n``` bash\nphp cordo core:module-builder \u003ccontext\u003e \u003cmodule_name\u003e [module_archive_file]\n```\n\nyou can find pre-prepared archive in `app/resources/module`.\n\nThat can save you a lot of time as this will generate the whole structure of folders and files for typycial CRUD.\n\n### Routes\n\nRoute definitons should be located at `app/[Context]/[PackageName]/UI/Http/Route/[PackageName]Routes.php` file. Routes loader class should inherit from abstract class `Cordo\\Core\\Application\\Service\\Register\\RoutesRegister`.\n\nRouting is done with use of [FastRoute](https://github.com/nikic/FastRoute) but modified allowing to use per route `Middlewares`.\n\nPerferable way to generate API documentation is [ApiDoc](http://apidocjs.com) but that can be changed according to individual preferences.\n\n### Dependency Injection Container\n\nDI Conteriner definitions should be placed in `app/[Context]/[PackageName]/Application/definitions.php`.\n\nCordo uses [PHP-ID](http://php-di.org/) for DI Container, if you need to find out more check the [documentation](http://php-di.org/doc/).\n\n### Config\n\nGlobal config files should be located at *config/* dir, while feature configs location should be: `app/[Context]/[PackageName]/Application/config/`\n\nConfig files should return PHP associative arrays. Multidimensional arrays are supproted.\n\nUsage:\n``` php\n$limit = $config-\u003eget('users.limit');\n```\nwhere \"users\" is the name of the config file and the following segments are array associative keys.\n\n### Database\n\nDatabase configuration is located at `bootstrap/db.php` file. Framework uses [Doctrine](https://www.doctrine-project.org/) for database storage and object mapping.\n\nAccording to the CQRS approach preferable way is to use [Doctrine ORM](https://www.doctrine-project.org/projects/orm.html) for storing and [Doctrine DBAL](https://www.doctrine-project.org/projects/dbal.html) for querying.\n\nDoctine is preconfigured to support [UUID](https://github.com/ramsey/uuid-doctrine).\n\n[XML Mapping](https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/xml-mapping.html) also is supported so you can map your Domain Models directly with database tables. You should place your mappings in `app/[Context]/[PackageName]/Infractructure/Doctrine/ORM/Metadata/`.\n\nWhen you have your mappings ready you can create/update/drop schema directly from composer:\n\n``` php\ncomposer schema-create\ncomposer schema-update\ncomposer schema-drop\n```\n\n### Mailer\n\nFor mail [Laminas Mail](https://docs.laminas.dev/laminas-mail/) is used. Currently there are 2 drivers for mail transport `log` (for development) and `smtp`. You can define your mail driver and credentials in `config/mail.php` file.\n\nIn order to add/change drivers you can just replace `MailerFactory` class with your own in `bootstrap/app.php`.\n\n### Internationalization ###\n\nTranslations are handled with [Symfony Translations](https://symfony.com/doc/current/translation.html). Default file format for translations is `Yaml`. \n\nYou should place your translation files in `app/[Context]/[PackageName]/UI/trans/` folder or subfolders. Naming convention is: `[domain].[locale].yaml`, for example: `mail.en.yaml`.\n\nUsage:\n``` php\n$translator-\u003etrans('hello', [], 'mail', 'en')\n```\n\nYou can set per request locale by adding `\u0026lang` parameter to request uri or by adding `--lang` flag while running console command.\n\nConfig file for translations is located at `config/trans.php`.\n\n### Views ###\n\n[Plates](http://platesphp.com/) is used as a template engine. Place your view files in `app/[Context]/[PackageName]/UI/views/` folder or subfolders.\n\nUsage:\n``` php\n$templates-\u003erender('[context].[module]::[subfolder]/[template_name]', [\n    // data you want to pass to the template file\n]);\n```\n\n### Command bus\n\nCordo uses [Tactician](https://tactician.thephpleague.com/) command bus for implementing `Command Pattern`.\n\nYour Command -\u003e Handler mappings should be placed in: `app/[Context]/[PackageName]/Application/handlers.php` file.\n\nCommand bus is configured to lock each handler in seperate transaction, it also supports events, queues, command logging. Check `bootstrap/command_bus.php` and [Tactician](https://tactician.thephpleague.com/) documentation for details.\n\n### Events\n\nIn contrast to the Command -\u003e Handler mapping where for one *Command* there can be one and only one *Handler* you can have several listeners for a single emmited event.\n\nYour listeners definitions should be located at: `app/[Context]/[PackageName]/Event/Loader/[PackageName]Listeners.php`. Events loaders class should extend `Cordo\\Core\\Application\\Service\\Register\\ListenersRegister`.\n\nHere is how you can emit an event:\n\n``` php\n/**\n * @var League\\Event\\EmitterInterface\n */\n$emitter-\u003eemit('users.created', new UserCreated($command-\u003eemail()));\n```\n\nDefine your listeners in `app/[Context]/[PackageName]/Application/events.php` file (see example in Users module):\n\n``` php\n$emitter-\u003eaddListener(\n    'users.created',\n    static function ($event, UserCreated $userCreated) use ($container) {\n        $listener = new UserCreatedListener($container);\n        $listener-\u003ehandle($userCreated);\n    }\n);\n```\n\n#### Queues\n\nFor background processing [Bernard](https://bernard.readthedocs.io/) library is used.\n\nCurrently supported drivers are:\n\n- Flatfile\n- Redis Extension\n\nThe config file for queues where you can specify the driver to be used in your application is located here `config/queue.php`.\n\nIf you want to make your Command to be queued just make it implementing `Cordo\\Core\\Application\\Queue\\MessageInterface` interface\nor just extend `Cordo\\Core\\Application\\Queue\\AbstractMessage` class.\n\nTo launch background process that will process queued commands run in the console:\n\n``` shell\nphp queue-worker \u0026\n```\n\nTo better understand how to deal with events check [Users Bundle](https://github.com/darkorsa/cordo-bundle-users) module and see how welcome message is being sent for newly created users.\n\n\u003e **Note**: if you'd like to use the *Redis driver* for queues make sure that you have Redis extension for PHP installed. You can install it from [PECL](https://pecl.php.net/) repository:\n\n``` bash\n$ sudo pecl install redis\n$ sudo service php7.4-fpm restart\n``` \n\n### OAuth2\n\nThis framework is shipped with OAuth2 as the default authorization method. OAuth2 servers configuration is located at: `app/config/auth.php`.\n\nFor real life OAuth2 implementation please check [Users Bundle](https://github.com/darkorsa/cordo-bundle-users))\n\n### ACL\n\nFor the purpose of Authorization [Zend ACL](https://docs.zendframework.com/zend-permissions-acl/usage/) has been used. ACL roles, resources, permissions cen be defined seperately in each package in `app/[Context]/[PackageName]/Application/Acl/[PackageName]Acl.php` which should extend `Cordo\\Core\\Application\\Service\\Register\\AclRegister`.\n\nIn `Auth` package that is shipped with  [Users Bundle](https://github.com/darkorsa/cordo-bundle-users) there are CRUD actions prepared for users ACL rules.\n\n### Errors\n\nBy default all the errors are logged to the `storage/logs/error.log` file.\n\nAdditionally in `dev` environment errors will be prompth to the screen in pretty format using [Whoops](https://github.com/filp/whoops). Errors in console are also pretty formated. In `production` environment errors stack traces will be emailed to the addresses defined in `config/error.php`.\n\nIf you'd like to change any of that bevavior you can to it in: `bootstrap/error.php` file.\n\n## Credits\n\n- [Dariusz Korsak][link-author]\n- [All Contributors][link-contributors]\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n\n[link-author]: https://github.com/darkorsa\n[link-contributors]: ../../contributors\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarkorsa%2Fcordo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdarkorsa%2Fcordo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarkorsa%2Fcordo/lists"}