Patterns

Everything the library does, and how much of it application.yml can reach.

The starter is a thin layer: it builds beans from properties and hands you the library's own AceMq. So every pattern below is available to a Boot application — but they divide into three groups, and knowing which group you are in saves a search for a property that does not exist.

The three groups

Properties. Configured entirely from application.yml, with no Java beyond a handler.

Pattern Where
Consume a queue @AceListener
Topology: exchanges, queues, bindings, drift Topology
Retries — the non-blocking ladder Retries
Dead letters, .dlq and .parked Dead letters
TLS, credentials, development certificates Security
Serialization format Formats
Health, metrics Observability
Graceful shutdown Shutdown
Publisher confirms, back pressure, blocked connections Publishing

A bean that replaces an auto-configured one. Change the behaviour of everything on the connection at once. Each of these backs off with @ConditionalOnMissingBean, so declaring yours is the whole mechanism.

Pattern The bean to declare
Payload encryption Codec
Claim check Codec
Avro with a schema registry Codec
Tracing Telemetry
Rotating credentials ConnectionConfig

A bean of its own. The pattern is a library object the starter has no opinion about. Declare it, take AceMq as a parameter, and give it destroyMethod = "close" if it holds anything.

Pattern The type
Publishing Publisher<T>
Idempotent consumer IdempotencyStore + a ConsumerGroup
Replay Replay, per call
Request and reply Requester, Responder
Scheduling Scheduler
Transactional outbox OutboxStore + OutboxRelay
Saga Saga<T>, built where the steps are
Pipelines Pipeline<T>
Ordered per key OrderedQueue<T>
Routing slips Itinerary, per message
Streams StreamConsumer
Interceptors PublishInterceptor, ConsumeInterceptor

Four things the annotation cannot do

Worth stating plainly, because the natural first move is to look for an attribute.

@AceListener cannot take an idempotency store. The registry builds ConsumerOptions from properties, and there is no property for a store because a store is a bean. Declare the consumer as a ConsumerGroup bean instead — Idempotency has it.

@AceListener cannot have its own retry ladder or codec. Same reason, same answer. The ladder in acemq.listener.retry.* applies to every annotated listener in the application.

@AceListener cannot read a stream. A stream reader has to say where in the log to start, and that answer usually comes from a database. Streams explains why an annotation would be worse than a bean here.

There is no return value that acknowledges. The library has an AckAwareHandler that returns an Ack, and nothing in the library dispatches to it yet. An annotation wired to an unused code path would be a starter growing a feature its own library does not have.

Choosing between them

A few of these solve overlapping problems, and the overlap is where time gets lost.

Saga or pipeline? A saga is one process running several steps in memory, with compensations; a pipeline is several queues with a consumer on each. Use a saga when the steps must be undone on failure and the whole thing is short enough to hold open. Use a pipeline when the steps are long, need to scale independently, or must survive a restart partway through.

Pipeline or routing slip? A pipeline's route is fixed at build time and typed by the compiler. A slip's route travels in the message and varies per message. Prefer the pipeline; it is the one where a wrong step is a compile error.

Ordered queue or concurrency 1? concurrency = 1 on a listener gives total order across every message on the queue, and a throughput of one handler. An ordered queue gives order per key and parallelism across keys. Total order is almost never the requirement.

Outbox or just publish? If the publish and a database write must both happen or neither, the outbox. If the publish is the only side effect, publish directly — the outbox adds a table, a relay and a polling interval, and paying that for a message with nothing to be consistent with is cost without a benefit.

Stream or queue? A queue distributes work and forgets. A stream is a log that several readers each read in full and can re-read. Streams has the table.

The library's own examples

Each of these patterns has a runnable, single-file example in acemq-java-amqp-examples, against a real broker and without Spring. When a page here shows a pattern wired into a context, that repository shows the same pattern working on its own — which is the shorter thing to read when the question is what the library does rather than how the starter reaches it.