@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.
Files changed (190) hide show
  1. package/dist/index.js +25449 -0
  2. package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
  3. package/drizzle/0001_wise_wolf_cub.sql +13 -0
  4. package/drizzle/0002_secret_spyke.sql +16 -0
  5. package/drizzle/0003_workspace_config_singleton.sql +16 -0
  6. package/drizzle/0004_terminal_claim_v120.sql +11 -0
  7. package/drizzle/0005_brainstorm_v124.sql +10 -0
  8. package/drizzle/0006_project_v140.sql +6 -0
  9. package/drizzle/0007_prd_wizard_v124.sql +14 -0
  10. package/drizzle/0008_workflow_artifact_v126.sql +24 -0
  11. package/drizzle/0009_project_import_v150.sql +7 -0
  12. package/drizzle/0010_quality_v151.sql +3 -0
  13. package/drizzle/meta/0000_snapshot.json +1335 -0
  14. package/drizzle/meta/0001_snapshot.json +1416 -0
  15. package/drizzle/meta/0002_snapshot.json +1510 -0
  16. package/drizzle/meta/_journal.json +83 -0
  17. package/package.json +41 -0
  18. package/plugin/.claude-plugin/marketplace.json +36 -0
  19. package/plugin/.claude-plugin/plugin.json +22 -0
  20. package/plugin/README.md +625 -0
  21. package/plugin/SKILLS.md +350 -0
  22. package/plugin/co2.svg +5 -0
  23. package/plugin/commands/brainstorm-loop.md +129 -0
  24. package/plugin/skills/conductor-defect/SKILL.md +898 -0
  25. package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
  26. package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
  27. package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
  28. package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
  29. package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
  30. package/plugin/skills/depgen-k8s/SKILL.md +468 -0
  31. package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
  32. package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
  33. package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
  34. package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
  35. package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
  36. package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
  37. package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
  38. package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
  39. package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
  40. package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
  41. package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
  42. package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
  43. package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
  44. package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
  45. package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
  46. package/plugin/skills/modelgen-relational/SKILL.md +707 -0
  47. package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
  48. package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
  49. package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
  50. package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
  51. package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
  52. package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
  53. package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
  54. package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
  55. package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
  56. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
  57. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
  58. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
  59. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
  60. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
  61. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
  62. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
  63. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
  64. package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
  65. package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
  66. package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
  67. package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
  68. package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
  69. package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
  70. package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
  71. package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
  72. package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
  73. package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
  74. package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
  75. package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
  76. package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
  77. package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
  78. package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
  79. package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
  80. package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
  81. package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
  82. package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
  83. package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
  84. package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
  85. package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
  86. package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
  87. package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
  88. package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
  89. package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
  90. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
  91. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
  92. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
  93. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
  94. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
  95. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
  96. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
  97. package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
  98. package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
  99. package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
  100. package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
  101. package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
  102. package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
  103. package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
  104. package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
  105. package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
  106. package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
  107. package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
  108. package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
  109. package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
  110. package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
  111. package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
  112. package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
  113. package/plugin/skills/testgen-functional/SKILL.md +1381 -0
  114. package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
  115. package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
  116. package/plugin/skills/util-projectinit/SKILL.md +307 -0
  117. package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
  118. package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
  119. package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
  120. package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
  121. package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
  122. package/plugin/skills/util-projectsync/SKILL.md +501 -0
  123. package/plugin/skills/util-updprd/SKILL.md +324 -0
  124. package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
  125. package/plugin/skills/util-ustagger/SKILL.md +154 -0
  126. package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
  127. package/static/assets/arc-DRKw7sjs.js +1 -0
  128. package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
  129. package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
  130. package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
  131. package/static/assets/channel-C2nVbuwv.js +1 -0
  132. package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
  133. package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
  134. package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
  135. package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
  136. package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
  137. package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
  138. package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
  139. package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
  140. package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
  141. package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
  142. package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
  143. package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
  144. package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
  145. package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
  146. package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
  147. package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
  148. package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
  149. package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  150. package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
  151. package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
  152. package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
  153. package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
  154. package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
  155. package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
  156. package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
  157. package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
  158. package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
  159. package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
  160. package/static/assets/graph-C9eacEi8.js +1 -0
  161. package/static/assets/index-LZSQkLE2.css +41 -0
  162. package/static/assets/index-y7XDSgMG.js +495 -0
  163. package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
  164. package/static/assets/init-Gi6I4Gst.js +1 -0
  165. package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
  166. package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
  167. package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
  168. package/static/assets/katex-C5jXJg4s.js +257 -0
  169. package/static/assets/layout-DEXfKzaS.js +1 -0
  170. package/static/assets/linear-CLO--ryk.js +1 -0
  171. package/static/assets/map-Czzmt4hB.js +1 -0
  172. package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
  173. package/static/assets/ordinal-Cboi1Yqb.js +1 -0
  174. package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
  175. package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
  176. package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
  177. package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
  178. package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
  179. package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
  180. package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
  181. package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
  182. package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
  183. package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
  184. package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
  185. package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
  186. package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
  187. package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
  188. package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
  189. package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
  190. package/static/index.html +24 -0
