# Messaging (ActiveMQ / RocketMQ)

Not every integration in xly is a synchronous HTTP call. The framework
runs two message brokers, each with a different role:

| Broker | Used for | Producer | Consumer |
|---|---|---|---|
| **ActiveMQ / JMS** | In-cluster fan-out events: base-data merge jobs (consolidating per-tenant rows into flattened lookup tables) and document-update / document-delete notifications. **Despite the historical naming of one queue, this channel is NOT used for Redis cache invalidation** — see [Cache invalidation on metadata change](../reference/maintainer/cache-invalidation.md) for the actual cache-evict path. | `xlyErpJmsProductor` | `xlyErpJmsConsumer` |
| **RocketMQ** | Other integration flows where the ActiveMQ assumptions don't fit. | `RocketMQServiceImpl` (in `xlyBusinessService`) | (varies — service-specific) |

This page is a pointer rather than a deep dive — exact queue names and
payloads are documented at the consumer-thread level in
`xlyErpJmsConsumer/src/main/java/com/xly/xlyerpjmsconsumer/`.

## ActiveMQ / JMS — base-data merge + fan-out channel

Producer-side queue declarations live in
`xlyErpJmsProductor/src/main/java/com/xly/xlyerpjmsproductor/config/P2pQueue.java`.
The full set is **24 destinations**, grouped by intent:

### Module / control (2)

| Constant | Purpose |
|---|---|
| `ERP_JMS_ACTIVEMQ_CHANGE_GDS_MODULE` | "Module metadata changed" — `ConsumerChangeGdsModuleThread` runs the stored proc `PRO_ERPMERGEBASEGDSMODULE` to merge per-tenant `gdsmodule` rows into a flattened base lookup table. **Does not invalidate Redis caches** despite the name — Redis cache eviction happens synchronously via `@CacheEvict` in BACK during save. See [Cache invalidation on metadata change](../reference/maintainer/cache-invalidation.md). |
| `ERP_JMS_ACTIVEMQ_CHANGE_WORK_ORDER_CONTROL` | Work-order control state changed (status/aggregate flags) — fan-out for downstream recalculation. |

### Document operations (6)

| Constant | Purpose |
|---|---|
| `ERP_JMS_ACTIVEMQ_UPD_SALE_ORDER` / `_UPD_WORK_ORDER` / `_UPD_PRODUCTION_REPORT` | "Document was updated" notifications consumed by background workers (totals recalculation, downstream invalidations). |
| `ERP_JMS_ACTIVEMQ_DEL_SALE_ORDER` / `_DEL_WORK_ORDER` / `_DEL_PRODUCTION_REPORT` | Document-delete notifications. |

### Element-master change fan-out (7) — `CHANGE_ELE_*`

| Constant | Purpose |
|---|---|
| `_CHANGE_ELE_CUSTOMER` | Customer-master change. |
| `_CHANGE_ELE_EMPLOYEE` | Employee-master change. |
| `_CHANGE_ELE_MACHINE` | Shop-floor machine-master change. |
| `_CHANGE_ELE_MATERIALS` | Materials-master change. |
| `_CHANGE_ELE_PRODUCT` | Product-master change. |
| `_CHANGE_ELE_PROCESS` | Process-master change. |
| `_CHANGE_ELE_TEAM` | Team-master change. |

### System-info / lookup-table change fan-out (9) — `CHANGE_SIS_*`

| Constant | Purpose |
|---|---|
| `_CHANGE_SIS_CUSTOMER_CLASSIFY` | Customer-classification change. |
| `_CHANGE_SIS_DELIVER` | Delivery-method change. |
| `_CHANGE_SIS_FORMULA` | Calculation-formula change. |
| `_CHANGE_SIS_PAYMENT` | Payment-method change. |
| `_CHANGE_SIS_PROCESS_CLASSIFY` | Process-classification change. |
| `_CHANGE_SIS_PRODUCT_CLASSIFY` | Product-classification change. |
| `_CHANGE_SIS_SALES_MAN` | Sales-personnel change. |
| `_CHANGE_SIS_TAX` | Tax-rate change. |
| `_CHANGE_SIS_WORK_CENTER` | Work-centre change. |

(Constant prefixes elided to `_…` after the first table — the full literal is `ERP_JMS_ACTIVEMQ_…`.)

### Listener side

`xlyErpJmsConsumer/.../consumer/Consumer.java` is a single class that
hosts **all 24 `@JmsListener` methods** — one per destination. Each
method dispatches the payload to a corresponding `Consumer*Thread`
class under `xlyErpJmsConsumer/.../thread/`, which executes the
domain-specific work (typically calling a `PRO_ERPMERGEBASE*` stored
proc that consolidates per-tenant rows into a flattened base lookup
table) asynchronously. There is *one* listener class with 24 methods,
*not* 24 listener classes. None of the consumer threads invoke
`@CacheEvict` or `cleanRedis*` — Redis cache invalidation is
synchronous in BACK during save, see [cache-invalidation.md](../reference/maintainer/cache-invalidation.md).

## RocketMQ — other flows

`RocketMQServiceImpl` and its companion `RocketMQService` interface
sit in `xlyBusinessService/src/main/java/com/xly/service/`. They cover
non-cache-invalidation flows (for example, `MQCompensateServiceImpl`
handles compensation/retry semantics that ActiveMQ's destinations don't
express well). RocketMQ topics are configured per environment.

## Manual cache-invalidation poke

If a metadata change happens via raw SQL, the cache across nodes will not
bust automatically because no BACK save path calls `@CacheEvict`. The
supported override is to invoke the appropriate
`BusinessCleanRedisDataImpl.delCleanRedisDataByTableName(...)` cleaner
from inside the application once; it evicts the shared Redis-backed Spring
cache directly. See
[cache invalidation on metadata change](../reference/maintainer/cache-invalidation.md)
for the broader troubleshooting path.

## What this is *not*

- **Not a public integration channel.** External integrators do not
  publish or subscribe to these brokers. They are *internal* fan-out
  for the cluster.
- **Not a cache-invalidation channel.** The HTTP write paths in
  `xlyEntry` already evict Redis synchronously when they should; the
  manual cleaner call is for raw-SQL or bypass paths.
