How transport-agnostic business logic becomes a real architectural advantage — and where teams often let protocol details leak back in.
Your win: explain how the repo reuses business logic across transports, why thin wrappers matter, and what makes shared handlers a design win rather than accidental coupling.
In plain English Plain English: the transport layer should unwrap the message and hand it to business logic, not become the place where the business logic secretly lives.
The problem: one capability may arrive through several protocols
A capability may be triggered by gRPC, Kafka, NATS, or HTTP. If every transport reimplements the logic, behavior drifts. If the business logic is shared cleanly, the transport becomes just the delivery wrapper.
The mental model Protocol wrappers should be thin; reusable handlers or subscribers should hold the business meaning.
Why this matters in this repo
Notification is the strongest example because it is not a gRPC-only service. It has gRPC, Kafka, NATS, cron, and HTTP paths. The architectural question is whether each transport stays a wrapper or becomes a second business layer by accident.
Anchor — where this matters here The notification docs explicitly call out shared subscriber/consumer logic reused across NATS and Kafka, and `system_notification` as the canonical CQRS module. That is exactly the kind of transport-agnostic seam a senior reviewer wants to see.
Common mistake Saying “shared logic across transports” while keeping most of the real behavior buried in protocol-specific handlers or wrappers.
Read this next
Notification architecture notes
The notification CLAUDE doc is the key source because it names the actual shared-handler patterns in the codebase.
→ internal/notification/CLAUDE.md
Check yourself (from memory)
Q1. A good transport adapter should usually be…
Thin wrappers keep business behavior reusable and consistent.
Why is transport-agnostic business logic valuable?
recall, then click to reveal
Because one business capability can be reused across gRPC, Kafka, NATS, or HTTP without reimplementing the rules differently in each wrapper.
Want me to compare a thin transport wrapper and a fat one from the repo’s patterns? Ask me.