-
Notifications
You must be signed in to change notification settings - Fork 3
Open
Description
Summary
The Bus struct has 30+ public methods with inconsistent naming and overlapping functionality. This creates a confusing API surface that's hard to learn and use correctly.
Problems
1. Method Naming Inconsistencies
Send variants:
sendsend_extsend_blockingsend_oneforce_sendsend_boxedsend_with_response
Flush variants:
flushflush_allflush2flush_and_sync
Issues:
- Not clear when to use which variant
- Naming doesn't follow consistent pattern
flush2is particularly unclear
2. Too Many Methods
30+ methods on a single struct makes the API:
- Hard to discover the right method
- Difficult to document effectively
- Confusing for new users
- Large surface area for bugs
3. Overlapping Functionality
Some methods seem to do very similar things:
sendvssend_ext- What's the difference?flushvsflush_all- When to use each?send_onevssend- Not immediately obvious
Recommended Improvements
1. Use Builder Pattern for Send Options
Instead of many send variants:
// Before
bus.send(msg).await?;
bus.send_ext(msg, addr).await?;
bus.send_blocking(msg)?;
bus.send_one(msg, id).await?;
// After
bus.send(msg).await?;
bus.send(msg).to_address(addr).await?;
bus.send(msg).blocking()?;
bus.send(msg).to_receiver(id).await?;2. Consolidate Flush Methods
Replace multiple flush methods with one configurable method:
// Before
bus.flush().await;
bus.flush_all().await;
bus.flush2(policy).await;
// After
bus.flush().await; // Default flush
bus.flush().all().await; // Flush all
bus.flush().with_policy(policy).await; // Custom policy3. Group Related Operations
Consider splitting into smaller, focused traits:
trait MessageSender {
fn send<M: Message>(&self, msg: M) -> SendFuture<M>;
}
trait BusControl {
fn flush(&self) -> FlushFuture;
fn close(&self) -> CloseFuture;
}
trait RequestResponse {
fn request<M: Message>(&self, msg: M) -> ResponseFuture<M::Response>;
}
impl MessageSender for Bus { /* ... */ }
impl BusControl for Bus { /* ... */ }
impl RequestResponse for Bus { /* ... */ }4. Use Enums Instead of Multiple Methods
For addressing modes:
pub enum SendMode {
Broadcast,
ToAddress(Address),
ToReceiver(ReceiverId),
Random,
Balanced,
}
// Single method instead of 5 variants
bus.send_with_mode(msg, SendMode::ToReceiver(id)).await?;5. Improve Documentation
Whichever approach is chosen, document:
- When to use each method
- Performance implications
- Ordering guarantees
- Error conditions
Example Refactored API
Current (Simplified)
impl Bus {
pub async fn send<M>(&self, msg: M) -> Result<(), Error>;
pub async fn send_ext<M>(&self, msg: M, addr: Address) -> Result<(), Error>;
pub fn send_blocking<M>(&self, msg: M) -> Result<(), Error>;
pub async fn send_one<M>(&self, msg: M, id: ReceiverId) -> Result<(), Error>;
pub async fn force_send<M>(&self, msg: M) -> Result<(), Error>;
// ... 25 more methods
}Proposed
impl Bus {
/// Send a message (returns a builder for configuration)
pub fn send<M: Message>(&self, msg: M) -> SendBuilder<M>;
/// Flush pending messages
pub fn flush(&self) -> FlushBuilder;
/// Close the bus
pub async fn close(self);
/// Request-response pattern
pub fn request<M: Message>(&self, msg: M) -> RequestBuilder<M>;
}
pub struct SendBuilder<M> {
// Configure how the message is sent
}
impl<M> SendBuilder<M> {
pub async fn await(self) -> Result<(), Error>; // Default send
pub fn to_receiver(self, id: ReceiverId) -> Self;
pub fn to_address(self, addr: Address) -> Self;
pub fn balanced(self) -> Self;
pub fn random(self) -> Self;
pub fn blocking(self) -> Result<(), Error>;
}Migration Strategy
This is a breaking change, so:
-
Deprecation period
- Mark old methods as
#[deprecated] - Add new methods alongside
- Provide migration guide
- Mark old methods as
-
Semver major version bump
- Communicate changes clearly
- Update all examples
- Update documentation
-
Gradual migration
- Can keep both APIs during transition
- Remove deprecated methods in next major version
Benefits
- Clearer API: Obvious what each method does
- Better discoverability: IDE autocomplete shows fewer options
- More flexible: Builder pattern allows adding options without new methods
- Better documentation: Less to document, clearer organization
- Fewer breaking changes: Adding options doesn't require new methods
Priority
Low-Medium - This is a nice-to-have improvement for API ergonomics but represents a breaking change. Best done:
- Before 1.0 release
- During a planned major version bump
- When making other API changes
Related Issues
- Issue Add API documentation for public interfaces #11: Better API docs would help current API
- Consider this refactor when adding new features
Discussion Points
- Is backwards compatibility important?
- When is a good time for breaking changes?
- Which approach is preferred (builder, enums, traits)?
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels