Laravents

Eloquent for events. Modules for Laravel. Commands, queries and events with conventions, attributes and an outbox, so modules talk through a public API and never through each other's models.Eloquent para eventos. Módulos para Laravel. Comandos, consultas y eventos con convenciones, atributos y un outbox, para que los módulos hablen a través de una API pública y nunca a través de los modelos de otro.Eloquent per gli eventi. Moduli per Laravel. Comandi, query ed eventi con convenzioni, attributi e un outbox, così i moduli parlano attraverso un'API pubblica e mai attraverso i modelli degli altri.

Laravel 13 PHP 8.3+ code: MIT
Laravel's shapeLa forma de LaravelLa forma di Laravel

Built from Laravel's own piecesConstruido con las piezas de LaravelCostruito con i pezzi di Laravel

What Eloquent is to your database, Laravents is to your messages. Where Laravel already has a shape for something, Laravents takes that shape and invents nothing of its own, so Laravel's own documentation keeps working for you.Lo que Eloquent es para tu base de datos, Laravents lo es para tus mensajes. Donde Laravel ya tiene una forma para algo, Laravents toma esa forma y no inventa nada propio, así que la documentación de Laravel te sigue sirviendo.Ciò che Eloquent è per il tuo database, Laravents lo è per i tuoi messaggi. Dove Laravel ha già una forma per qualcosa, Laravents prende quella forma e non inventa nulla di suo, così la documentazione di Laravel continua a valere per te.

LaraventsLaravel's pieceLa pieza de LaravelIl pezzo di Laravel
Dispatching commands, queries and eventsDespachar comandos, consultas y eventosInviare comandi, query ed eventiThe Bus dispatcher and PipelineEl dispatcher del Bus y el PipelineIl dispatcher del Bus e la Pipeline
Message metadata: ids and the actorMetadatos del mensaje: ids y el actorMetadati del messaggio: id e l'attoreContext
Asynchronous handlersHandlers asíncronosHandler asincroniThe queue, its retries and failed_jobsLa cola, sus reintentos y failed_jobsLa coda, i suoi tentativi e failed_jobs
#[Authorize]The Gate, with the routing key as the abilityEl Gate, con la routing key como abilityIl Gate, con la routing key come ability
Handlers and their dependenciesLos handlers y sus dependenciasGli handler e le loro dipendenzeThe container, contextual attributes includedEl contenedor, atributos contextuales incluidosIl container, attributi contestuali inclusi
rules() at the HTTP gatewayrules() en el gateway HTTPrules() nel gateway HTTPThe validator, with Laravel's 422El validador, con el 422 de LaravelIl validatore, con il 422 di Laravel
Laravents::fake()The shape of Event::fake()La forma de Event::fake()La forma di Event::fake()
GeneratorsGeneradoresGeneratoriGeneratorCommand, with publishable stubsGeneratorCommand, con stubs publicablesGeneratorCommand, con stub pubblicabili
The registry cacheLa caché del registroLa cache del registrophp artisan optimize and optimize:clearphp artisan optimize y optimize:clearphp artisan optimize e optimize:clear
Outbox pruningLimpieza del outboxPulizia dell'outboxMassPrunable and model:pruneMassPrunable y model:pruneMassPrunable e model:prune
Live demoDemo en vivoDemo dal vivo

The modules at a glanceLos módulos de un vistazoI moduli a colpo d'occhio

Real output from the example application: its handlers, its model publications, its modules and the outbox connections, in Laravel's own about command.Salida real de la aplicación de ejemplo: sus handlers, sus publicaciones de modelos, sus módulos y las conexiones del outbox, en el propio comando about de Laravel.Output reale dell'applicazione di esempio: i suoi handler, le sue pubblicazioni di modelli, i suoi moduli e le connessioni dell'outbox, nel comando about di Laravel.

artisan
$ php artisan about --only=laravents
Laravents .................................................................. HTTP gateway ........................................... Enabled, /laravents Handlers ................................... 4 commands / 1 query / 3 events Model publications ....................................................... 2 Modules ........................................... 3 (Band, FanClub, Press) Outbox connections ................................................. default Registry ........................................................... Scanned

