The two interfaces looks cleaner indeed plus we could remove the callback structure on the responder side but it could be a bit confusing for developers to know the one to use I guess.
This approach is actually used in Spring for RMI and the implementation of JAX-RPC, two somewhat deprecated technologies.
The recommended technologies by Spring (JAX-RS, JMS, Burlap, Hessian, ...) use the following runtime exception not declared in a single business interface defined by the user:
A client object simply receives an implementation for the interface that
it needs via a bean reference, like it does for a local bean as well.
A client may catch RemoteAccessException if it wants to, but as
remote access errors are typically unrecoverable, it will probably let
such exceptions propagate to a higher level that handles them generically.
In this case, the client code doesn't show any signs of being involved in
remote access, as there aren't any remoting-specific dependencies.
They insist on the unrecoverable error aspect of such remoting interfaces and they are known to be advocates of RuntimeException especially through their famous template pattern.
I guess these remoting facilities are more targeted towards J2EE applications rather than highly available distributed applications where you expect things to fail and need explicit control.
The two interfaces available might actually be the best of both worlds, I am wondering if we could use both of them on the client side with the same *Requestor code.
That is by introspecting the interface's method exceptions in the InvocationHandler we can choose whether to throw an AvroRemoteException if declared or else an AvroRuntimeException.
We can even choose at generation if the user want to have both interfaces or else just the "business" one or else the current one for backward compatibility (might be confusing again if not properly documented).
The performance impact should be negligible especially if the behaviour is cached per method.
Today using ReflectResponder will wrap a transport error into an AvroRemoteException except if it is a runtime or a declared exception.
This means that if somebody uses an interface without AvroRemoteException declared in the methods, the only way to recover is to catch Exception and check it is an AvroRemoteException because catching it directly AvroRemoteException won't compile.