Message Roles (CQRS-style organisation)
Postboy itself does not enforce any naming or structural rules — it only understands message types and contracts.
However, the author recommends organising messages according to their role in the system, following a CQRS-inspired separation.
This document describes four clear roles:
Query – a request for data (always returns a result)
Command – an instruction to perform an action (may or may not return a result)
Event – a notification that something happened (never returns a result)
Executor – a synchronous operation, often used for mapping or instant transformation
All roles share the same Postboy infrastructure (ConnectMessage, exec, sub, fire, fireCallback) and differ only in intent and, optionally, naming conventions.
Why separate by role?
Readability: a developer can instantly understand what a message does by looking at its name and location
Architecture clarity: the codebase makes the distinction between queries, side‑effecting commands, and domain events visible at the file system level
Predictable communication: callers know whether to expect a result, and handlers understand their responsibility
Easier refactoring: changing a command’s side effects does not affect queries, and vice versa
The four roles
1. Query
A query is a read request. It always returns data and never changes system state.
Extends
PostboyCallbackMessage<T>Always calls
msg.finish(...)with a resultNamed with the suffix
Query
Example — loading a user
Handler:
Caller:
2. Command
A command instructs the system to do something. It may return a result (e.g., the ID of a created entity) or nothing at all.
Extends
PostboyCallbackMessage<T>(if returns something) orPostboyGenericMessage(if not)If there is a useful return value, it is returned via
msg.finish(...); otherwise there is nothing to do for a subscriberNamed with the suffix
Command
Example A — command that returns a value
Handler:
Example B — command without a meaningful result
Handler:
Caller:
3. Event
An event is a pure notification. Something happened; anyone interested can react. Events never return a result and do not expect a single handler.
Extends
PostboyGenericMessagePublished with
fire; subscribers usesubNamed with the suffix
Event
Example
Registration and publishing:
Subscriber (e.g., notification service):
4. Executor
An executor is a synchronous operation that transforms or computes something immediately.
It is a command-like message that always runs synchronously and is typically used for mapping, formatting, or simple lookups.
Extends
PostboyExecutor<T>Its handler is always synchronous
Named with the suffix
Executor
Example — product model to view‑model mapping
Handler (synchronous):
Caller:
Naming conventions
The suggested naming pattern is:
Role | Base class | Name suffix | Returns |
|---|---|---|---|
Query |
|
| Always |
Command |
|
| Optional |
Event |
| Never | |
Executor |
| Usually |
Directory structure
The recommended layout is:
This grouping immediately tells a developer where to look when they need to send a request, publish an event, or understand what side effects a command has.
Important note
Postboy does not require these conventions. You can name your classes however you like, put them anywhere, and mix roles arbitrarily.
The suggestions above are an author’s recommended practice that makes large‑scale event‑driven codebases easier to navigate and maintain.