Caller and Receiver
In traditional distributed systems, communication is usually described in terms of clients and servers.
One side sends requests.
The other side responds.
This model works, but it also introduces assumptions that are not always helpful—especially once systems become more dynamic, stateful, or symmetric.
Graftcode uses a different mental model: caller and receiver.
Why not client and server?
The client–server distinction is rooted in networking, not in programming.
It implies:
- fixed roles
- directional dependency
- a request–response mindset
In practice, modern systems rarely fit this cleanly:
- services call each other
- roles switch depending on context
- communication becomes bidirectional
Graftcode avoids this framing and instead describes communication in terms of who is calling and who is receiving at a given moment.
These roles are temporary, not structural.
The caller
A caller is any piece of code that invokes a method exposed by a public interface.
From the caller’s perspective:
- it holds a Graft
- it calls methods on it
- it receives return values or exceptions
The caller does not know:
- where the code runs
- how the call is transported
- whether the receiver is local, remote, or in memory
It simply calls code.
The receiver
A receiver is the runtime that hosts the actual implementation of the public interface.
From the receiver’s perspective:
- it exposes selected classes and methods
- it executes business logic
- it returns results or throws exceptions
The receiver does not know:
- who the caller is
- which language the caller uses
- whether the call originated locally or remotely
It simply runs code.
A symmetric relationship
The important part is that caller and receiver are symmetric roles.
The same application can:
- be a caller in one interaction
- be a receiver in another
- sometimes be both at the same time
There is no permanent “client” or “server” identity.
This symmetry becomes especially important when:
- services call each other in cycles
- systems are composed of many small modules
- responsibilities shift over time
Calls, not messages
In the caller–receiver model, interaction is expressed as method calls, not messages.
This has several consequences:
- arguments are passed as values
- results are returned directly
- errors are raised as exceptions
The interaction follows the same rules developers expect from local code.
The runtime is responsible for translating this interaction into an appropriate execution strategy.
State and lifecycle
The caller–receiver model also makes state explicit.
If the public interface:
- exposes only static methods
→ calls are stateless and independent
If the public interface:
- exposes objects
- maintains subscriptions
- reacts to events
→ calls are stateful and tied to a lifecycle
The model does not hide state—it makes it visible in code.
This allows developers to reason about behavior before thinking about deployment or transport.
Bidirectional and duplex communication
Because roles are not fixed, communication does not have to be one-way.
A receiver can:
- call back into the caller
- emit events
- participate in long-lived, duplex interactions
From the programming perspective, this still looks like calling methods and handling events—not managing connections or sessions explicitly.
Why this matters
By replacing client–server thinking with caller–receiver thinking, Graftcode:
- removes protocol-driven assumptions
- aligns communication with programming concepts
- supports richer interaction patterns naturally
It becomes easier to design systems where:
- responsibilities are shared
- components evolve independently
- communication patterns change over time
In short
In Graftcode:
- caller means “the code that invokes”
- receiver means “the code that executes”
- roles are dynamic and symmetric
- communication is expressed as method calls
This shift in perspective prepares the ground for runtime-level integration—where how calls are executed becomes a configuration detail, not a design constraint.
See also: Graftcode Gateway