I thought a bit about a service based architecture around vibe's async model. A few points were already discussed here: the sketched implementation of a service, where for each client connection a different handler object was created and the service skeleton from David where a single handler object was used.
Now I would like to sketch a service where the handler is a singleton (not quite finished and not integrated in the thrift server model). This would use a proxy/stub model in order to guarantee that no client request is handled during the processing of another request.
The vision would be to have different proxy/stubs that would allow simple implementation and invocation of services, like simple method calls, be they on a separate fiber, separate thread or a different process/machine. So a service method call would yield the current fiber and, if the method is not an one-way, will wait for the response. This waiting for the response must be done with a priorityReceive, that whould check only the priority message queue. Thus the client requests are serialized but responses from subsequent service call responses can be received. The tricky part would be to avoid dead-locks (maybe through a global service registry to trace the call graph). Also, the excesive copying of the arguments by message sending should be avoided someway.

So, back to the skeleton for the same process, one fiber for the service handler, multiple fibers for the clients.

Having this service definition (generated from the sample thrift idl):

enum Operation {
  Add = 1,
  Substract = 2,
  Multiply = 3,
  Divide = 4
}

struct Work {
  int x;
  int y;
  Operation op;
}

interface Calculator {
  int calculate(ref const(Work) w);

  enum methodMeta = [
    TMethodMeta(`calculate`, 
      [TParamMeta(`w`, 1)]
    )
  ];
}

would generate such a proxy class, for clarity the template parameter is replaced:

class ServiceProxy(Calculator) : Calculator
  Tid _tid; // The task id where the actor is running
  this(Tid tid) { _tid = tid; }

  /* Send a request to the stub */
  auto sendRequest(alias Method, Args...)(Args args) 
  {
    send!(Method.RequestMessage, Tid, Method.requestParamTypes)(_tid, Method.RequestMessage(), Task.getThis(), args);
    static if(!is(Method.RetType == void)) 
    {
      // Wait for the response
      // TODO: use priorityReceive() to get only from the priority message queue. 
      //       on the normal queue may come other client requests.
      Method.RetType retValue;
      receive(
          (Method.ResponseMessage, Method.RetType r) { retValue = r; },
          (Method.ResponseMessage, shared(Exception) exc) { throw exc; }
      );
      return retValue;
    }
  }

  /* 
    Generated methods, implementing the interface
    mixin(ServiceProxyStub!(Iface).proxyCode)
  */
  int calculate(ref const(Work) w)
  {
    return  sendRequest!(MethodInfo!("calculate", calculate))(w);
  }
  
}

The stub part that runs in its own task/fiber, here again with the template parameter replaced with the concrete type and including the generated code.

class ServiceStub(Calculator) : WhiteHole!Calculator
{
static:
  /* The stub task/fiber function of the service */
  public void serviceStubFunc(Calculator handler)
  {
    receive(
      /*
        Generated code 
        mixin(ServiceProxyStub!(Iface).stubCode)
      */
      stubDelegateRequestResponse!(MethodInfo!("calculate", calculate), typeof(&handler.calculate))(&handler.calculate) 
    );
  }

  /* Stub delegate to handle a request and send back the result.
    It will be used with concurrency.receive in the stub fiber function. 
    The Handler is the delegate of the service handler object. 
    It may have slightly different arguments than those in Args..., 
    because we removed any type qualifiers for sending/receiving. */
  private auto stubDelegateRequestResponse(alias Method, Handler)(Handler handler)
  {
    return (Method.RequestMessage, Tid proxyTid, Method.requestParamTypes args) 
    { 
      try 
      {
        // Invoke the handler and send back the result to the proxy
        prioritySend(proxyTid, Method.ResponseMessage(), handler(args));
      } 
      catch(shared(Exception) e) 
      {
        // Send the exception back to the proxy
        prioritySend(proxyTid, Method.ResponseMessage(), e);
      }
    };
  }

  /* Stub delegate to handle an one way request.
    It will be used with concurrency.receive in the stub fiber function. 
    The Handler is the delegate of the service handler object. 
    It may have slightly different arguments than those in Args..., 
    because we removed any type qualifiers for sending/receiving. */
  private auto stubDelegateOneWayRequest(alias Method, Handler)(Handler handler)
  {
    return (Method.RequestMessage, Tid proxyTid, Method.requestParamTypes args) 
    { 
      try 
      {
        handler(args);
      } 
      catch(shared(Exception) e) 
      {
        // TODO: log
      }
    };
  }
}

Client code would be like this (must be from a task/fiber in order to receive the response):

auto calc = runService!CalculatorHandler();
Work w;
w.x = 40;
w.y = 2;
w.op = Operation.Add;
int result = calc.calculate(w);
writeln(result);

What are your thoughts about such a method call dispatching using vibe's concurrency (priority)send/receive?