https://github.com/coronabytes/servicemesh
.NET service mesh based on nats
https://github.com/coronabytes/servicemesh
csharp nats service-mesh
Last synced: 11 months ago
JSON representation
.NET service mesh based on nats
- Host: GitHub
- URL: https://github.com/coronabytes/servicemesh
- Owner: coronabytes
- License: mit
- Created: 2024-07-28T12:44:13.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2025-06-10T15:32:24.000Z (about 1 year ago)
- Last Synced: 2025-06-10T15:56:53.697Z (about 1 year ago)
- Topics: csharp, nats, service-mesh
- Language: C#
- Homepage:
- Size: 140 KB
- Stars: 8
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://www.nuget.org/packages/Core.ServiceMesh)
[](https://www.nuget.org/packages/Core.ServiceMesh)
# Service Mesh for ASP.NET Core
interconnect (Micro-)services in .NET with ease
## Installation
```sh
# for shared libraries
dotnet add package Core.ServiceMesh.Abstractions
dotnet add package Core.ServiceMesh.SourceGen
# for containers
dotnet add package Core.ServiceMesh
dotnet add package Core.ServiceMesh.SourceGen
```
## Features
- based on https://nats.io
- uses source generators for remote and telemetry proxies
- service request reponse pattern (sync)
- strongly typed clients out of the box
- response streaming with IAsyncEnumerable
- event streaming via NATS JetStream (async)
- durable and transient consumers
- open telemetry support
- supports local service traces in "AutoTrace" mode
## Initialization in ASP.NET Core
```csharp
builder.AddServiceMesh(options =>
{
options.ConfigureNats = opts => opts with
{
Url = "nats://localhost:4222"
};
options.ConfigureStream = (name, config) =>
{
config.MaxAge = TimeSpan.FromDays(1);
};
options.InterfaceMode = ServiceInterfaceMode.Auto;
options.Assemblies = [typeof(ISomeService).Assembly, typeof(SomeService).Assembly];
});
```
## Service Interface
- service interfaces/contracts should be placed in abstraction libraries to be shared among your microservices
- only ValueTask and ValueTask\ and IAsyncEnumerable supported
- open generics with optional constraints are supported
```csharp
[ServiceMesh("someservice")]
public interface ISomeService
{
ValueTask GetSomeString(int a, string b);
ValueTask CreateSomeObject();
ValueTask GenericAdd(T a, T b) where T : INumber;
IAsyncEnumerable StreamResponse();
};
```
## Service Implementation
```csharp
[ServiceMesh]
public class SomeService(ILogger logger) : ISomeService
{
public async ValueTask GetSomeString(int a, string b)
{
await Task.Delay(100);
return b + " " + a;
}
public async ValueTask CreateSomeObject()
{
await Task.Delay(100);
logger.LogInformation(nameof(CreateSomeObject));
}
public async ValueTask GenericAdd(T a, T b) where T : INumber
{
await Task.Delay(100);
return a + b;
}
public async IAsyncEnumerable StreamResponse()
{
await Task.Delay(100);
yield return "a";
await Task.Delay(100);
yield return "b";
await Task.Delay(100);
yield return "c";
}
}
```
## Service Invocation
- inject service interface into your controllers/services
- they are automatically proxied over nats when not available in the same container
```csharp
public class DevController(ISomeService someService) : ControllerBase
{
[HttpPost("add-ints")]
public async Task> CreateIntObject([FromQuery] int a = 3, [FromQuery] int b = 5)
{
return await someService.GenericAdd(a, b);
}
[HttpPost("add-doubles")]
public async Task> CreateDoubleObject([FromQuery] double a = 3.1, [FromQuery] double b = 5.1)
{
return await someService.GenericAdd(a, b);
}
}
```
## Events, Streams and Consumers
- durable consumers need to have a unique name (so you can rename your class later on)
- PublishAsync will await confirmation by nats broker
- SendAsync means Fire and Forget
```csharp
public record SomeCommand(string Name);
[DurableConsumer("SomeCommandHandler", "default")]
public class SomeCommandHandler(ILogger logger) : IConsumer
{
public ValueTask ConsumeAsync(SomeCommand message, CancellationToken token)
{
// do stuff
return ValueTask.CompletedTask;
}
}
public class DevController(IServiceMesh mesh) : ControllerBase
{
[HttpPost("publish")]
public async Task Publish([FromQuery] string message)
{
await mesh.PublishAsync(new SomeCommand(message));
await mesh.SendAsync(new SomeCommand(message));
return Ok();
}
}
```
## (Experimental) HTTP Endpoints
- to lazy to write controllers?
- services and consumer messages may be exposed directly via http endpoints
- for services only methods with a single complex parameter are supported
- no generics
- no simple types
## expose services and messages
- for this example types ending with ..Command or ..Message will be exposed as endpoints
```csharp
app.MapServiceMesh(["Command", "Message"]);
```
## customize or filter http endpoints
```csharp
builder.AddServiceMesh(options =>
{
options.MapHttpPublishRoute =
(app, type, handler) =>
{
app.MapPost("/api/publish/" + type.Name, handler)
.WithTags("publish");
};
options.MapHttpSendRoute =
(app, type, handler) =>
{
// no handlers without nats ack
//app.MapPost("/api/send/" + type.Name, handler).WithTags("send");
};
options.MapHttpRequestRoute = { get; set; } =
(app, requestType, responseType, service, method, handler) =>
{
app.MapPost("/api/" + service + "/" + method.Name, handler)
.Produces(200, responseType)
.WithTags(service);
};
});
```