@@ -0,0 +1,512 @@
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 Spring Modulith's internal application events
5
+ and from Spring Batch remote partitioning — it provides explicit publisher/consumer
6
+ services that modules call to communicate with external systems.
7
+
8
+ ---
9
+
10
+ ## Architecture Overview
11
+
12
+ ```
13
+ ┌─────────────────────────────────────────────┐
14
+ │ RabbitMQ Broker │
15
+ │ │
16
+ ┌──────────┐ │ ┌──────────────────┐ ┌───────────────┐ │ ┌──────────────┐
17
+ │ │ publish │ │ app.events │ │ app.commands │ │ consume │ External │
18
+ │ Module │────────►│ │ (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
+ ## Maven Dependencies
47
+
48
+ ```xml
49
+ <!-- RabbitMQ Messaging (included when Messaging = yes) -->
50
+ <dependency>
51
+ <groupId>org.springframework.boot</groupId>
52
+ <artifactId>spring-boot-starter-amqp</artifactId>
53
+ </dependency>
54
+ ```
55
+
56
+ > **Note:** If Remote Partitioning is also selected, `spring-boot-starter-amqp` is already
57
+ > present — do not duplicate. The same RabbitMQ broker connection is shared.
58
+
59
+ ---
60
+
61
+ ## Application Configuration
62
+
63
+ ```yaml
64
+ # [If Messaging = yes] add to application.yml:
65
+ spring:
66
+ rabbitmq:
67
+ host: localhost
68
+ port: 5672
69
+ username: guest
70
+ password: guest
71
+ virtual-host: /
72
+
73
+ app:
74
+ messaging:
75
+ events-exchange: app.events.exchange
76
+ commands-exchange: app.commands.exchange
77
+ dead-letter-exchange: app.dlx.exchange
78
+ ```
79
+
80
+ > **Shared broker config:** If Remote Partitioning is also selected, the `spring.rabbitmq.*`
81
+ > connection block is shared — include it only once. The batch partition queues
82
+ > (`batch.partition.*`) and messaging queues (`app.*`) use different exchanges and routing
83
+ > keys, so they do not conflict.
84
+
85
+ ### application-prod.yml overrides
86
+
87
+ ```yaml
88
+ spring:
89
+ rabbitmq:
90
+ host: ${RABBITMQ_HOST}
91
+ port: ${RABBITMQ_PORT:5672}
92
+ username: ${RABBITMQ_USERNAME}
93
+ password: ${RABBITMQ_PASSWORD}
94
+ virtual-host: ${RABBITMQ_VHOST:/}
95
+ connection-timeout: 10000
96
+ requested-heartbeat: 30
97
+ publisher-confirm-type: correlated
98
+ publisher-returns: true
99
+ listener:
100
+ simple:
101
+ prefetch: 10
102
+ concurrency: 2
103
+ max-concurrency: 5
104
+ retry:
105
+ enabled: true
106
+ initial-interval: 1000
107
+ max-attempts: 3
108
+ max-interval: 10000
109
+ multiplier: 2.0
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Package Structure
115
+
116
+ Messaging artifacts split across two locations to honor Spring Modulith boundaries:
117
+
118
+ ```
119
+ shared/
120
+ └── messaging/ # Shared infrastructure ONLY (no business logic)
121
+ ├── RabbitMQMessagingConfig.java # Exchange, queue, binding declarations
122
+ ├── RabbitMQPublisher.java # Generic publisher service
123
+ ├── MessageConverterConfig.java # Jackson2JsonMessageConverter bean
124
+ ├── OrderExportedEvent.java # Inter-system event/command DTOs
125
+ └── InventorySyncCommand.java
126
+
127
+ {{module}}/ # Module — PUBLIC API
128
+ └── internal/ # INTERNAL — hidden from other modules
129
+ ├── {{Module}}ServiceImpl.java
130
+ ├── ...
131
+ └── {{Module}}EventConsumer.java # @RabbitListener — MUST live here, NOT in shared/
132
+ ```
133
+
134
+ > **Convention — MQ listeners are module-internal.**
135
+ > Every `@RabbitListener` (and any other inbound MQ adapter) is a module-specific
136
+ > **inbound adapter** — it belongs inside the owning module's `internal/` package, never
137
+ > under `shared/messaging/consumer/` or any other shared location. This mirrors how
138
+ > page/fragment controllers are placed: inside `internal/`. The shared `messaging/`
139
+ > package holds infrastructure beans only (config, publisher, converter, cross-module
140
+ > DTOs) — it must contain **no** `@RabbitListener` classes.
141
+ >
142
+ > Reasoning:
143
+ > - A consumer that calls `OrderService` belongs to the `order` module's bounded context.
144
+ > - Placing it in `shared/` lets it bypass module boundaries and inject any `*ServiceImpl`,
145
+ > silently breaking Spring Modulith encapsulation and making `ApplicationModules.verify()`
146
+ > results misleading.
147
+ > - Module-internal placement keeps the listener, the service it drives, and the entity
148
+ > it persists colocated and independently verifiable.
149
+
150
+ ---
151
+
152
+ ## RabbitMQ Infrastructure Configuration
153
+
154
+ ```java
155
+ package {{BASE_PACKAGE}}.shared.messaging;
156
+
157
+ import org.springframework.amqp.core.Binding;
158
+ import org.springframework.amqp.core.BindingBuilder;
159
+ import org.springframework.amqp.core.DirectExchange;
160
+ import org.springframework.amqp.core.FanoutExchange;
161
+ import org.springframework.amqp.core.Queue;
162
+ import org.springframework.amqp.core.QueueBuilder;
163
+ import org.springframework.amqp.core.TopicExchange;
164
+ import org.springframework.beans.factory.annotation.Value;
165
+ import org.springframework.context.annotation.Bean;
166
+ import org.springframework.context.annotation.Configuration;
167
+
168
+ @Configuration
169
+ public class RabbitMQMessagingConfig {
170
+
171
+ @Value("${app.messaging.events-exchange}")
172
+ private String eventsExchangeName;
173
+
174
+ @Value("${app.messaging.commands-exchange}")
175
+ private String commandsExchangeName;
176
+
177
+ @Value("${app.messaging.dead-letter-exchange}")
178
+ private String dlxExchangeName;
179
+
180
+ // ── Exchanges ────────────────────────────────────────────────────────
181
+
182
+ @Bean
183
+ TopicExchange eventsExchange() {
184
+ return new TopicExchange(eventsExchangeName, true, false);
185
+ }
186
+
187
+ @Bean
188
+ DirectExchange commandsExchange() {
189
+ return new DirectExchange(commandsExchangeName, true, false);
190
+ }
191
+
192
+ @Bean
193
+ FanoutExchange deadLetterExchange() {
194
+ return new FanoutExchange(dlxExchangeName, true, false);
195
+ }
196
+
197
+ // ── Dead Letter Queue ────────────────────────────────────────────────
198
+
199
+ @Bean
200
+ Queue deadLetterQueue() {
201
+ return QueueBuilder.durable("app.dlq").build();
202
+ }
203
+
204
+ @Bean
205
+ Binding deadLetterBinding(Queue deadLetterQueue, FanoutExchange deadLetterExchange) {
206
+ return BindingBuilder.bind(deadLetterQueue).to(deadLetterExchange);
207
+ }
208
+
209
+ // ── Sample Event Queue (Topic Exchange) ──────────────────────────────
210
+ // Subscribe to all order-related events via routing key pattern "order.*"
211
+
212
+ @Bean
213
+ Queue orderEventsQueue() {
214
+ return QueueBuilder.durable("app.events.order")
215
+ .withArgument("x-dead-letter-exchange", dlxExchangeName)
216
+ .build();
217
+ }
218
+
219
+ @Bean
220
+ Binding orderEventsBinding(Queue orderEventsQueue, TopicExchange eventsExchange) {
221
+ return BindingBuilder.bind(orderEventsQueue).to(eventsExchange).with("order.*");
222
+ }
223
+
224
+ // ── Sample Command Queue (Direct Exchange) ───────────────────────────
225
+ // Point-to-point command delivery for inventory sync
226
+
227
+ @Bean
228
+ Queue inventorySyncQueue() {
229
+ return QueueBuilder.durable("app.commands.inventory-sync")
230
+ .withArgument("x-dead-letter-exchange", dlxExchangeName)
231
+ .build();
232
+ }
233
+
234
+ @Bean
235
+ Binding inventorySyncBinding(Queue inventorySyncQueue, DirectExchange commandsExchange) {
236
+ return BindingBuilder.bind(inventorySyncQueue).to(commandsExchange).with("inventory.sync");
237
+ }
238
+ }
239
+ ```
240
+
241
+ ---
242
+
243
+ ## Message Converter
244
+
245
+ ```java
246
+ package {{BASE_PACKAGE}}.shared.messaging;
247
+
248
+ import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
249
+ import org.springframework.amqp.support.converter.MessageConverter;
250
+ import org.springframework.context.annotation.Bean;
251
+ import org.springframework.context.annotation.Configuration;
252
+
253
+ @Configuration
254
+ public class MessageConverterConfig {
255
+
256
+ @Bean
257
+ MessageConverter jackson2JsonMessageConverter() {
258
+ return new Jackson2JsonMessageConverter();
259
+ }
260
+ }
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Publisher Service
266
+
267
+ ```java
268
+ package {{BASE_PACKAGE}}.shared.messaging;
269
+
270
+ import lombok.RequiredArgsConstructor;
271
+ import lombok.extern.slf4j.Slf4j;
272
+ import org.springframework.amqp.rabbit.core.RabbitTemplate;
273
+ import org.springframework.beans.factory.annotation.Value;
274
+ import org.springframework.stereotype.Service;
275
+
276
+ @Service
277
+ @RequiredArgsConstructor
278
+ @Slf4j
279
+ public class RabbitMQPublisher {
280
+
281
+ private final RabbitTemplate rabbitTemplate;
282
+
283
+ @Value("${app.messaging.events-exchange}")
284
+ private String eventsExchange;
285
+
286
+ @Value("${app.messaging.commands-exchange}")
287
+ private String commandsExchange;
288
+
289
+ /**
290
+ * Publish a domain event to the topic exchange.
291
+ * Consumers subscribe via routing key patterns (e.g., "order.*").
292
+ *
293
+ * @param routingKey the routing key (e.g., "order.exported", "order.cancelled")
294
+ * @param event the event payload — serialized to JSON automatically
295
+ */
296
+ public void publishEvent(String routingKey, Object event) {
297
+ log.info("Publishing event [{}]: {}", routingKey, event.getClass().getSimpleName());
298
+ rabbitTemplate.convertAndSend(eventsExchange, routingKey, event);
299
+ }
300
+
301
+ /**
302
+ * Send a command to the direct exchange for point-to-point delivery.
303
+ *
304
+ * @param routingKey the routing key matching a specific queue (e.g., "inventory.sync")
305
+ * @param command the command payload — serialized to JSON automatically
306
+ */
307
+ public void sendCommand(String routingKey, Object command) {
308
+ log.info("Sending command [{}]: {}", routingKey, command.getClass().getSimpleName());
309
+ rabbitTemplate.convertAndSend(commandsExchange, routingKey, command);
310
+ }
311
+ }
312
+ ```
313
+
314
+ ---
315
+
316
+ ## Sample Event / Command DTOs
317
+
318
+ ```java
319
+ package {{BASE_PACKAGE}}.shared.messaging;
320
+
321
+ import java.time.Instant;
322
+
323
+ /**
324
+ * Sample domain event broadcast to external systems via the topic exchange.
325
+ */
326
+ public record OrderExportedEvent(
327
+ String orderId,
328
+ String customerId,
329
+ Instant exportedAt
330
+ ) {}
331
+
332
+ /**
333
+ * Sample point-to-point command sent via the direct exchange.
334
+ */
335
+ public record InventorySyncCommand(
336
+ String productId,
337
+ int quantity,
338
+ String warehouseCode
339
+ ) {}
340
+ ```
341
+
342
+ ---
343
+
344
+ ## Consumer Service (module-internal)
345
+
346
+ The consumer lives inside the owning module's `internal/` package — **never** in
347
+ `shared/messaging/`. Use package-private visibility (no `public` modifier) so Spring
348
+ Modulith treats it as internal-only.
349
+
350
+ ```java
351
+ package {{BASE_PACKAGE}}.order.internal;
352
+
353
+ import {{BASE_PACKAGE}}.order.OrderService;
354
+ import {{BASE_PACKAGE}}.shared.messaging.OrderExportedEvent;
355
+ import lombok.RequiredArgsConstructor;
356
+ import lombok.extern.slf4j.Slf4j;
357
+ import org.springframework.amqp.rabbit.annotation.RabbitListener;
358
+ import org.springframework.stereotype.Component;
359
+
360
+ @Component
361
+ @RequiredArgsConstructor
362
+ @Slf4j
363
+ class OrderEventConsumer {
364
+
365
+ private final OrderService orderService;
366
+
367
+ /**
368
+ * Listens to order events from the topic exchange.
369
+ * The queue "app.events.order" is bound with routing key pattern "order.*".
370
+ *
371
+ * Inbound adapter — adapts a RabbitMQ message into a call against the module's
372
+ * public service interface (OrderService). Same role as a page/fragment controller,
373
+ * but for MQ instead of HTTP.
374
+ */
375
+ @RabbitListener(queues = "app.events.order")
376
+ void handleOrderEvent(OrderExportedEvent event) {
377
+ log.info("Received order exported event: orderId={}, customerId={}",
378
+ event.orderId(), event.customerId());
379
+ // Delegate to the module's public service interface.
380
+ // Do NOT inject another module's *ServiceImpl or repository here.
381
+ }
382
+ }
383
+ ```
384
+
385
+ > **Placement rule** — for every `@RabbitListener` you generate:
386
+ > 1. Identify the **bounded context** it serves (which module's data/logic it drives).
387
+ > 2. Put the class in `{{BASE_PACKAGE}}.<that-module>.internal`.
388
+ > 3. Make it package-private (drop `public`) so other modules cannot reference it.
389
+ > 4. Inject only that module's own components or other modules' **public service interfaces** — never another module's `*ServiceImpl`, `*Repository`, or `*Entity`.
390
+ > 5. If a single message would fan out work across multiple modules, publish a Spring Modulith `ApplicationEvent` from the listener and let each module react via `@ApplicationModuleListener` in its own `internal/` package.
391
+
392
+ ---
393
+
394
+ ## Error Handling
395
+
396
+ ### Retry with Dead Letter Queue
397
+
398
+ Queues are configured with `x-dead-letter-exchange` pointing to `app.dlx.exchange`.
399
+ When a message fails after the configured retry attempts, Spring AMQP routes it to the
400
+ dead letter queue (`app.dlq`) for manual investigation.
401
+
402
+ ### Custom Error Handler (optional)
403
+
404
+ ```java
405
+ package {{BASE_PACKAGE}}.shared.messaging;
406
+
407
+ import lombok.extern.slf4j.Slf4j;
408
+ import org.springframework.amqp.core.Message;
409
+ import org.springframework.amqp.rabbit.listener.api.RabbitListenerErrorHandler;
410
+ import org.springframework.amqp.rabbit.support.ListenerExecutionFailedException;
411
+ import org.springframework.stereotype.Component;
412
+
413
+ @Component("messagingErrorHandler")
414
+ @Slf4j
415
+ public class MessagingErrorHandler implements RabbitListenerErrorHandler {
416
+
417
+ @Override
418
+ public Object handleError(Message amqpMessage,
419
+ org.springframework.messaging.Message<?> message,
420
+ ListenerExecutionFailedException exception) {
421
+ log.error("Failed to process message from queue [{}]: {}",
422
+ amqpMessage.getMessageProperties().getConsumerQueue(),
423
+ exception.getCause().getMessage(),
424
+ exception);
425
+ // Optionally: send alert, record metric, etc.
426
+ // Re-throw to trigger retry / DLQ routing
427
+ throw exception;
428
+ }
429
+ }
430
+ ```
431
+
432
+ Usage in a consumer:
433
+ ```java
434
+ @RabbitListener(queues = "app.events.order", errorHandler = "messagingErrorHandler")
435
+ public void handleOrderEvent(OrderExportedEvent event) {
436
+ // ...
437
+ }
438
+ ```
439
+
440
+ ### Retry Configuration (application.yml)
441
+
442
+ ```yaml
443
+ spring:
444
+ rabbitmq:
445
+ listener:
446
+ simple:
447
+ retry:
448
+ enabled: true
449
+ initial-interval: 1000
450
+ max-attempts: 3
451
+ max-interval: 10000
452
+ multiplier: 2.0
453
+ ```
454
+
455
+ ---
456
+
457
+ ## Usage from Modules
458
+
459
+ Modules inject `RabbitMQPublisher` to send messages to external systems.
460
+ This is **separate** from Spring Modulith's `ApplicationEventPublisher`, which handles
461
+ in-process inter-module events.
462
+
463
+ ```java
464
+ package {{BASE_PACKAGE}}.order.internal;
465
+
466
+ import {{BASE_PACKAGE}}.shared.messaging.OrderExportedEvent;
467
+ import {{BASE_PACKAGE}}.shared.messaging.RabbitMQPublisher;
468
+ import lombok.RequiredArgsConstructor;
469
+ import lombok.extern.slf4j.Slf4j;
470
+ import org.springframework.stereotype.Service;
471
+ import java.time.Instant;
472
+
473
+ @Service
474
+ @RequiredArgsConstructor
475
+ @Slf4j
476
+ class OrderServiceImpl implements OrderService {
477
+
478
+ private final OrderRepository repository;
479
+ private final RabbitMQPublisher messagingPublisher;
480
+
481
+ public void exportOrder(String orderId) {
482
+ var order = repository.findById(orderId)
483
+ .orElseThrow(() -> new OrderException.NotFound(orderId));
484
+
485
+ // ... export logic ...
486
+
487
+ // Publish event to external systems via RabbitMQ
488
+ messagingPublisher.publishEvent("order.exported",
489
+ new OrderExportedEvent(order.getId(), order.getCustomerId(), Instant.now()));
490
+ log.info("Order {} exported and event published", orderId);
491
+ }
492
+ }
493
+ ```
494
+
495
+ ---
496
+
497
+ ## Operational Notes
498
+
499
+ - **Connection pooling**: Spring Boot auto-configures a `CachingConnectionFactory`.
500
+ Default channel cache size is 25. Adjust `spring.rabbitmq.cache.channel.size` for
501
+ high-throughput scenarios.
502
+ - **Heartbeat**: Set `spring.rabbitmq.requested-heartbeat: 30` in production to detect
503
+ dropped connections within 60 seconds (2x heartbeat interval).
504
+ - **Consumer prefetch**: `spring.rabbitmq.listener.simple.prefetch` controls how many
505
+ messages a consumer fetches at once. Default is 250; set to 1-10 for slow consumers
506
+ to avoid message starvation across instances.
507
+ - **Publisher confirms**: Enable `spring.rabbitmq.publisher-confirm-type: correlated` in
508
+ production to guarantee message delivery to the broker.
509
+ - **Monitoring**: Monitor the dead letter queue (`app.dlq`) for failed messages.
510
+ Integrate with your alerting system to notify on DLQ message accumulation.
511
+ - **Queue naming convention**: All messaging queues use the `app.*` prefix. Batch
512
+ partition queues use `batch.partition.*`. These namespaces must not overlap.