PHPackages                             1tomany/rich-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. 1tomany/rich-bundle

ActiveSymfony-bundle[Framework](/categories/framework)

1tomany/rich-bundle
===================

Symfony bundle that provides easy scaffolding to build RICH applications

v2.0.5(1mo ago)231.1k↓50%[5 issues](https://github.com/1tomany/rich-bundle/issues)MITPHPPHP &gt;=8.4

Since Jan 30Pushed 2mo ago3 watchersCompare

[ Source](https://github.com/1tomany/rich-bundle)[ Packagist](https://packagist.org/packages/1tomany/rich-bundle)[ RSS](/packages/1tomany-rich-bundle/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (30)Versions (78)Used By (0)

RICH Bundle for Symfony
=======================

[](#rich-bundle-for-symfony)

This bundle makes it easy to incorporate the RICH architecture into your Symfony application. RICH stands for Request, Input, Command, Handler, and its goal is to make backend web application development as easy, straightforward, and futureproof as possible. RICH applications apply the single responsibility principle to each action a user can take with your application which guarantees that it can mutate over time without unintended consequences.

RICH philosophy
---------------

[](#rich-philosophy)

The central philosophy behind a RICH application is to apply to backend engineering what Tailwind CSS did to frontend engineering without introducing the unnecessary complexity of hexagonal architecture, domain driven design, and CQRS.

Tailwind decoupled CSS into atomic components that you can apply as classes to HTML elements. This essentially undoes the Cascading in Cascading Style Sheets, but for good reason: you can safely modify the style of one element without much fear that it will radically alter the layout of your application. This is incredibly powerful for teams of developers: the tenured team member knows that the CSS class `.btn-blue` renders a full width block level button with a red background, but the developer that started last week doesn't, and he accidentally destroyed the styling of the application with his first PR.

RICH applies these sames principles to backend engineering: each input, command, and handler are separate PHP classes that only have a single responsibility. The naming of these classes should describe an action that can be performed in your application. For example, if your application allows users to be created and updated, you would have `CreateUserInput` and `UpdateUserInput` as your input classes, `CreateUserCommand` and `UpdateUserCommand` as your command classes, and you guessed it, `CreateUserHandler` and `UpdateUserHandler` as your handler classes. Like Tailwind, this may seem redundant and a source of code duplication, but the benefits an architecture like this provide far outweigh the negatives.

RICH structure
--------------

[](#rich-structure)

**Request** Everything starts with an action taken by an outside actor. The request is content and metadata associated with that action. Almost anything can generate a request: another system that has integrated with your REST API, a user submitting a form, a developer running a console command, or even another module from within your application.

**Input** Once sent, the request content and metadata is mapped onto an input object and validated. By default, this bundle uses the Symfony serializer and validator components, but you're welcome to manually map data and validate it however you see fit.

Input objects can contain some basic logic, but should generally rely on no additional dependencies outside of the standard PHP library.

In this bundle, all input objects must implement the interface defined in `OneToMany\RichBundle\Contract\Action\InputInterface`.

**Command** The input object creates the command object once the request is successfully mapped and validated. A command object is as simple of a class as you can get in PHP. Ideally, it should be `final`, `readonly`, and use constructor promotion to ensure immutability.

A command object is a POPO - Plain Old PHP Object - and should do its best to use scalar primitives (`null`, `bool`, `int`, `float`, and `string`), basic arrays, standard PHP classes, or other value objects.

In other words, a command object would use an `int` (or a simple value object) to refer to the primary key of a Doctrine entity rather than the entity itself. Command objects should be so simple they can easily be serialized and deserialized so they can used in an asynchronous message queue.

In this bundle, all command objects must implement the interface defined in `OneToMany\RichBundle\Contract\Action\CommandInterface`.

**Handler** Once created, the command object is passed to the handler. For the vast majority of applications, this can (and should) be done manually - using an asynchronous message queue is not necessary. A handler should hydrate the environment it needs without assuming it already exists. It should not be aware of an HTTP request, session data, cookie data, or that an entity it relies on is already being managed by Doctrine.

The handler that runs synchronously today may need to be placed in a message queue tomorrow for a variety of reason and having the foresight to make it stateless today will save you endless headaches tomorrow. This is also why you want your handlers to rehydrate your entity map: an entity that existed when the command was pushed onto an asynchronous queue may not exist when the handler is executed.

Each handler should contain the business logic necessary to handle the command passed to it. Ideally, handlers should be `final` and `readonly` as well to ensure they don't accidentally rely on any previous state to handle a command.

[![](https://raw.githubusercontent.com/1tomany/rich-bundle/refs/heads/main/assets/architecture.png)](https://raw.githubusercontent.com/1tomany/rich-bundle/refs/heads/main/assets/architecture.png)

Getting started
---------------

[](#getting-started)

Because this is a new bundle, you'll have to manually create the structure for each module in your application. My goal is to leverage the Symfony Maker Bundle to allow you to create the RICH structure for each action similar to how you would create a Doctrine entity.

### Install the bundle

[](#install-the-bundle)

Install the bundle using Composer:

```
composer require 1tomany/rich-bundle
```

### Create the module structure

[](#create-the-module-structure)

Next, you'll need to create the directory structure for your first module. There is no strict definition on what a module is, other than a set of features that are loosely related to the same domain.

It's easiest to think of a module as being related to each of your "primary" entities where a "primary" entity is one that can (mostly) exist without a parent entity. For example, `Invoice` would be a "primary" entity, but `InvoiceLine` would not be because an `InvoiceLine` can't exist without a parent `Invoice`.

I recommend the following directory structure for each module:

```
src/
  /
    Action/
      Command/
      Event/
      Handler/
        Exception/
      Input/
    Contract/
      Enum/
      Exception/
      Repository/
    Exception/
    Framework/
      Controller/
        API/
        Web/

```

We'll get into the purpose of each of these soon. Use the following command to create this structure for a module named `Account` in your application:

```
./vendor/bin/rich Account
```

Moving forward, lets assume we're working on a module named `Account` for a Doctrine entity also named `Account` which uses a repository (shockingly) named `AccountRepository`.

### Create the module's contracts

[](#create-the-modules-contracts)

As the name implies, the `Contract` directory stores contracts to interact with this module. You should have, at minimum, two contracts to start with: a `RepositoryInterface` and `ExceptionInterface`.

In the `Contract/Repository` directory, you'll find a file named `AccountRepositoryInterface.php` with the following scaffolding:

```