And the boundaries, checked in CI: modules use each other only through their Api, with no cycles.Y los límites, verificados en CI: los módulos se usan entre sí solo a través de su Api, sin ciclos.E i confini, verificati in CI: i moduli si usano tra loro solo attraverso la loro Api, senza cicli.

artisan
$ php artisan laravents:verify

  Module graph ..................................................... 3 modules
  FanClub
  Press -> Band

   INFO  3 modules verified, no violations.
Thirty secondsTreinta segundosTrenta secondi

A command, its handler, an event, a listenerUn comando, su handler, un evento, un listenerUn comando, il suo handler, un evento, un listener

Every sample is from the example application in workbench/, which the test suite runs: a Beatles fan-club site, built by Luciano, a fan and a developer.Cada ejemplo viene de la aplicación de ejemplo en workbench/, que ejecuta la suite de pruebas: un sitio de fans de los Beatles, hecho por Luciano, fan y desarrollador.Ogni esempio viene dall'applicazione di esempio in workbench/, che la suite di test esegue: un sito di fan dei Beatles, costruito da Luciano, fan e sviluppatore.

Command A plain object with Sendable; its routing key, band.give_nickname, comes from its module and its nameUn objeto simple con Sendable; su routing key, band.give_nickname, sale de su módulo y su nombreUn oggetto semplice con Sendable; la sua routing key, band.give_nickname, viene dal suo modulo e dal suo nome
final readonly class GiveNickname implements ValidatesPayload
{
    use Sendable;

    public function __construct(public int $memberId, public string $nickname) {}

    public static function rules(): array
    {
        return [
            'memberId' => 'required|integer|min:1',
            'nickname' => 'required|string|max:40',
        ];
    }
}
Handler Found by attribute; #[Authorize] asks the Gate, #[Transactional] wraps it in a transaction, #[Expose] serves it over HTTPEncontrado por atributo; #[Authorize] consulta el Gate, #[Transactional] lo envuelve en una transacción, #[Expose] lo sirve por HTTPTrovato tramite attributo; #[Authorize] interroga il Gate, #[Transactional] lo avvolge in una transazione, #[Expose] lo serve via HTTP
#[CommandHandler]
#[Authorize]
#[Transactional]
#[Expose]
public function give(GiveNickname $command, #[CurrentUser] Authenticatable $actor): int
{
    return Nickname::query()->create([
        'given_by' => $actor->getAuthIdentifier(),
        'member_id' => $command->memberId,
        'nickname' => $command->nickname,
    ])->id;
}
Event The model publishes it when it is created, inside the command's transactionEl modelo lo publica cuando se crea, dentro de la transacción del comandoIl modello lo pubblica quando viene creato, dentro la transazione del comando
#[Publishes(NicknameGiven::class, on: 'created')]
final class Nickname extends Model
{
    protected $fillable = ['given_by', 'member_id', 'nickname'];
}
Listener Another module, Press, on the queue: the headline is printed if and only if the nickname was committedOtro módulo, Press, en la cola: el titular se imprime si y solo si el apodo se confirmóUn altro modulo, Press, in coda: il titolo viene stampato se e solo se il soprannome è stato confermato
#[EventHandler]
#[Asynchronous('press')]
public function __invoke(NicknameGiven $event, CommandBus $commands, #[Header('publication_id')] string $publicationId): void
{
    $commands->dispatch(new PrintHeadline($event->nicknameId, $event->memberId, $publicationId));
}
Send One line, by object or by keyUna línea, por objeto o por claveUna riga, per oggetto o per chiave
$id = GiveNickname::send(memberId: 2, nickname: 'The Quiet One');

$id = Laravents::command('band.give_nickname', ['memberId' => 2, 'nickname' => 'The Quiet One']);
Where it fitsDónde encajaDove si adatta

Use casesCasos de usoCasi d'uso

Drag or scroll sideways.Arrastra o desplázate lateralmente.Trascina o scorri lateralmente.

From install to a first messageDe la instalación al primer mensajeDall'installazione al primo messaggio

How it worksCómo funcionaCome funziona

Four steps, all of them Laravel's own commands or shaped like them.Cuatro pasos, todos comandos propios de Laravel o con su forma.Quattro passaggi, tutti comandi di Laravel o con la loro forma.

1

InstallInstalarInstalla

composer require laravents/laravents: package discovery registers the provider and the facade.composer require laravents/laravents: el package discovery registra el provider y la facade.composer require laravents/laravents: il package discovery registra il provider e la facade.

2

MigrateMigrarMigra

php artisan migrate creates the outbox table; vendor:publish --tag=laravents-config publishes the config.php artisan migrate crea la tabla del outbox; vendor:publish --tag=laravents-config publica la configuración.php artisan migrate crea la tabella dell'outbox; vendor:publish --tag=laravents-config pubblica la configurazione.

3

Create a moduleCrear un móduloCrea un modulo

php artisan laravents:module Band writes the module and registers its provider, as make:provider does.php artisan laravents:module Band escribe el módulo y registra su provider, como lo hace make:provider.php artisan laravents:module Band scrive il modulo e registra il suo provider, come fa make:provider.

4

SendEnviarInvia

php artisan laravents:command GiveNickname --module=Band, then GiveNickname::send().php artisan laravents:command GiveNickname --module=Band, y luego GiveNickname::send().php artisan laravents:command GiveNickname --module=Band, poi GiveNickname::send().

Not sure it is wired? php artisan about has a Laravents section: the registry, the modules, the handlers by kind, the HTTP gateway and the outbox connections. php artisan laravents:list prints every handler.¿No estás seguro de que esté conectado? php artisan about tiene una sección de Laravents: el registro, los módulos, los handlers por tipo, el gateway HTTP y las conexiones del outbox. php artisan laravents:list imprime cada handler.Non sei sicuro che sia collegato? php artisan about ha una sezione Laravents: il registro, i moduli, gli handler per tipo, il gateway HTTP e le connessioni dell'outbox. php artisan laravents:list stampa ogni handler.
Laravel 13

Quick startInicio rápidoAvvio rapido

PHP 8.3 or newer and Laravel 13. Require the package, publish the config and run the outbox migration:PHP 8.3 o más reciente y Laravel 13. Requiere el paquete, publica la configuración y ejecuta la migración del outbox:PHP 8.3 o più recente e Laravel 13. Richiedi il pacchetto, pubblica la configurazione ed esegui la migrazione dell'outbox:

composer require laravents/laravents
php artisan vendor:publish --tag=laravents-config
php artisan migrate

Modules live in modules/ under the Modules\ namespace. Autoload that directory in your application's composer.json:Los módulos viven en modules/ bajo el namespace Modules\. Carga ese directorio con el autoload del composer.json de tu aplicación:I moduli vivono in modules/ sotto il namespace Modules\. Carica quella cartella con l'autoload del composer.json della tua applicazione:

"autoload": {
    "psr-4": {
        "App\\": "app/",
        "Modules\\": "modules/"
    }
}

Create a module and its first command. Then read the manual, from installation to the reference:Crea un módulo y su primer comando. Después lee el manual, de la instalación a la referencia:Crea un modulo e il suo primo comando. Poi leggi il manuale, dall'installazione al riferimento:

php artisan laravents:module Band
php artisan laravents:command GiveNickname --module=Band
Artisan

ConsoleConsolaConsole

Every command, as route:list and model:show would have it. Their options and output are on the console page:Cada comando, como lo harían route:list y model:show. Sus opciones y su salida están en la página de la consola:Ogni comando, come farebbero route:list e model:show. Le loro opzioni e il loro output sono nella pagina della console:

CommandComandoComandoWhat it doesQué haceCosa fa
laravents:listEvery handler: kind, routing key, message, handler, mode, queue, HTTP route.Cada handler: tipo, routing key, mensaje, handler, modo, cola, ruta HTTP.Ogni handler: tipo, routing key, messaggio, handler, modalità, coda, rotta HTTP.
laravents:showOne module's canvas: commands, queries, events published and consumed, model publications, HTTP endpoints, dependencies.El mapa de un módulo: comandos, consultas, eventos publicados y consumidos, publicaciones de modelos, endpoints HTTP, dependencias.La mappa di un modulo: comandi, query, eventi pubblicati e consumati, pubblicazioni di modelli, endpoint HTTP, dipendenze.
laravents:verifyModule direction, Api purity, cycles and the registry; exit 1 on a violation.La dirección entre módulos, la pureza de la Api, los ciclos y el registro; sale con 1 ante una violación.La direzione tra moduli, la purezza della Api, i cicli e il registro; esce con 1 in caso di violazione.
laravents:docsA Markdown page per module and a Mermaid dependency graph; --check exits 1 when they are stale.Una página Markdown por módulo y un grafo de dependencias en Mermaid; --check sale con 1 cuando están desactualizadas.Una pagina Markdown per modulo e un grafo delle dipendenze in Mermaid; --check esce con 1 quando non sono aggiornate.
laravents:cacheCache the handler registry, as part of php artisan optimize.Guarda en caché el registro de handlers, como parte de php artisan optimize.Mette in cache il registro degli handler, come parte di php artisan optimize.
laravents:republishRe-dispatch outbox rows that were never picked up.Vuelve a despachar las filas del outbox que nunca se tomaron.Rispedisce le righe dell'outbox mai prese in carico.
laravents:moduleA module, its provider and its first test; laravents:command, :query, :event and :interceptor write the rest.Un módulo, su provider y su primera prueba; laravents:command, :query, :event y :interceptor escriben el resto.Un modulo, il suo provider e il suo primo test; laravents:command, :query, :event e :interceptor scrivono il resto.
The ideaLa ideaL'idea

Why LaraventsPor qué LaraventsPerché Laravents

Laravel's shape firstPrimero, la forma de LaravelPrima di tutto, la forma di Laravel

A Laravel developer should be able to guess the API before reading it: the facade, the fake, the generators and about work as Laravel's own do.Quien desarrolla con Laravel debería poder adivinar la API antes de leerla: la facade, el fake, los generadores y about funcionan como los de Laravel.Chi sviluppa con Laravel dovrebbe poter indovinare l'API prima di leggerla: la facade, il fake, i generatori e about funzionano come quelli di Laravel.

The actor is never a headerEl actor nunca es un headerL'attore non è mai un header

#[Authorize] trusts only a user the guard holds, and an asynchronous handler runs as its publication's actor, rebuilt through the guard's UserProvider.#[Authorize] solo confía en un usuario que el guard tiene, y un handler asíncrono se ejecuta como el actor de su publicación, reconstruido con el UserProvider del guard.#[Authorize] si fida solo di un utente che il guard ha, e un handler asincrono viene eseguito come l'attore della sua pubblicazione, ricostruito tramite lo UserProvider del guard.

Committed or not at allConfirmado o nadaConfermato o niente

One outbox row per event and asynchronous listener, in the publisher's transaction: a rolled-back command never reaches a listener.Una fila del outbox por evento y listener asíncrono, en la transacción del publicador: un comando revertido nunca llega a un listener.Una riga dell'outbox per evento e listener asincrono, nella transazione di chi pubblica: un comando annullato non raggiunge mai un listener.

Boundaries are checkedLos límites se verificanI confini vengono verificati

laravents:verify reads the source, not a manifest: a module that reaches into another's internals fails CI.laravents:verify lee el código, no un manifiesto: un módulo que entra en los internos de otro hace fallar la CI.laravents:verify legge il codice, non un manifest: un modulo che entra negli interni di un altro fa fallire la CI.

Standards at the boundaryEstándares en el bordeStandard al confine

HTTP statuses and error bodies come from your exception handler. A message's fields are the module's own, and no standard adds an envelope to them.Los estados HTTP y los cuerpos de error vienen de tu exception handler. Los campos de un mensaje son del módulo, y ningún estándar les agrega un sobre.Gli stati HTTP e i corpi d'errore vengono dal tuo exception handler. I campi di un messaggio sono del modulo, e nessuno standard aggiunge loro una busta.

Tests prove every claimLas pruebas respaldan cada afirmaciónI test dimostrano ogni affermazione

A rule in the manual has a test that holds it, named in a "Proven by" line, and every code sample is an excerpt of a file the suite runs.Cada regla del manual tiene una prueba que la sostiene, nombrada en una línea "Proven by", y cada ejemplo de código es un extracto de un archivo que ejecuta la suite.Ogni regola del manuale ha un test che la sostiene, indicato in una riga "Proven by", e ogni esempio di codice è un estratto di un file che la suite esegue.

Before you askAntes de preguntarPrima di chiedere

FAQPreguntas frecuentesDomande frequenti

The short answers — the manual has the long ones.Las respuestas breves — el manual tiene las largas.Le risposte brevi — il manuale ha quelle lunghe.

No. An #[EventHandler] of a framework, package or application event is subscribed with Event::listen(), so Laravel's events come in. The other way there is no bridge: a Laravents event is not dispatched to Laravel's dispatcher.No. Un #[EventHandler] de un evento del framework, de un paquete o de la aplicación se suscribe con Event::listen(), así que los eventos de Laravel entran. En sentido contrario no hay puente: un evento de Laravents no se despacha al dispatcher de Laravel.No. Un #[EventHandler] di un evento del framework, di un pacchetto o dell'applicazione viene registrato con Event::listen(), quindi gli eventi di Laravel entrano. Nel senso opposto non c'è un ponte: un evento di Laravents non viene inviato al dispatcher di Laravel.

No. Commands and queries are always synchronous, because a caller waits for their answer. Event handlers can be asynchronous, with #[Asynchronous], or run after the response, with #[Deferred].No. Los comandos y las consultas son siempre síncronos, porque quien llama espera su respuesta. Los handlers de eventos pueden ser asíncronos, con #[Asynchronous], o ejecutarse después de la respuesta, con #[Deferred].No. I comandi e le query sono sempre sincroni, perché chi chiama aspetta la loro risposta. Gli handler di eventi possono essere asincroni, con #[Asynchronous], o essere eseguiti dopo la risposta, con #[Deferred].

No. The Band, Press and FanClub modules live in workbench/, for the tests and the documentation. composer require laravents/laravents ships src/, config/, database/, routes/ and stubs/ only.No. Los módulos Band, Press y FanClub viven en workbench/, para las pruebas y la documentación. composer require laravents/laravents instala solo src/, config/, database/, routes/ y stubs/.No. I moduli Band, Press e FanClub vivono in workbench/, per i test e la documentazione. composer require laravents/laravents installa solo src/, config/, database/, routes/ e stubs/.

The row stores the event as Laravel's queue stores a job, serialized inside JSON, so it fits a text column on every driver. The test suite runs on SQLite, and the payload format is built so PostgreSQL accepts it too.La fila guarda el evento como la cola de Laravel guarda un job, serializado dentro de JSON, así que entra en una columna de texto con cualquier driver. La suite de pruebas corre sobre SQLite, y el formato del payload está pensado para que PostgreSQL también lo acepte.La riga salva l'evento come la coda di Laravel salva un job, serializzato dentro JSON, quindi sta in una colonna di testo con qualsiasi driver. La suite di test gira su SQLite, e il formato del payload è pensato perché anche PostgreSQL lo accetti.

Only the handlers marked #[Expose], at POST /laravents/commands/{key} and GET|POST /laravents/queries/{key}. Anything else is one identical 404, whether the key exists or not, and #[Authorize] still applies.Solo los handlers marcados con #[Expose], en POST /laravents/commands/{key} y GET|POST /laravents/queries/{key}. Todo lo demás es un único 404 idéntico, exista la clave o no, y #[Authorize] sigue aplicándose.Solo gli handler marcati con #[Expose], su POST /laravents/commands/{key} e GET|POST /laravents/queries/{key}. Tutto il resto è un unico 404 identico, che la chiave esista o no, e #[Authorize] continua ad applicarsi.

Laravents is MIT-licensed.Laravents tiene licencia MIT.Laravents ha licenza MIT.