Skip to content

feat(v3): per-message send functions for multi-message operations (#140) - #332

Merged
lerenn merged 1 commit into
mainfrom
feat/140-multi-message-send
Jun 12, 2026
Merged

lerenn merged 1 commit into
mainfrom
feat/140-multi-message-send

Conversation

@lerenn

@lerenn lerenn commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Issue

Addresses #140 — operations carrying more than one message only ever used the first one (op.Messages[0] // TODO: change); the others were silently discarded.

Decision

Per the maintainer's direction: per-message methods (additive), and the send side first (the receive side needs a separate design — see below).

Change

When an operation (or its channel) declares more than one message, one send function is generated per message, named <SendFunc>For<Message>:

func (c *AppController) SendAsSendEventsOperationForUserCreated(ctx, msg UserCreatedMessage) error
func (c *AppController) SendAsSendEventsOperationForUserDeleted(ctx, msg UserDeletedMessage) error

Single-message operations are unchanged — go generate ./... produces no diff outside the new test.

Also fixes a latent bug: Channel.GetMessage ranged a map (non-deterministic), which for multi-message channels produced unstable and even non-compiling generated code (the callback type and the brokerMessageTo… type could resolve to different messages). It now returns the lowest-named message deterministically.

Scope / follow-up

The receive side is intentionally not included. The chosen per-message design (SubscribeToXForA / SubscribeToXForB) collides with the controller's address-keyed subscription map (c.subscriptions[addr] + an "already subscribed" guard), so supporting it needs a rework of subscription management plus the agreed try-each-until-valid discrimination. The subscribe side continues to handle the first message, so this PR is "Addresses" rather than "Fixes" #140.

Test

test/v3/issues/140 sends both messages through a capturing mock broker and asserts each per-message function publishes its own payload on the channel. It fails to compile before the change (the functions don't exist) and passes after.

🤖 Generated with Claude Code

…ations (#140)

Operations carrying more than one message only exposed the first one.
Now, when an operation (or its channel) has several messages, one send
function is generated per message (SendAs<Op>For<Message>), each sending
its own message type. Single-message operations are unchanged
(byte-identical output).

Also makes Channel.GetMessage deterministic (it ranged a map, which could
produce unstable / non-compiling generated code for channels with several
messages).

Reception of multiple message types on a channel is left for a follow-up
(it requires reworking the address-keyed subscription map); the subscribe
side still handles the first message.

Addresses #140

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lerenn
lerenn merged commit a19ea48 into main Jun 12, 2026
5 checks passed
@lerenn
lerenn deleted the feat/140-multi-message-send branch June 12, 2026 16:37
lerenn added a commit that referenced this pull request Jun 14, 2026
)

Follow-up to #140/#332, which shipped the send side. When a receive
operation (or its channel) declares more than one message, generate one
subscribe function per message instead of silently handling only the
first one:

- SubscribeToXForUserCreated / SubscribeToXForUserDeleted, with one
  callback per message type, plus matching UnsubscribeFromXFor... and
  per-message methods on the subscriber interface. Single-message
  operations are unchanged (byte-identical output).
- A single broker subscription is opened per channel and multiplexed to
  the registered per-message handlers, so several per-message
  subscriptions on one channel no longer collide.
- Received messages are discriminated with a try-each-until-valid
  strategy (unmarshal + validate against each candidate schema using
  go-playground/validator) and dispatched to the first match.
- A message matching none of the expected types is nacked and surfaced
  through the error handler as the new extensions.ErrNoMatchingMessage.

Adds a broker-backed regression test (test/v3/issues/333) covering
dispatch of two distinct message types on one channel, the no-match
path, and per-message unsubscribe. Documents the behavior and the
validator dependency in the README.

Co-authored-by: Louis FRADIN <louis.fradin@pm.me>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant