Skip to content

bluetape4k-dependencies 2.0.0 in Practice Part 3: Ownership in Messaging and Streams

A 3D miniature workshop where a central BOM board branches into compatibility, data, messaging, and operational-safety stations
Delivery, business success, acknowledgement, and checkpoints are separate events with separate owners.

A message reaching a handler does not mean business work finished. A process may stop after a database write but before ack, or after emitting a stream record but before persisting its checkpoint. A redelivering system must treat those gaps as normal paths.

bluetape4k-dependencies 2.0.0 selects bluetape4k-projects 2.0.0 and bluetape4k-aws 1.0.0 together. Their messaging APIs keep at-least-once redelivery and caller-owned idempotency visible.

Receipt and Business Success Are Different Events

Section titled “Receipt and Business Success Are Different Events”

The smallest safe ordering looks like this:

flow.collect { message ->
try {
processIdempotently(message) // Complete the business side effect first.
message.ack() // Then advance the consumer position.
} catch (retryable: RetryableFailure) {
message.nak()
} catch (permanent: PermanentFailure) {
message.term()
}
}

Depending on the transaction and broker, the application may need an inbox/outbox or idempotency key. The adapter does not make the business side effect and broker acknowledgement one atomic transaction.

Projects 2.0.0 adds cold Flow<Message> adapters that create a JetStream ConsumerContext pull consumer or JetStream push subscription per collection. They validate Flow capacity and NATS pending limits. On cancellation, the adapter closes only the JetStreamSubscription or IterableConsumer it created; the caller still owns the connection and stream configuration.

Acknowledgement is manual. Call ack() after business success, nak() for retryable failures, and term() when policy says the message must not be retried. A pending-queue drop becomes NatsConsumerFlowException, not successful completion.

DynamoDB Streams follows parent-before-child shard traversal. Resume can replay the saved sequence inclusively, so business processing must be idempotent. This adapter does not have the distributed lease contract used by the Kinesis runtime.

Kinesis consumerFlow combines shard discovery, parent ordering, bounded concurrency, leases, and fencing. Emitting a record does not save its checkpoint. The collector must return successfully first. Lease loss stops new emissions and checkpoint work.

AdapterOrderingProgress stateCaller responsibility
DynamoDB Streamsparent before childinclusive checkpoint replayidempotency and checkpoint-store lifetime
Kinesisshard graph and parent orderingcheckpoint after lease/fencingidempotency and lease/checkpoint-store configuration

Long-running work may exceed the visibility timeout. A visibility heartbeat extends the message’s invisible window but does not decide whether business work succeeded. Batch listeners acknowledge successful entries and leave failed entries for redelivery. FIFO queues add message-group ordering and backpressure to that boundary.

The Extended Client offloads large payloads to S3 and puts a signed pointer in the SQS body. The application provisions the bucket, IAM, encryption, retention, lifecycle, retries, and orphan cleanup. The pointer belongs to this module and is not automatically interoperable with the AWS Java Extended Client.

The SNS HTTP adapter parses structure, applies an exact TopicArn allowlist before network access, retrieves a certificate from an allowed URL, and verifies Signature v1/v2. Only verified messages reach the handler. Subscription confirmation is an explicit side effect: the handler must call NotificationStatus.confirmSubscription().

The SNS signature visual companion plays this gate in order.

Spring Modulith integration has two opt-in directions:

  • Producer externalization sends application events to SNS or SQS.
  • Consumer ingress validates DIRECT bodies or SNS-wrapped SQS bodies before application dispatch.

Enabling one direction does not configure the other. See the Modulith issue and event externalization visual companion.

BoundaryAdapter ownsCaller owns
NATS Flowcold bridge, bounded channel, created-handle cleanupconnection, business success, ack policy
AWS Streamsshard traversal, emission order, defined checkpoint hookidempotency, durable stores, operations
SQS listenerpolling, heartbeat, partial-ack protocolhandler transaction, retry/DLQ, payload storage policy
SNS HTTPallowlist and signature gateallowed topics, confirmation, business side effects
Modulithselected outbound or inbound adapterevent contract, version, idempotency, opposite direction

Adapters close the handles they create. Applications retain ownership of clients, credentials, IAM, queues/topics, durable stores, and business transactions.

Comments

Leave a note or reaction with your GitHub account.