@rashidee/co2 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +25449 -0
- package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
- package/drizzle/0001_wise_wolf_cub.sql +13 -0
- package/drizzle/0002_secret_spyke.sql +16 -0
- package/drizzle/0003_workspace_config_singleton.sql +16 -0
- package/drizzle/0004_terminal_claim_v120.sql +11 -0
- package/drizzle/0005_brainstorm_v124.sql +10 -0
- package/drizzle/0006_project_v140.sql +6 -0
- package/drizzle/0007_prd_wizard_v124.sql +14 -0
- package/drizzle/0008_workflow_artifact_v126.sql +24 -0
- package/drizzle/0009_project_import_v150.sql +7 -0
- package/drizzle/0010_quality_v151.sql +3 -0
- package/drizzle/meta/0000_snapshot.json +1335 -0
- package/drizzle/meta/0001_snapshot.json +1416 -0
- package/drizzle/meta/0002_snapshot.json +1510 -0
- package/drizzle/meta/_journal.json +83 -0
- package/package.json +41 -0
- package/plugin/.claude-plugin/marketplace.json +36 -0
- package/plugin/.claude-plugin/plugin.json +22 -0
- package/plugin/README.md +625 -0
- package/plugin/SKILLS.md +350 -0
- package/plugin/co2.svg +5 -0
- package/plugin/commands/brainstorm-loop.md +129 -0
- package/plugin/skills/conductor-defect/SKILL.md +898 -0
- package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
- package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
- package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
- package/plugin/skills/depgen-k8s/SKILL.md +468 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
- package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
- package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
- package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
- package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
- package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
- package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
- package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
- package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
- package/plugin/skills/modelgen-relational/SKILL.md +707 -0
- package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
- package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
- package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
- package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
- package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
- package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
- package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
- package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
- package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
- package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
- package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
- package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
- package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
- package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
- package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
- package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
- package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
- package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
- package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
- package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
- package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
- package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
- package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
- package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
- package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
- package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
- package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
- package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
- package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
- package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
- package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
- package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
- package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
- package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
- package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
- package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
- package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
- package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
- package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
- package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
- package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
- package/plugin/skills/testgen-functional/SKILL.md +1381 -0
- package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
- package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
- package/plugin/skills/util-projectinit/SKILL.md +307 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
- package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
- package/plugin/skills/util-projectsync/SKILL.md +501 -0
- package/plugin/skills/util-updprd/SKILL.md +324 -0
- package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
- package/plugin/skills/util-ustagger/SKILL.md +154 -0
- package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
- package/static/assets/arc-DRKw7sjs.js +1 -0
- package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
- package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
- package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
- package/static/assets/channel-C2nVbuwv.js +1 -0
- package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
- package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
- package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
- package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
- package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
- package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
- package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
- package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
- package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
- package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
- package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
- package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
- package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
- package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
- package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
- package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
- package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
- package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
- package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
- package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
- package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
- package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
- package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
- package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
- package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
- package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
- package/static/assets/graph-C9eacEi8.js +1 -0
- package/static/assets/index-LZSQkLE2.css +41 -0
- package/static/assets/index-y7XDSgMG.js +495 -0
- package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
- package/static/assets/init-Gi6I4Gst.js +1 -0
- package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
- package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
- package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
- package/static/assets/katex-C5jXJg4s.js +257 -0
- package/static/assets/layout-DEXfKzaS.js +1 -0
- package/static/assets/linear-CLO--ryk.js +1 -0
- package/static/assets/map-Czzmt4hB.js +1 -0
- package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
- package/static/assets/ordinal-Cboi1Yqb.js +1 -0
- package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
- package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
- package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
- package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
- package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
- package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
- package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
- package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
- package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
- package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
- package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
- package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
- package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
- package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
- package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
- package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
- package/static/index.html +24 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
# Messaging Patterns — RabbitMQ Pub/Sub
|
|
2
|
+
|
|
3
|
+
This reference describes the standalone RabbitMQ pub/sub messaging layer for inter-system
|
|
4
|
+
communication. This is **independent** from Laravel's internal event system and from
|
|
5
|
+
queue batching — it provides explicit publisher/consumer services that modules call to
|
|
6
|
+
communicate with external systems.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Architecture Overview
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
┌─────────────────────────────────────────────┐
|
|
14
|
+
│ RabbitMQ Broker │
|
|
15
|
+
│ │
|
|
16
|
+
┌──────────┐ │ ┌──────────────────┐ ┌───────────────┐ │ ┌──────────────┐
|
|
17
|
+
│ Module │ publish │ │ app.events │ │ app.commands │ │ consume │ External │
|
|
18
|
+
│ │────────►│ │ (TopicExchange) │ │ (DirectExch.) │ │────────►│ System A │
|
|
19
|
+
│ Service │ │ └────────┬─────────┘ └───────┬───────┘ │ └──────────────┘
|
|
20
|
+
└──────────┘ │ │ │ │
|
|
21
|
+
│ ┌─────▼──────┐ ┌─────▼──────┐ │
|
|
22
|
+
│ │ Queue: │ │ Queue: │ │
|
|
23
|
+
│ │ order.* │ │ inventory │ │
|
|
24
|
+
│ └─────┬──────┘ │ .sync │ │
|
|
25
|
+
│ │ └─────┬──────┘ │
|
|
26
|
+
┌──────────┐ consume │ │ │ │ ┌──────────────┐
|
|
27
|
+
│ This │◄─────────│───────────┘ └─────────│────────►│ External │
|
|
28
|
+
│ App │ │ │ │ System B │
|
|
29
|
+
└──────────┘ │ ┌──────────────────┐ │ └──────────────┘
|
|
30
|
+
│ │ app.dlx │ │
|
|
31
|
+
│ │ (Dead Letter) │ │
|
|
32
|
+
│ └──────────────────┘ │
|
|
33
|
+
└─────────────────────────────────────────────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Key concepts:**
|
|
37
|
+
- **Topic Exchange** (`app.events.exchange`) — pub/sub event broadcasting with routing key
|
|
38
|
+
patterns (e.g., `order.exported`, `order.cancelled`)
|
|
39
|
+
- **Direct Exchange** (`app.commands.exchange`) — point-to-point command delivery to a
|
|
40
|
+
specific queue (e.g., `inventory.sync`)
|
|
41
|
+
- **Dead Letter Exchange** (`app.dlx.exchange`) — failed messages routed here after max
|
|
42
|
+
retries for investigation and replay
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Composer Dependencies
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"require": {
|
|
51
|
+
"vladimir-yuldashev/laravel-queue-rabbitmq": "^14.0",
|
|
52
|
+
"php-amqplib/php-amqplib": "^3.7"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> **Note:** `laravel-queue-rabbitmq` provides the queue driver for consuming messages
|
|
58
|
+
> as Laravel jobs. `php-amqplib` provides low-level AMQP access for advanced exchange
|
|
59
|
+
> patterns (topic/direct publishing with routing keys).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Application Configuration
|
|
64
|
+
|
|
65
|
+
### .env
|
|
66
|
+
|
|
67
|
+
```env
|
|
68
|
+
RABBITMQ_HOST=localhost
|
|
69
|
+
RABBITMQ_PORT=5672
|
|
70
|
+
RABBITMQ_USER=guest
|
|
71
|
+
RABBITMQ_PASSWORD=guest
|
|
72
|
+
RABBITMQ_VHOST=/
|
|
73
|
+
RABBITMQ_QUEUE=default
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### config/queue.php (rabbitmq connection)
|
|
77
|
+
|
|
78
|
+
```php
|
|
79
|
+
'rabbitmq' => [
|
|
80
|
+
'driver' => 'rabbitmq',
|
|
81
|
+
'queue' => env('RABBITMQ_QUEUE', 'default'),
|
|
82
|
+
'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class,
|
|
83
|
+
'hosts' => [
|
|
84
|
+
[
|
|
85
|
+
'host' => env('RABBITMQ_HOST', '127.0.0.1'),
|
|
86
|
+
'port' => env('RABBITMQ_PORT', 5672),
|
|
87
|
+
'user' => env('RABBITMQ_USER', 'guest'),
|
|
88
|
+
'password' => env('RABBITMQ_PASSWORD', 'guest'),
|
|
89
|
+
'vhost' => env('RABBITMQ_VHOST', '/'),
|
|
90
|
+
],
|
|
91
|
+
],
|
|
92
|
+
'options' => [
|
|
93
|
+
'ssl_options' => [
|
|
94
|
+
'cafile' => env('RABBITMQ_SSL_CAFILE'),
|
|
95
|
+
'local_cert' => env('RABBITMQ_SSL_LOCALCERT'),
|
|
96
|
+
'local_key' => env('RABBITMQ_SSL_LOCALKEY'),
|
|
97
|
+
'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true),
|
|
98
|
+
],
|
|
99
|
+
'queue' => [
|
|
100
|
+
'exchange' => env('RABBITMQ_EXCHANGE', ''),
|
|
101
|
+
'exchange_type' => env('RABBITMQ_EXCHANGE_TYPE', 'direct'),
|
|
102
|
+
'exchange_routing_key' => env('RABBITMQ_EXCHANGE_ROUTING_KEY', ''),
|
|
103
|
+
],
|
|
104
|
+
'heartbeat' => 30,
|
|
105
|
+
],
|
|
106
|
+
],
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### config/messaging.php (custom exchange configuration)
|
|
110
|
+
|
|
111
|
+
```php
|
|
112
|
+
return [
|
|
113
|
+
'exchanges' => [
|
|
114
|
+
'events' => env('MESSAGING_EVENTS_EXCHANGE', 'app.events.exchange'),
|
|
115
|
+
'commands' => env('MESSAGING_COMMANDS_EXCHANGE', 'app.commands.exchange'),
|
|
116
|
+
'dead_letter' => env('MESSAGING_DLX_EXCHANGE', 'app.dlx.exchange'),
|
|
117
|
+
],
|
|
118
|
+
|
|
119
|
+
'queues' => [
|
|
120
|
+
'order_events' => 'app.events.order',
|
|
121
|
+
'inventory_sync' => 'app.commands.inventory-sync',
|
|
122
|
+
'dead_letter' => 'app.dlq',
|
|
123
|
+
],
|
|
124
|
+
];
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Package Structure
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
app/
|
|
133
|
+
└── Services/
|
|
134
|
+
└── Messaging/
|
|
135
|
+
├── RabbitMQInfrastructure.php # Exchange, queue, binding declarations
|
|
136
|
+
├── MessagePublisher.php # Generic publisher service
|
|
137
|
+
└── Consumers/
|
|
138
|
+
└── SampleEventConsumer.php # Sample consumer job
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## RabbitMQ Infrastructure Setup
|
|
144
|
+
|
|
145
|
+
Service to declare exchanges, queues, and bindings at application boot:
|
|
146
|
+
|
|
147
|
+
```php
|
|
148
|
+
namespace App\Services\Messaging;
|
|
149
|
+
|
|
150
|
+
use PhpAmqpLib\Channel\AMQPChannel;
|
|
151
|
+
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
|
152
|
+
use PhpAmqpLib\Wire\AMQPTable;
|
|
153
|
+
|
|
154
|
+
class RabbitMQInfrastructure
|
|
155
|
+
{
|
|
156
|
+
private AMQPChannel $channel;
|
|
157
|
+
|
|
158
|
+
public function __construct()
|
|
159
|
+
{
|
|
160
|
+
$connection = new AMQPStreamConnection(
|
|
161
|
+
config('queue.connections.rabbitmq.hosts.0.host'),
|
|
162
|
+
config('queue.connections.rabbitmq.hosts.0.port'),
|
|
163
|
+
config('queue.connections.rabbitmq.hosts.0.user'),
|
|
164
|
+
config('queue.connections.rabbitmq.hosts.0.password'),
|
|
165
|
+
config('queue.connections.rabbitmq.hosts.0.vhost'),
|
|
166
|
+
);
|
|
167
|
+
$this->channel = $connection->channel();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public function declareInfrastructure(): void
|
|
171
|
+
{
|
|
172
|
+
$eventsExchange = config('messaging.exchanges.events');
|
|
173
|
+
$commandsExchange = config('messaging.exchanges.commands');
|
|
174
|
+
$dlxExchange = config('messaging.exchanges.dead_letter');
|
|
175
|
+
|
|
176
|
+
// Declare exchanges
|
|
177
|
+
$this->channel->exchange_declare($eventsExchange, 'topic', false, true, false);
|
|
178
|
+
$this->channel->exchange_declare($commandsExchange, 'direct', false, true, false);
|
|
179
|
+
$this->channel->exchange_declare($dlxExchange, 'fanout', false, true, false);
|
|
180
|
+
|
|
181
|
+
// Dead letter queue
|
|
182
|
+
$this->channel->queue_declare('app.dlq', false, true, false, false);
|
|
183
|
+
$this->channel->queue_bind('app.dlq', $dlxExchange);
|
|
184
|
+
|
|
185
|
+
// Sample event queue (Topic Exchange) — subscribe to order.* events
|
|
186
|
+
$this->channel->queue_declare(
|
|
187
|
+
config('messaging.queues.order_events'),
|
|
188
|
+
false, true, false, false,
|
|
189
|
+
false,
|
|
190
|
+
new AMQPTable(['x-dead-letter-exchange' => $dlxExchange])
|
|
191
|
+
);
|
|
192
|
+
$this->channel->queue_bind(
|
|
193
|
+
config('messaging.queues.order_events'),
|
|
194
|
+
$eventsExchange,
|
|
195
|
+
'order.*'
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// Sample command queue (Direct Exchange) — inventory sync
|
|
199
|
+
$this->channel->queue_declare(
|
|
200
|
+
config('messaging.queues.inventory_sync'),
|
|
201
|
+
false, true, false, false,
|
|
202
|
+
false,
|
|
203
|
+
new AMQPTable(['x-dead-letter-exchange' => $dlxExchange])
|
|
204
|
+
);
|
|
205
|
+
$this->channel->queue_bind(
|
|
206
|
+
config('messaging.queues.inventory_sync'),
|
|
207
|
+
$commandsExchange,
|
|
208
|
+
'inventory.sync'
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Register as an Artisan command for setup:
|
|
215
|
+
```php
|
|
216
|
+
namespace App\Console\Commands;
|
|
217
|
+
|
|
218
|
+
use App\Services\Messaging\RabbitMQInfrastructure;
|
|
219
|
+
use Illuminate\Console\Command;
|
|
220
|
+
|
|
221
|
+
class SetupRabbitMQCommand extends Command
|
|
222
|
+
{
|
|
223
|
+
protected $signature = 'rabbitmq:setup';
|
|
224
|
+
protected $description = 'Declare RabbitMQ exchanges, queues, and bindings';
|
|
225
|
+
|
|
226
|
+
public function handle(RabbitMQInfrastructure $infra): int
|
|
227
|
+
{
|
|
228
|
+
$infra->declareInfrastructure();
|
|
229
|
+
$this->info('RabbitMQ infrastructure declared successfully.');
|
|
230
|
+
return self::SUCCESS;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Publisher Service
|
|
238
|
+
|
|
239
|
+
```php
|
|
240
|
+
namespace App\Services\Messaging;
|
|
241
|
+
|
|
242
|
+
use Illuminate\Support\Facades\Log;
|
|
243
|
+
use PhpAmqpLib\Channel\AMQPChannel;
|
|
244
|
+
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
|
245
|
+
use PhpAmqpLib\Message\AMQPMessage;
|
|
246
|
+
|
|
247
|
+
class MessagePublisher
|
|
248
|
+
{
|
|
249
|
+
private AMQPChannel $channel;
|
|
250
|
+
|
|
251
|
+
public function __construct()
|
|
252
|
+
{
|
|
253
|
+
$connection = new AMQPStreamConnection(
|
|
254
|
+
config('queue.connections.rabbitmq.hosts.0.host'),
|
|
255
|
+
config('queue.connections.rabbitmq.hosts.0.port'),
|
|
256
|
+
config('queue.connections.rabbitmq.hosts.0.user'),
|
|
257
|
+
config('queue.connections.rabbitmq.hosts.0.password'),
|
|
258
|
+
config('queue.connections.rabbitmq.hosts.0.vhost'),
|
|
259
|
+
);
|
|
260
|
+
$this->channel = $connection->channel();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Publish a domain event to the topic exchange.
|
|
265
|
+
* Consumers subscribe via routing key patterns (e.g., "order.*").
|
|
266
|
+
*/
|
|
267
|
+
public function publishEvent(string $routingKey, array $event): void
|
|
268
|
+
{
|
|
269
|
+
Log::info("Publishing event [{$routingKey}]", $event);
|
|
270
|
+
|
|
271
|
+
$message = new AMQPMessage(
|
|
272
|
+
json_encode($event),
|
|
273
|
+
['content_type' => 'application/json', 'delivery_mode' => 2]
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
$this->channel->basic_publish(
|
|
277
|
+
$message,
|
|
278
|
+
config('messaging.exchanges.events'),
|
|
279
|
+
$routingKey
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Send a command to the direct exchange for point-to-point delivery.
|
|
285
|
+
*/
|
|
286
|
+
public function sendCommand(string $routingKey, array $command): void
|
|
287
|
+
{
|
|
288
|
+
Log::info("Sending command [{$routingKey}]", $command);
|
|
289
|
+
|
|
290
|
+
$message = new AMQPMessage(
|
|
291
|
+
json_encode($command),
|
|
292
|
+
['content_type' => 'application/json', 'delivery_mode' => 2]
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
$this->channel->basic_publish(
|
|
296
|
+
$message,
|
|
297
|
+
config('messaging.exchanges.commands'),
|
|
298
|
+
$routingKey
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Consumer Job
|
|
307
|
+
|
|
308
|
+
Consumers are implemented as Laravel queue jobs consumed via `laravel-queue-rabbitmq`:
|
|
309
|
+
|
|
310
|
+
```php
|
|
311
|
+
namespace App\Jobs\Consumers;
|
|
312
|
+
|
|
313
|
+
use Illuminate\Bus\Queueable;
|
|
314
|
+
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
315
|
+
use Illuminate\Foundation\Bus\Dispatchable;
|
|
316
|
+
use Illuminate\Queue\InteractsWithQueue;
|
|
317
|
+
use Illuminate\Queue\SerializesModels;
|
|
318
|
+
use Illuminate\Support\Facades\Log;
|
|
319
|
+
|
|
320
|
+
class OrderEventConsumer implements ShouldQueue
|
|
321
|
+
{
|
|
322
|
+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
323
|
+
|
|
324
|
+
public int $tries = 3;
|
|
325
|
+
public int $backoff = 5;
|
|
326
|
+
|
|
327
|
+
public function __construct(
|
|
328
|
+
public readonly array $payload,
|
|
329
|
+
) {}
|
|
330
|
+
|
|
331
|
+
public function handle(): void
|
|
332
|
+
{
|
|
333
|
+
Log::info('Received order event', $this->payload);
|
|
334
|
+
// Process the event — e.g., notify downstream systems, update read models
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
public function failed(\Throwable $exception): void
|
|
338
|
+
{
|
|
339
|
+
Log::error('Failed to process order event', [
|
|
340
|
+
'payload' => $this->payload,
|
|
341
|
+
'error' => $exception->getMessage(),
|
|
342
|
+
]);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
For low-level AMQP consumption (bypassing Laravel queues):
|
|
348
|
+
```php
|
|
349
|
+
// Custom Artisan command for consuming from specific queues
|
|
350
|
+
namespace App\Console\Commands;
|
|
351
|
+
|
|
352
|
+
use Illuminate\Console\Command;
|
|
353
|
+
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
|
354
|
+
|
|
355
|
+
class ConsumeOrderEventsCommand extends Command
|
|
356
|
+
{
|
|
357
|
+
protected $signature = 'rabbitmq:consume-order-events';
|
|
358
|
+
protected $description = 'Consume messages from the order events queue';
|
|
359
|
+
|
|
360
|
+
public function handle(): int
|
|
361
|
+
{
|
|
362
|
+
$connection = new AMQPStreamConnection(
|
|
363
|
+
config('queue.connections.rabbitmq.hosts.0.host'),
|
|
364
|
+
config('queue.connections.rabbitmq.hosts.0.port'),
|
|
365
|
+
config('queue.connections.rabbitmq.hosts.0.user'),
|
|
366
|
+
config('queue.connections.rabbitmq.hosts.0.password'),
|
|
367
|
+
config('queue.connections.rabbitmq.hosts.0.vhost'),
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
$channel = $connection->channel();
|
|
371
|
+
$channel->basic_qos(0, 10, false);
|
|
372
|
+
|
|
373
|
+
$channel->basic_consume(
|
|
374
|
+
config('messaging.queues.order_events'),
|
|
375
|
+
'',
|
|
376
|
+
false, false, false, false,
|
|
377
|
+
function ($msg) {
|
|
378
|
+
$payload = json_decode($msg->body, true);
|
|
379
|
+
$this->info("Received: " . json_encode($payload));
|
|
380
|
+
|
|
381
|
+
// Process...
|
|
382
|
+
|
|
383
|
+
$msg->ack();
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
$this->info('Waiting for order events...');
|
|
388
|
+
while ($channel->is_consuming()) {
|
|
389
|
+
$channel->wait();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return self::SUCCESS;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Usage from Modules
|
|
400
|
+
|
|
401
|
+
Modules inject `MessagePublisher` to send messages to external systems.
|
|
402
|
+
This is **separate** from Laravel's `event()` helper, which handles in-process
|
|
403
|
+
inter-module events.
|
|
404
|
+
|
|
405
|
+
```php
|
|
406
|
+
namespace Modules\Order\Services;
|
|
407
|
+
|
|
408
|
+
use App\Services\Messaging\MessagePublisher;
|
|
409
|
+
use Modules\Order\Contracts\OrderServiceInterface;
|
|
410
|
+
use Modules\Order\Models\Order;
|
|
411
|
+
|
|
412
|
+
class OrderService implements OrderServiceInterface
|
|
413
|
+
{
|
|
414
|
+
public function __construct(
|
|
415
|
+
private readonly MessagePublisher $publisher,
|
|
416
|
+
) {}
|
|
417
|
+
|
|
418
|
+
public function exportOrder(string $orderId): void
|
|
419
|
+
{
|
|
420
|
+
$order = Order::findOrFail($orderId);
|
|
421
|
+
|
|
422
|
+
// ... export logic ...
|
|
423
|
+
|
|
424
|
+
// Publish event to external systems via RabbitMQ
|
|
425
|
+
$this->publisher->publishEvent('order.exported', [
|
|
426
|
+
'order_id' => $order->id,
|
|
427
|
+
'customer_id' => $order->customer_id,
|
|
428
|
+
'exported_at' => now()->toIso8601String(),
|
|
429
|
+
]);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Error Handling
|
|
437
|
+
|
|
438
|
+
### Retry with Dead Letter Queue
|
|
439
|
+
|
|
440
|
+
Queues are configured with `x-dead-letter-exchange` pointing to `app.dlx.exchange`.
|
|
441
|
+
When a message fails after the configured retry attempts, it is routed to the dead
|
|
442
|
+
letter queue (`app.dlq`) for manual investigation.
|
|
443
|
+
|
|
444
|
+
### Failed Jobs Table
|
|
445
|
+
|
|
446
|
+
Laravel's `failed_jobs` table catches jobs that exhaust all retries:
|
|
447
|
+
```bash
|
|
448
|
+
php artisan queue:failed
|
|
449
|
+
php artisan queue:retry <id>
|
|
450
|
+
php artisan queue:flush
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Operational Notes
|
|
456
|
+
|
|
457
|
+
- **Connection pooling**: Use `AMQPLazyConnection` to establish connections on-demand
|
|
458
|
+
rather than at boot time.
|
|
459
|
+
- **Heartbeat**: Set `heartbeat: 30` in queue config to detect dropped connections
|
|
460
|
+
within 60 seconds (2x heartbeat interval).
|
|
461
|
+
- **Consumer prefetch**: Set `basic_qos(0, 10, false)` for slow consumers to avoid
|
|
462
|
+
message starvation across instances.
|
|
463
|
+
- **Publisher confirms**: Enable `confirm_select()` on the channel in production to
|
|
464
|
+
guarantee message delivery to the broker.
|
|
465
|
+
- **Monitoring**: Monitor the dead letter queue (`app.dlq`) for failed messages.
|
|
466
|
+
Integrate with your alerting system to notify on DLQ accumulation.
|
|
467
|
+
- **Queue naming convention**: All messaging queues use the `app.*` prefix. Batch
|
|
468
|
+
queues use the default Laravel queue naming. These namespaces must not overlap.
|