{"id":51489614,"url":"https://github.com/pragmaticalabs/ticketing","last_synced_at":"2026-07-07T10:30:21.899Z","repository":{"id":368344999,"uuid":"1284703186","full_name":"pragmaticalabs/ticketing","owner":"pragmaticalabs","description":"Event-ticketing platform — a PFD → JBCT → Aether reference implementation (runnable posterchild).","archived":false,"fork":false,"pushed_at":"2026-06-30T06:26:25.000Z","size":120,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-30T07:21:36.623Z","etag":null,"topics":["aether","example","functional-programming","java","jbct","pfd","process-first-design","reference-implementation"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pragmaticalabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-30T05:52:40.000Z","updated_at":"2026-06-30T06:26:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pragmaticalabs/ticketing","commit_stats":null,"previous_names":["pragmaticalabs/ticketing"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/pragmaticalabs/ticketing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticalabs%2Fticketing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticalabs%2Fticketing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticalabs%2Fticketing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticalabs%2Fticketing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pragmaticalabs","download_url":"https://codeload.github.com/pragmaticalabs/ticketing/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pragmaticalabs%2Fticketing/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35225022,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-07T02:00:07.222Z","response_time":90,"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":["aether","example","functional-programming","java","jbct","pfd","process-first-design","reference-implementation"],"created_at":"2026-07-07T10:30:19.732Z","updated_at":"2026-07-07T10:30:21.874Z","avatar_url":"https://github.com/pragmaticalabs.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ticketing Platform — a PFD → JBCT → Aether reference\n\nA complete, runnable realization of the **event-ticketing example** threaded through the\n[*Process-First Design*](https://leanpub.com/process-first-design) book, implemented with **[JBCT](https://pragmatica.dev/)** (functional Java: `Result`/`Option`/`Promise`,\nparse-don't-validate, sealed typed failures; see the [JBCT book](https://leanpub.com/jbct-book)) on the **[Aether](https://github.com/pragmaticalabs/pragmatica/tree/main/aether)** unified runtime. This is the\nposterchild: the book designs the processes; this repo runs them.\n\n\u003e **⚠️ Status — depends on an unreleased toolchain.** This project builds and passes its full test\n\u003e suite on **Pragmatica Lite / Aether / JBCT `1.0.0-rc2`** (Java 25) — but **rc2 is not yet released**:\n\u003e its artifacts are not on Maven Central. It compiles locally against an `~/.m2` install of rc2; a\n\u003e fresh clone **cannot resolve the dependencies yet**, so for now read it as a reference. It becomes\n\u003e buildable as-is once rc2 is published.\n\u003e\n\u003e Design rationale and the full process catalog live in [`docs/DESIGN.md`](docs/DESIGN.md).\n\n---\n\n## The telescope, as packages\n\nPFD's four altitudes — **system → subsystem → workflow → use case** — are the package hierarchy.\nEach **use case is one slice** (one `Request`/`Response`, one `execute(Request)` method), the leaf of\nthe tree. 23 single-use-case slices:\n\n| Subsystem (persistence) | Workflow | Use-case slices |\n|---|---|---|\n| **booking** — `BookingStore` | `purchase/` | `BuyTicket` |\n| | `cancellation/` | `CancelTicket` |\n| | `hold/` | `AcquireHold`, `CheckHold`, `SweepHolds` |\n| **pricing** — `PricingStore` | `schedule/` | `SetPrice`, `AdjustPrice` |\n| | `quoting/` | `QuotePrice` |\n| **eventmanagement** — `EventStore` | `lifecycle/` | `CreateEvent`, `OpenEvent`, `CancelEvent` |\n| | `capacity/` | `AddSeat`, `BlockSeat`, `ReleaseSeat` |\n| | `sales/` | `SaleStatus` |\n| | `convergence/` | `MarkSeatSold`, `MarkSeatReleased` *(SeatSold/SeatReleased consumers)* |\n| **availability** — per-use-case `@PgSql` | `query/` | `SeatStatus`, `SoldCount` |\n| | `projection/` | `ProjectSeatSold`, `ProjectSeatReleased` *(consumers)* |\n| **quote** — per-use-case `@PgSql` | `query/` | `QuoteForCustomer` |\n| | `projection/` | `ProjectPrice` *(PriceChanged consumer)* |\n\nAbove the subsystems, `shared/` holds the cross-cutting value objects (`Money`, `Percent`,\n`SeatState`, the id types, `SeatLocation`) and `shared/event/` the pub-sub facts (`SeatSold`,\n`SeatReleased`, `PriceChanged`).\n\nEvery use-case slice is an interface with nested `Request`/`Response` records, a sealed `…Error\nextends Cause`, and one method `Promise\u003cResponse\u003e execute(Request)`. The write subsystems share one\n`@PgSql` store each (`BookingStore`, `PricingStore`, `EventStore`); the read subsystems\n(`availability`, `quote`) instead use **per-use-case `@PgSql` interfaces** co-located with each slice\n(interface segregation — no slice there needs more than one method). The composition is the book's\n**enterprise profile** vector: **synchronous reads across subsystems** (`BuyTicket` calls `QuotePrice`\nand `SaleStatus` directly) and **typed facts over pub-sub** for cross-subsystem state\n(`SeatSold`/`SeatReleased`/`PriceChanged`, with `convergence`/`projection` consumer slices).\n\n**All six PFD patterns** appear — Leaf (VO factories/adapters), Sequencer (`BuyTicket` saga),\nFork-Join (`SaleStatus` ∥ eligibility in `BuyTicket`), Condition (cancellable?, hold decay),\nIteration (`SweepHolds`), Aspects (declared compensation). **All three recovery classes** — BER\n(`BuyTicket`/`CancelTicket` money sagas), FER (`Fresh→Stale→Expired` holds, best-effort notify),\ndesign-out (the seat claim, the price log).\n\n### The design-out seat claim (the heart of it)\n\nTwo buyers cannot double-book: the reservation table has one row per seat, and the claim is a single\nguarded statement that also reclaims expired holds inline —\n\n```sql\nINSERT INTO reservations (...) VALUES (..., 'held', now() + interval '15 minutes', now())\nON CONFLICT (seat_id) DO UPDATE SET ... state = 'held', ...\nWHERE reservations.state IN ('cancelled','expired')\n   OR (reservations.state = 'held' AND reservations.expires_at \u003c now())\nRETURNING id\n```\n\nA fresh hold or a confirmed booking can't be overwritten (the loser gets zero rows → typed\n`SeatUnavailable`); a stale/expired hold is stolen atomically. No version column, no race — the\nconflict is impossible by the shape of the statement.\n\n---\n\n## Build \u0026 test\n\n\u003e **Prerequisite (see the Status note above):** needs the **whole `1.0.0-rc2` line** — `core`,\n\u003e `aether` (`slice-api`/`pg-codegen`/…), and the `slice-processor` (with the codegen fixes merged via\n\u003e [pragmaticalabs/pragmatica#364](https://github.com/pragmaticalabs/pragmatica/pull/364)) — in your\n\u003e local `~/.m2`. None of these are on Maven Central yet, so until rc2 is published you must build them\n\u003e from a `release-1.0.0-rc2` checkout (`mvn install`) before this project will resolve.\n\n```bash\nmvn clean install         # compiles (slice-processor + pg-codegen), runs 125 unit tests, generates target/blueprint.toml (23 slices)\njbct check src/main/java  # JBCT format + lint (0 errors, 0 warnings)\n```\n\nEach use-case slice has unit tests (validation, happy path, each typed failure) against in-memory\nfakes — no database required.\n\n## Run locally (Forge)\n\n```bash\n./start-postgres.sh                 # PostgreSQL 17 (db=forge); Aether applies src/main/resources/schema on deploy\npython3 scripts/stub-gateway.py \u0026   # stub payment gateway on :9100 (BuyTicket's @Http target)\n# optional: a MailHog/Mailpit SMTP sink on :1025 captures @Notify mail — notifications are FER, optional\n./run-forge.sh                      # build + 5-node cluster; deploys the blueprint BY COORDINATE; app on :8070\n```\n\n\u003e The rc1 forge resolves `--blueprint` as an **artifact coordinate** (not a file path); `run-forge.sh`\n\u003e passes `org.pragmatica.example:ticketing:1.0.0-SNAPSHOT:blueprint` accordingly.\n\n### A walk through the API (`:8070`)\n\nRoutes mirror the telescope. Representative flow:\n\n```bash\ncurl -s :8070/api/v1/events/create -d '{\"venue\":\"O2 Arena\",\"onSaleAt\":\"2026-07-01T10:00:00Z\"}'   # -\u003e event\ncurl -s :8070/api/v1/seats/add     -d '{\"event\":\"\u003ce\u003e\",\"section\":\"A\",\"row\":\"12\",\"number\":7,\"tier\":\"STANDARD\"}'  # -\u003e seat\ncurl -s :8070/api/v1/pricing/set   -d '{\"event\":\"\u003ce\u003e\",\"tier\":\"STANDARD\",\"amount\":\"49.50\",\"currency\":\"USD\"}'\ncurl -s -X POST :8070/api/v1/events/open/\u003ce\u003e\ncurl -s :8070/api/v1/booking/buy   -d '{\"customer\":\"\u003cuuid\u003e\",\"event\":\"\u003ce\u003e\",\"seat\":\"\u003cs\u003e\",\"tier\":\"STANDARD\"}'\ncurl -s :8070/api/v1/pricing/quote/\u003ce\u003e/STANDARD          # authoritative quote\ncurl -s :8070/api/v1/availability/seats/\u003cs\u003e              # 'sold' after SeatSold propagates\ncurl -s :8070/api/v1/booking/cancel -d '{\"booking\":\"\u003cb\u003e\",\"customer\":\"\u003cuuid\u003e\"}'\n```\n\nEach slice's exact route + error→status map: `src/main/resources/.../\u003cusecase\u003e/routes.toml`.\n\n---\n\n## What \"test our own tool\" found\n\nBuilding the book's idiomatic patterns — and then the one-use-case-per-slice + synchronous-call design\n— surfaced real toolchain issues (full list: [`docs/DESIGN.md`](docs/DESIGN.md) §8):\n\n- **Two slice-processor codegen bugs, both fixed in the runtime** — on the rc2 branch via PR\n  [pragmaticalabs/pragmatica#364](https://github.com/pragmaticalabs/pragmatica/pull/364), each with a\n  regression test (the project builds green on **1.0.0-rc2**):\n  1. duplicate single-type imports when two error types share a simple name (the book's per-VO\n     `Blank`/`Malformed` guarantee it);\n  2. generated codecs referenced an injected slice's nested `Request`/`Response` by *simple* name,\n     which the host slice's inherited member types shadow (JLS §6.5.5.2) — guaranteed by \"every slice\n     has `Request`/`Response`\" + \"a slice injects another slice\". Both now emit fully-qualified names;\n     both have regression tests.\n- **pg-codegen / structure workarounds, documented:** `schema/migrations.list` manifest; single-line\n  `@Query` (no text blocks, no data-modifying CTEs); one parameter per method; `@Notify` in a separate\n  `resource-notification` artifact; slice-factory transitive deps cap at 15 (the split keeps every\n  factory well under); no `@Heartbeat`; topics must be flat kebab-case; the forge `--blueprint`\n  coordinate form. These are the friction a real telescope-shaped app finds that a HelloWorld never will.\n\nA consolidated, prioritized DX wish list for the Aether toolchain — distilled from building this app —\nlives in [`docs/AETHER-WISHLIST.md`](docs/AETHER-WISHLIST.md).\n\n---\n\n## License\n\nLicensed under the [Apache License, Version 2.0](LICENSE). See [`NOTICE`](NOTICE) for attribution.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpragmaticalabs%2Fticketing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpragmaticalabs%2Fticketing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpragmaticalabs%2Fticketing/lists"}