Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f3617ac
test(lumen): prove the sample on the portable database outbox, delive…
Oct 13, 2026
36a968d
test(lumen): run the sample's portable outbox on every integration lane
Oct 13, 2026
ec797b5
feat(lumen): cache wallet balances with #[CachedQuery] and evict them…
Oct 13, 2026
5ffeb1b
feat(lumen): deposit card top-ups from a signed firefly/webhooks endp…
Oct 13, 2026
e284fe3
test(lumen): deliver signed partner callbacks for the funds events, v…
Oct 13, 2026
6581f51
feat(lumen): pay wallets out to a bank through a compensating saga wi…
Oct 13, 2026
5a5484b
feat(lumen): expose the payout saga at POST /api/v1/wallets/{id}/payo…
Oct 13, 2026
b9fa8af
fix(lumen): answer a payout whose refund failed with 500 PAYOUT_COMPE…
Oct 13, 2026
9d108bd
test(docs): boot the tutorial's greeting-card saga as the reader's ow…
Oct 13, 2026
5f00186
docs(book): Chapter 7 caches Lumen's balance with #[CachedQuery] and …
Oct 13, 2026
8c5133e
docs(book): Chapter 8 teaches the outbox on every database — claims, …
Oct 13, 2026
064af37
docs(book): add Chapter 8A, webhooks in and callbacks out on Lumen, i…
Oct 13, 2026
3f2d8db
docs(book): add Chapter 9A, Lumen's compensating payout saga with TCC…
Oct 13, 2026
8bbc129
docs(book): Chapter 12 runs the same tests on every real server — Int…
Oct 13, 2026
06e6385
docs(book): the preface, Chapter 1, Appendix A and the glossary name …
Oct 13, 2026
539f598
docs(tutorial): Step 12 moves the greeting events onto the database o…
Oct 13, 2026
ad2a917
docs(tutorial): Step 13 sells a greeting card through a compensating …
Oct 13, 2026
67cf3a9
docs: the front pages name orchestration, webhooks, callbacks and the…
Oct 13, 2026
4d959e2
docs(changelog): record the D1 books, tutorials and Lumen sample work…
Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,25 @@ All notable changes to LaraFly are documented here. This project uses CalVer (`Y
pessimistic/optimistic repository locking are integration-tested on every server; `DatabaseCapabilities` (in
`firefly/data`) reads SKIP LOCKED and READPAST support from the server version; the [database support
matrix](docs/database-support.md) documents the result.
- **Book: Chapters 8A and 9A, in English and Spanish.** *Webhooks and Callbacks* (Lumen tops wallets up from a
signed card-processor webhook and sends signed callbacks to a partner) and *Sagas, TCC and Durable Workflows*
(Lumen's payout saga against a bank port, compensating newest first, with database checkpoints, recovery, dead
letters, TCC, durable workflows and the scheduler's distributed lock), slotted after Chapters 8 and 9 without
renumbering. Chapter 7 gains the query cache, Chapter 8 the portable database outbox (claim strategies per
server, any number of consumers, the relay, `eda-postgres` as the LISTEN/NOTIFY optimization, durable dead
letters and replay, Kafka/RabbitMQ messaging), and Chapter 12 the integration lanes (`IntegrationDsn`,
`composer test:integration`, the CI matrix, SQL Server's lessons); the preface, Chapter 1, Appendix A and the
glossary follow. The book now has eighteen chapters.
- **Tutorial Steps 12 and 13, in English and Spanish.** The greeting app moves its events onto the database outbox
and delivers them with `php artisan firefly:eda:consume`, then sells a greeting card through a compensating saga
at `POST /cards/{to}`; the saga's files are quoted from `tests/Fixtures/Tutorial/app`, which
`tests/TutorialGreetingCardSagaTest.php` boots through the real web and orchestration pipeline.
- **`samples/lumen` exercises W1, W2 and W4.** A `payout` saga over a `BankGateway` port with a simulated bank and
`POST /api/v1/wallets/{id}/payouts` (201 when the bank confirms, 409 `PAYOUT_FAILED` when the saga compensated,
500 `PAYOUT_COMPENSATION_FAILED` when a refund itself failed); `CardTopUpListener` on a signed `firefly/webhooks`
endpoint; partner callbacks through `firefly/callbacks`; `GetBalance` as a `#[CachedQuery]` evicted per wallet by
`#[InvalidatesQuery]` on `FundsDeposited`/`FundsWithdrawn`; and the portable outbox, proven on PostgreSQL, MySQL,
MariaDB, SQL Server and SQLite by `OutboxDatabaseRoundTripTest`.

### Changed

Expand Down Expand Up @@ -106,6 +125,10 @@ All notable changes to LaraFly are documented here. This project uses CalVer (`Y
- **The CQRS/EDA bridge figure still credited only the Postgres adapter with same-transaction atomicity.** Its
caption now names the database outbox (`provider=database` or `postgres`), and its adapter row draws the
`database` provider; a test derives both providers from their outbox auto-configurations.
- **The Lumen test harness dropped the query-cache policies.** `LumenTestCase` built `HandlerManifest` from the
scanner's handlers and destinations only, so a `#[CachedQuery]` in the sample would have run uncached and every
`#[InvalidatesQuery]` would have evicted nothing once the cache was on; it now passes the cached queries and the
invalidation rules too.

### Deprecated

Expand Down
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

<p align="center">
<em>Dependency injection with stereotypes, conditional auto-configuration, hexagonal ports &amp; adapters,
CQRS, event-driven architecture with a genuine same-transaction outbox, Spring-Security-shaped method
CQRS, event-driven architecture with a genuine same-transaction outbox on any database, sagas and webhooks, Spring-Security-shaped method
security on any bean, both halves of OAuth2, and OpenTelemetry-shaped tracing — wired the moment you
install a package, with your own beans always winning.</em>
</p>
Expand Down Expand Up @@ -71,9 +71,9 @@ event-driven, actuator-observed microservice, chapter by chapter — every listi
**[Download the book in English or Spanish, as PDF or EPUB](https://fireflyframework.github.io/fireflyframework-php/book/).**
The published editions include the single-package installation and bundled installer.

The book is **structurally complete and bilingual (English + Spanish)**: a quick start, **sixteen chapters**
The book is **structurally complete and bilingual (English + Spanish)**: a quick start, **eighteen chapters**
across four parts — Foundations (DI, config, HTTP), Modelling & Persisting the Domain (repositories, DDD),
Coordinating & Securing the App (CQRS, EDA + transactional outbox, `#[Transactional]`, security, OAuth2 and
Coordinating & Securing the App (CQRS, EDA + transactional outbox, webhooks and callbacks, `#[Transactional]`, sagas/TCC/durable workflows, security, OAuth2 and
OpenID Connect), and Observability, Testing & Delivery (actuator, testing, the CLI + zero-reflection cache,
feature flags)
— plus a Laravel→LaraFly cheat-sheet and a glossary. The two editions are line-for-line the same book: every
Expand Down Expand Up @@ -158,7 +158,7 @@ LaraFly is not a fork of Laravel and does not hide it — every package layers c
`Illuminate\Container`, Eloquent, the HTTP kernel, and the queue/cache/scheduler, so everything you already
know about Laravel keeps working underneath.

Four capabilities in that list are recent enough — and far enough from what teams assume a PHP framework
Five capabilities in that list are recent enough — and far enough from what teams assume a PHP framework
will have — that they are worth naming outright:

- **Both halves of OAuth2, as two packages you install.**
Expand Down Expand Up @@ -193,6 +193,14 @@ will have — that they are worth naming outright:
[flagd](https://flagd.dev) definitions in-process on OpenFeature with the same semantics and percentage buckets
as PyFly. Gate a bean method or route with `#[FeatureFlag]` and a view with `@featureflag`; change flags at runtime
from the admin dashboard, `/actuator/flags` or `php artisan firefly:flags`, with an audit trail.
- **Work that spans systems.** [`firefly/orchestration`](docs/modules/orchestration.md) runs sagas whose
completed steps are compensated newest first when a later one fails, TCC transactions that reserve with every
participant before confirming any, and durable workflows that wait for signals and timers across processes —
every step checkpointed (memory, cache or a portable database store), stale executions recovered, failures
dead-lettered. [`firefly/webhooks`](docs/modules/webhooks.md) verifies Stripe, GitHub, Twilio or HMAC
signatures, refuses replays and duplicates and publishes the rest to the event bus;
[`firefly/callbacks`](docs/modules/callbacks.md) sends the bus's events to subscribers as signed, retried POSTs
behind a circuit breaker per endpoint.

### Who is LaraFly for?

Expand Down Expand Up @@ -1133,7 +1141,7 @@ Start at the **[documentation table of contents](docs/README.md)** — it groups
- [Laravel ↔ Spring Boot Comparison](docs/laravel-comparison.md) — concept-by-concept mapping for both audiences.
- [Versioning](docs/versioning.md) · [Contributing](docs/contributing.md) · [Publishing](docs/publishing.md).
- Every [module guide](#modules) above.
- [*larafly by example*](book/README.md) — the complete bilingual book (16 chapters + appendices, PDF + EPUB).
- [*larafly by example*](book/README.md) — the complete bilingual book (18 chapters + appendices, PDF + EPUB).
- [`samples/lumen/`](samples/lumen/) — the wallet-and-ledger sample this README's showcases are drawn from;
run its own test suite with `vendor/bin/pest samples/lumen/tests`.

Expand Down Expand Up @@ -1169,8 +1177,8 @@ accurately:
- **Read models / projections as a first-class concept.** The sample's `LedgerProjector` shows the pattern
today via a plain `#[EventListener]`; a dedicated `firefly/eventsourcing`-style package for event
sourcing/snapshots/projections is future work, as it is in PyFly.
- **Documentation.** The end-to-end [tutorial](docs/tutorial.md) (EN + ES), the *larafly by example*
[book](book/README.md) (16 chapters + appendices, EN + ES, PDF + EPUB), and a
- **Documentation.** The end-to-end [tutorial](docs/tutorial.md) (EN + ES, through a database outbox and a saga), the *larafly by example*
[book](book/README.md) (18 chapters + appendices, EN + ES, PDF + EPUB), and a
[docs table of contents](docs/README.md) all shipped with the documentation-parity milestone.
Deeper guides (more recipes, more diagrams) continue to grow from here.

Expand Down
13 changes: 8 additions & 5 deletions book/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ book/
src/ # EN manuscript (Markdown)
00-front/ # title/copyright/dedication/preface/conventions
00-quickstart.md # "Build Lumen step by step" quick start
01..13a-*.md # the sixteen chapters, 4A, 10A and 13A included (Parts I-IV)
01..13a-*.md # the eighteen chapters, 4A, 8A, 9A, 10A and 13A included (Parts I-IV)
90-appendix-a-laravel.md # Laravel -> LaraFly cheat-sheet
94-glossary.md # glossary
src-es/ # ES manuscript, same structure/filenames
Expand All @@ -139,16 +139,16 @@ book/
## Manuscript status

The manuscript is **structurally complete** in both languages: a five-file front
matter, a "Build Lumen step by step" quick start, sixteen chapters across four
matter, a "Build Lumen step by step" quick start, eighteen chapters across four
parts —

- **Part I — Foundations**: Why LaraFly, Dependency Injection & Auto-Configuration,
Configuration/Profiles/Secrets, Your First HTTP API
- **Part II — Modelling & Persisting the Domain**: Persistence & Repositories,
Domain-Driven Design
- **Part III — Coordinating & Securing the Application**: CQRS, Event-Driven
Architecture & the Transactional Outbox, Transactions & the `#[Transactional]`
proxy, Security, OAuth2 and OpenID Connect
Architecture & the Transactional Outbox, Webhooks and Callbacks, Transactions & the `#[Transactional]`
proxy, Sagas, TCC and Durable Workflows, Security, OAuth2 and OpenID Connect
- **Part IV — Observability, Testing & Delivery**: Observability/Actuator,
Testing, the CLI & the Zero-Reflection Cache, Feature Flags

Expand All @@ -158,7 +158,10 @@ in `src/` **and** in `src-es/` carries either a `source:` marker naming the
repository file it was excerpted from — compared line for line by the
repository's own documentation guard — or an `illustrative:` marker saying it is
the reader's own code, which is the kind `php -l` checks. Both editions build to
`book/dist/` as PDF + EPUB.
`book/dist/` as PDF + EPUB. Chapters 8A and 9A walk the code W1, W2 and W4
added to Lumen — `CardTopUpListener`, the partner callbacks, `PayoutSaga` and
`SimulatedBankGateway` — and every listing of that code is a `source:` excerpt
of the file the sample's own tests run.

**The two editions are the same book, line for line.** Every chapter file has
the same sections in the same order at the same line numbers, and every `php`
Expand Down
8 changes: 8 additions & 0 deletions book/book.es.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ labels:
# manuscritos y de book/README.md. Un capítulo y no dos porque los dos paquetes son las dos direcciones de un
# mismo protocolo, y quien instale cualquiera de ellos necesita el viaje completo delante para saber cuál tiene
# entre manos.
# Tarea D1: firefly/webhooks y firefly/callbacks añadidos como Capítulo 8A, justo después del capítulo de eventos en
# cuyo bus terminan los dos, y firefly/orchestration como Capítulo 9A, justo después del capítulo de transacciones cuyo
# límite (una transacción) existe para superar. Intercalados igual que 4A y 10A: `num` es una etiqueta libre y el orden
# sale de ESTA lista, así que los Capítulos 9-13A conservan su número y siguen siendo ciertas todas las referencias
# "Capítulo N" de ambos manuscritos y de book/README.md. Webhooks y callbacks comparten capítulo porque son las dos
# direcciones de un mismo contrato: la firma de un callback de LaraFly es exactamente la que comprueba el validador `hmac`.
front:
- {id: title, file: 00-front/00-title.md, nav: false}
- {id: copyright, file: 00-front/00-copyright.md, nav: false}
Expand All @@ -74,7 +80,9 @@ parts:
chapters:
- {id: ch07, file: 07-cqrs.md, num: 7, title: "CQRS: Comandos y Consultas"}
- {id: ch08, file: 08-eda-outbox.md, num: 8, title: "Arquitectura Guiada por Eventos y el Outbox Transaccional"}
- {id: ch08a, file: 08a-webhooks-callbacks.md, num: "8A", title: "Webhooks y Callbacks: Recibir y Enviar Eventos Firmados"}
- {id: ch09, file: 09-transactions.md, num: 9, title: "Transacciones y el Proxy #[Transactional]"}
- {id: ch09a, file: 09a-orchestration.md, num: "9A", title: "Sagas, TCC y Workflows Duraderos"}
- {id: ch10, file: 10-security.md, num: 10, title: "Seguridad: Autenticación y Autorización"}
- {id: ch10a, file: 10a-oauth2.md, num: "10A", title: "OAuth2 y OpenID Connect: Iniciar Sesión, y Ser el Proveedor"}
- title: "Parte IV — Observabilidad, Pruebas y Entrega"
Expand Down
8 changes: 8 additions & 0 deletions book/book.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ labels:
# renumbering Chapters 11-13 would have invalidated every "Chapter N" cross-reference in both manuscripts and in
# book/README.md. One chapter rather than two because the two packages are the two directions of one protocol,
# and a reader who installs either needs the round trip in front of them to know which one they are holding.
# Task D1: firefly/webhooks and firefly/callbacks added as Chapter 8A, immediately after the event-driven chapter whose
# bus both of them end on, and firefly/orchestration as Chapter 9A, immediately after the transactions chapter whose
# limit (one transaction) it exists to go past. Slotted exactly the way 4A and 10A were: `num` is a free-form label and
# the order comes from THIS list, so Chapters 9-13A keep their numbers and every "Chapter N" cross-reference in both
# manuscripts and in book/README.md stays true. Webhooks and callbacks share one chapter because they are the two
# directions of one contract — LaraFly's callback signature is exactly what the webhooks `hmac` validator checks.
front:
- {id: title, file: 00-front/00-title.md, nav: false}
- {id: copyright, file: 00-front/00-copyright.md, nav: false}
Expand All @@ -72,7 +78,9 @@ parts:
chapters:
- {id: ch07, file: 07-cqrs.md, num: 7, title: "CQRS: Commands and Queries"}
- {id: ch08, file: 08-eda-outbox.md, num: 8, title: "Event-Driven Architecture and the Transactional Outbox"}
- {id: ch08a, file: 08a-webhooks-callbacks.md, num: "8A", title: "Webhooks and Callbacks: Receiving and Sending Signed Events"}
- {id: ch09, file: 09-transactions.md, num: 9, title: "Transactions and the #[Transactional] Proxy"}
- {id: ch09a, file: 09a-orchestration.md, num: "9A", title: "Sagas, TCC and Durable Workflows"}
- {id: ch10, file: 10-security.md, num: 10, title: "Security: Authentication and Authorization"}
- {id: ch10a, file: 10a-oauth2.md, num: "10A", title: "OAuth2 and OpenID Connect: Signing In, and Being the Provider"}
- title: "Part IV — Observability, Testing & Delivery"
Expand Down
2 changes: 2 additions & 0 deletions book/src-es/00-front/00-preface.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Cada capítulo hace avanzar **Lumen**, un servicio de monedero digital y libro m

El recorrido empieza con suavidad. El **Inicio rápido** te lleva desde el instalador incluido `firefly new` hasta un endpoint en ejecución y consultable con curl, previendo en miniatura los estereotipos, el contenedor y la ruta de arranque compilada antes de que ningún capítulo te pida razonar sobre ellos. El **Capítulo 1** da un paso atrás y argumenta el enfoque completo — qué problema resuelve LaraFly y sobre qué pilares se sostiene. El **Capítulo 2** abre la sala de máquinas: el contenedor de inyección de dependencias, los atributos de estereotipo y el escaneo de componentes que compila tus clases anotadas en un manifiesto de arranque en caché, sin reflexión. Las partes posteriores de este libro — que llegarán en los capítulos siguientes — construyen hacia afuera desde esa base hacia la configuración, HTTP, la persistencia, el modelado de dominio, CQRS, la arquitectura orientada a eventos, la seguridad y la observabilidad, siempre a través de la misma base de código de `Lumen`, siempre con código que puedes ejecutar.

Los Capítulos 8A y 9A llevan a Lumen más allá de una sola base de datos: un procesador de tarjetas recarga wallets mediante un webhook firmado, un socio se entera de cada movimiento de dinero mediante callbacks firmados, y un pago saliente a un banco se ejecuta como una saga que se deshace sola, paso a paso, cuando el banco dice que no.

El Capítulo 13A cierra el recorrido de producción con feature flags: Lumen mantiene apagada una vista de la wallet, selecciona una cohorte compartida y prueba la ruta y el ciclo del override contra el framework real.

Cuando hayas terminado de trabajar todo el libro, tendrás un modelo mental de cada capa de un servicio LaraFly de producción, y una aplicación real y probada que lo demuestre.
Expand Down
2 changes: 1 addition & 1 deletion book/src-es/01-why-larafly.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Las escrituras y las lecturas viajan por dos caminos separados. Un `#[CommandHan

### 4. Arquitectura orientada a eventos, con un outbox genuino

Un agregado emite eventos de dominio que describen lo que ocurrió; tras una confirmación, esos eventos pueden proyectarse en el mismo proceso o conectarse hacia afuera al `EventPublisher`, respaldado por bróker, de `firefly/eda` — en memoria para las pruebas, una cola de Laravel para entrega asíncrona, o un bróker de mensajes real (Kafka, RabbitMQ, o `LISTEN`/`NOTIFY` de Postgres) para entrega entre servicios. El adaptador de Postgres en particular escribe la fila del outbox en la *misma* transacción de base de datos que el propio agregado, de modo que un evento nunca puede registrarse sin que la escritura que describe se haya confirmado realmente.
Un agregado emite eventos de dominio que describen lo que ocurrió; tras una confirmación, esos eventos pueden proyectarse en el mismo proceso o conectarse hacia afuera al `EventPublisher`, respaldado por bróker, de `firefly/eda` — en memoria para las pruebas, una cola de Laravel para entrega asíncrona, o un broker de mensajería real (Kafka o RabbitMQ) para la entrega entre servicios. El outbox de base de datos escribe la fila del evento en la *misma* transacción de base de datos que el propio agregado — en PostgreSQL, MySQL, MariaDB, SQL Server o SQLite, con `LISTEN`/`NOTIFY` como extra de PostgreSQL —, así que un evento nunca puede quedar registrado sin que la escritura que describe se haya confirmado de verdad. Cuando una operación abarca más de un sistema, las sagas, el TCC y los workflows duraderos toman el relevo donde termina una sola transacción.

### 5. Seguridad

Expand Down
Loading
Loading