ServiceStack way of communicating between services

Recently I have been trying to learn a little bit about software architecture and one concept that pops up a lot is that each service should not reach beyond it’s own boundry and just be responsible for it’s own data. If something happens in one service that means another service has to do something then service A should raise an event in a broker that service B subscribes to. My understanding is that this keeps the services clean and easier to refactor if you have to scale an individual service.

What would be the ServiceStack way to approach this? It seems like it would be with SSE but I am unsure exactly on the implementation. Are there any real world examples?

Let’s says I have 3 http service endpoints:

/Payment
/Warehouse
/Notification

When a payment is received we need to create a work item in the warehouse and send out some notifications.

So I could create a message with the relevant data.

public class PaymentReceived
{
    public string PaymentId { get; set; }
	public string[] WarehouseItems { get; set; }
	public string[] Emails { get; set; }
}

And then in the /Payment endpoint send that message to a channel

ServerEvents.NotifyChannel("payment-received", paymentReceived);

But now I am a bit unsure of how the subscription works. /warehouse and /notification are scoped to a HTTP request so I need a background service that listens to the payment-received channel and then generates calls to http endpoints and handles exceptions/retries. I am not sure how best to structure this in the project or if I am totally off base here and there is a better/easier way to handle service boundaries in servicestack. I am just trying to have some sort of broker in-between services.

What do you recommend for this scenario?

Is there a reason why you’re not just calling the APIs directly? e.g. using Service Client or Service Gateway?

Otherwise you can use the C# Server Events Client which maintains an SSE connection with the remote server.

I keep seeing people say that there should be a broker in between services so they are not tightly coupled. I was just wondering the best way to follow that pattern with SS.

I mean the Service Gateway is decoupled, the application sending it only sends a populated Request DTO message which is up to the Gateway to decide where to send it, which can also adopt a Service Discovery solution which dynamically maintains an active list of available registered endpoints.

OK I think I see. So if the payment service uses the client to call say CreateNotification request then it will call the local service and if I need to move that service elsewhere then I setup service discovery and it will work exactly the same because the client will know where to route that request.

Seems like I was trying to add something in that was already baked in from the start. This is why I use SS :slight_smile:

one thing that does stand out to me though in this scenario is that Service A has to know about Service B. If I wanted to follow a pattern where Service A raised an event where Service B/C/D could subscribe to, what would be the recommended way?

How you want to register and resolve Services would be up to your Custom Service Gateway implementation, the simplest solution would be for it to maintain a dictionary or switch mapping Request DTOs to API Clients which would avoid needing any infrastructure dependencies, a more complex solution would be to adopt a Service Discovery solution as others have done.

If you want a Pub/Sub solution you can use Server Events or Redis Pub/Sub which can provide a pub/sub connectivity for broadcasting custom events.

This brings me back to my OP where I am sending an event to a channel but I don’t know how to subscribe my service to listen for that event. Is there any example or part of docs I am missing?

Sending an event to what channel? Sending events to channels implies you want a Pub/Sub solution? which you can use Redis Pub/Sub and have all RedisPubSubServer clients connect to the Redis Broker, if you use Server Events client each Service will need a connection to each other or you can use Redis Server Events and have all Server Events Clients connect to the same Redis (backplane) Server.

I’d personally go for the simplest solution and have a custom Server Events Client which routes Request DTOs to the appropriate Service Client.

Or you can look into other Pub/Sub solutions used in .NET which may be more appropriate for your use case.

Yes, I want pub/sub. In my first post you can see the channel.

ServerEvents.NotifyChannel("payment-received", paymentReceived);

How do I trigger code on the backend when this message is generated?

This publishes a Server Events message to all Server Event Client subscribers, so each Service would need a C# Server Events Client that subscribes to the Server configured with the ServerEventsFeature (i.e. that publishes the ServerEvents.* messages).

Yeah this is where I get confused. The service is an HTTP endpoint so how does it listen for messages with a client.

I don’t understand, you use a Server Events Client to listen for messages from a Server Events Server.

I understand the client receives messages from the server. I don’t know where to add the client in my project.

I start by registering server events:

Plugins.Add(new ServerEventsFeature());

And in my payments endpoint I generate a message:

public class PaymentService : Service
{
    public IServerEvents ServerEvents { get; set; }
    
    public bool Post(CreatePayment req)
    {
        var paymentId = Guid.NewGuid().ToString();
        
        var paymentReceived = new PaymentReceived
        {
            PaymentId = paymentId,
            WarehouseItems = new string[] {"something", "else"},
            Emails = new string[] {"joe@bloggs.com", "sales@something.com"}
        };
        
        ServerEvents.NotifyChannel("payment-received", paymentReceived);

        return true;
    }
}

I have a notification endpoint

public class NotificationService : Service
{
    public bool Post(SendNotification req)
    {
        foreach (var email in req.Emails)
        {
            //send message
        }

        return true;
    }
}

I know I need to read the PaymentReceived message with the client and then generate a SendNotification request but I don’t know where to add the client to the ServiceStack project and the code to handle the message and generate all necessary requests.

I guess I am not explaining what I am trying to do well enough but not sure how else to put it.

Like any other client your project uses, e.g. in your AppHost or a plugin.

Here’s an SSE Client example which calls an existing Service after receiving a message from the server:

var sseClient = new ServerEventsClient(baseUrl)
{
    OnMessage = (message) =>
    {
        var gateway = HostContext.AppHost.GetServiceGateway();
        // inspect message to find out which Service to call...
        gateway.Send(new SendNotification { });
    },            
};
container.Register(sseClient);

That’s a basic message handler example but you’d want to read the docs for better ways to handle different messages.

1 Like