{"id":21283950,"url":"https://github.com/coronabytes/servicemesh","last_synced_at":"2025-07-11T11:31:33.399Z","repository":{"id":250583767,"uuid":"834812141","full_name":"coronabytes/servicemesh","owner":"coronabytes","description":".NET service mesh based on nats","archived":false,"fork":false,"pushed_at":"2025-06-10T15:32:24.000Z","size":143,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-10T15:56:53.697Z","etag":null,"topics":["csharp","nats","service-mesh"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coronabytes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-07-28T12:44:13.000Z","updated_at":"2025-06-10T15:32:17.000Z","dependencies_parsed_at":"2024-11-12T23:25:04.417Z","dependency_job_id":"e141292d-c02f-458b-b4be-1b59a1921d99","html_url":"https://github.com/coronabytes/servicemesh","commit_stats":null,"previous_names":["coronabytes/servicemesh"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/coronabytes/servicemesh","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coronabytes%2Fservicemesh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coronabytes%2Fservicemesh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coronabytes%2Fservicemesh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coronabytes%2Fservicemesh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coronabytes","download_url":"https://codeload.github.com/coronabytes/servicemesh/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coronabytes%2Fservicemesh/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264795385,"owners_count":23665227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","nats","service-mesh"],"created_at":"2024-11-21T11:13:16.698Z","updated_at":"2025-07-11T11:31:33.151Z","avatar_url":"https://github.com/coronabytes.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Nuget](https://img.shields.io/nuget/v/Core.ServiceMesh)](https://www.nuget.org/packages/Core.ServiceMesh)\n[![Nuget](https://img.shields.io/nuget/dt/Core.ServiceMesh)](https://www.nuget.org/packages/Core.ServiceMesh)\n\n# Service Mesh for ASP.NET Core\ninterconnect (Micro-)services in .NET with ease\n\n## Installation\n```sh\n# for shared libraries\ndotnet add package Core.ServiceMesh.Abstractions\ndotnet add package Core.ServiceMesh.SourceGen\n\n# for containers\ndotnet add package Core.ServiceMesh\ndotnet add package Core.ServiceMesh.SourceGen\n```\n\n## Features\n- based on https://nats.io\n- uses source generators for remote and telemetry proxies\n- service request reponse pattern (sync)\n- strongly typed clients out of the box\n- response streaming with IAsyncEnumerable\n- event streaming via NATS JetStream (async)\n- durable and transient consumers\n- open telemetry support\n- supports local service traces in \"AutoTrace\" mode\n\n## Initialization in ASP.NET Core\n```csharp\nbuilder.AddServiceMesh(options =\u003e\n{\n    options.ConfigureNats = opts =\u003e opts with\n    {\n        Url = \"nats://localhost:4222\"\n    };\n    options.ConfigureStream = (name, config) =\u003e\n    {\n        config.MaxAge = TimeSpan.FromDays(1);\n    };\n    options.InterfaceMode = ServiceInterfaceMode.Auto;\n    options.Assemblies = [typeof(ISomeService).Assembly, typeof(SomeService).Assembly];\n});\n```\n\n## Service Interface\n- service interfaces/contracts should be placed in abstraction libraries to be shared among your microservices\n- only ValueTask and ValueTask\\\u003cT\\\u003e and IAsyncEnumerable\u003cT\u003e supported\n- open generics with optional constraints are supported\n```csharp\n[ServiceMesh(\"someservice\")]\npublic interface ISomeService\n{\n    ValueTask\u003cstring\u003e GetSomeString(int a, string b);\n    ValueTask CreateSomeObject();\n    ValueTask\u003cT\u003e GenericAdd\u003cT\u003e(T a, T b) where T : INumber\u003cT\u003e;\n    IAsyncEnumerable\u003cstring\u003e StreamResponse();\n};\n```\n\n## Service Implementation\n\n```csharp\n[ServiceMesh]\npublic class SomeService(ILogger\u003cSomeService\u003e logger) : ISomeService\n{\n    public async ValueTask\u003cstring\u003e GetSomeString(int a, string b)\n    {\n        await Task.Delay(100);\n        return b + \" \" + a;\n    }\n\n    public async ValueTask CreateSomeObject()\n    {\n        await Task.Delay(100);\n        logger.LogInformation(nameof(CreateSomeObject));\n    }\n\n    public async ValueTask\u003cT\u003e GenericAdd\u003cT\u003e(T a, T b) where T : INumber\u003cT\u003e\n    {\n        await Task.Delay(100);\n        return a + b;\n    }\n\n    public async IAsyncEnumerable\u003cstring\u003e StreamResponse()\n    {\n        await Task.Delay(100);\n        yield return \"a\";\n        await Task.Delay(100);\n        yield return \"b\";\n        await Task.Delay(100);\n        yield return \"c\";\n    }\n}\n```\n\n## Service Invocation\n- inject service interface into your controllers/services\n- they are automatically proxied over nats when not available in the same container \n```csharp\npublic class DevController(ISomeService someService) : ControllerBase\n{\n    [HttpPost(\"add-ints\")]\n    public async Task\u003cActionResult\u003cint\u003e\u003e CreateIntObject([FromQuery] int a = 3, [FromQuery] int b = 5)\n    {\n        return await someService.GenericAdd(a, b);\n    }\n\n    [HttpPost(\"add-doubles\")]\n    public async Task\u003cActionResult\u003cdouble\u003e\u003e CreateDoubleObject([FromQuery] double a = 3.1, [FromQuery] double b = 5.1)\n    {\n        return await someService.GenericAdd(a, b);\n    }\n}\n```\n\n## Events, Streams and Consumers\n- durable consumers need to have a unique name (so you can rename your class later on)\n- PublishAsync will await confirmation by nats broker\n- SendAsync means Fire and Forget\n\n```csharp\npublic record SomeCommand(string Name);\n\n[DurableConsumer(\"SomeCommandHandler\", \"default\")]\npublic class SomeCommandHandler(ILogger\u003cSomeCommandHandler\u003e logger) : IConsumer\u003cSomeCommand\u003e\n{\n    public ValueTask ConsumeAsync(SomeCommand message, CancellationToken token)\n    {\n        // do stuff\n        return ValueTask.CompletedTask;\n    }\n}\n\npublic class DevController(IServiceMesh mesh) : ControllerBase\n{\n    [HttpPost(\"publish\")]\n    public async Task\u003cIActionResult\u003e Publish([FromQuery] string message)\n    {\n        await mesh.PublishAsync(new SomeCommand(message));\n        await mesh.SendAsync(new SomeCommand(message));\n        return Ok();\n    }\n}\n```\n\n## (Experimental) HTTP Endpoints\n- to lazy to write controllers?\n- services and consumer messages may be exposed directly via http endpoints\n  - for services only methods with a single complex parameter are supported\n  - no generics\n  - no simple types\n\n## expose services and messages\n- for this example types ending with ..Command or ..Message will be exposed as endpoints\n```csharp\napp.MapServiceMesh([\"Command\", \"Message\"]);\n```\n\n## customize or filter http endpoints\n```csharp\nbuilder.AddServiceMesh(options =\u003e\n{\n  options.MapHttpPublishRoute =\n        (app, type, handler) =\u003e\n        {\n            app.MapPost(\"/api/publish/\" + type.Name, handler)\n                .WithTags(\"publish\");\n        };\n\n  options.MapHttpSendRoute =\n        (app, type, handler) =\u003e\n        {\n            // no handlers without nats ack\n            //app.MapPost(\"/api/send/\" + type.Name, handler).WithTags(\"send\");\n        };\n\n  options.MapHttpRequestRoute = { get; set; } =\n        (app, requestType, responseType, service, method, handler) =\u003e\n        {\n            app.MapPost(\"/api/\" + service + \"/\" + method.Name, handler)\n                .Produces(200, responseType)\n                .WithTags(service);\n        };\n});\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoronabytes%2Fservicemesh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoronabytes%2Fservicemesh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoronabytes%2Fservicemesh/lists"}