{"id":26398531,"url":"https://github.com/rafaelpadovezi/ziggurat","last_synced_at":"2025-03-17T12:32:12.310Z","repository":{"id":37022224,"uuid":"408598875","full_name":"rafaelpadovezi/Ziggurat","owner":"rafaelpadovezi","description":"A .NET library to create message consumers.","archived":false,"fork":false,"pushed_at":"2024-12-23T12:34:58.000Z","size":248,"stargazers_count":28,"open_issues_count":6,"forks_count":2,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-11T12:50:54.079Z","etag":null,"topics":["csharp","dotnet","messaging"],"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/rafaelpadovezi.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}},"created_at":"2021-09-20T21:00:35.000Z","updated_at":"2025-02-17T18:59:20.000Z","dependencies_parsed_at":"2024-05-09T18:59:40.130Z","dependency_job_id":"0c38c20b-c25b-420c-838c-44a9dc44b3cd","html_url":"https://github.com/rafaelpadovezi/Ziggurat","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafaelpadovezi%2FZiggurat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafaelpadovezi%2FZiggurat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafaelpadovezi%2FZiggurat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rafaelpadovezi%2FZiggurat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rafaelpadovezi","download_url":"https://codeload.github.com/rafaelpadovezi/Ziggurat/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244033735,"owners_count":20386985,"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","dotnet","messaging"],"created_at":"2025-03-17T12:31:35.588Z","updated_at":"2025-03-17T12:32:12.303Z","avatar_url":"https://github.com/rafaelpadovezi.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![Ziggurat icon](./docs/icon.png) Ziggurat\n\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=rafaelpadovezi_Ziggurat\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=rafaelpadovezi_Ziggurat)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=rafaelpadovezi_Ziggurat\u0026metric=coverage)](https://sonarcloud.io/dashboard?id=rafaelpadovezi_Ziggurat)\n\nA .NET library to create message consumers.\n\nZiggurat implements functionalities to help solve common problems when dealing with messages:\n- [Idempotency](https://microservices.io/patterns/communication-style/idempotent-consumer.html)\n- Middleware: allows to create middlewares to consumers to handle logging, validation and whatever is needed. \n\n## How it works\n\nThe library uses the [decorator pattern](https://refactoring.guru/design-patterns/decorator/csharp/example) to execute a middleware pipeline when calling the consumer services. This way is possible to extend the service code adding new functionality.\n\nThe Idempotency middleware wraps the service enforcing that the message in only being processed once by tracking the message processing on the database.\n\nAlso, it's possible to add custom middlewares to the pipeline.\n\n## Support\n\nZiggurat has support to:\n- Storage:\n  - MS SQL Server\n  - MongoDB\n- Messaging Library\n  - [CAP](https://cap.dotnetcore.xyz/)\n\n## Install\n\n|                     |                                                                                                              |\n|---------------------|--------------------------------------------------------------------------------------------------------------|\n| Ziggurat            | [![Nuget](https://img.shields.io/nuget/v/Ziggurat)](https://www.nuget.org/packages/Ziggurat)                 |\n| Ziggurat.CapAdapter | [![Nuget](https://img.shields.io/nuget/v/Ziggurat.CapAdapter)](https://www.nuget.org/packages/Ziggurat.CapAdapter) |\n| Ziggurat.SqlServer  | [![Nuget](https://img.shields.io/nuget/v/Ziggurat.SqlServer)](https://www.nuget.org/packages/Ziggurat.SqlServer) |\n| Ziggurat.MongoDB    | [![Nuget](https://img.shields.io/nuget/v/Ziggurat.MongoDB)](https://www.nuget.org/packages/Ziggurat.MongoDB) |\n\n## Usage\n\nZiggurat works with middlewares. Registering middlewares adds functionality to the message consumer. Important to note that multiple middlewares can be registered to the same consumer. They are executed following the order of the registration.\n\n### SQL Server with Entity Framework\n\nZiggurat integrates with the application [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) to track the processed messages and ensures that each message is processed only once. Also, the EF Core migrations are used to create the message tracking table with the correct constraints. If you are not using migration in your project the table must be created manually.\n\nTo use Ziggurat is necessary to create a message and a consumer service type:\n\n```c#\npublic class MyMessage : IMessage\n{\n    public string Content { get; set; }\n    public string MessageId { get; set; }\n    public string MessageGroup { get; set; }\n}\n\npublic class MyMessageConsumerService : IConsumerService\u003cMyMessage\u003e\n{\n    private readonly MyDbContext _context;\n\n    public MyMessageConsumerService(MyDbContext context)\n    {\n        _context = context;\n    }\n\n    public async Task ProcessMessageAsync(MyMessage message)\n    {\n        // Change the application bussiness objects tracked by EF Core\n        _context.SomeEntity.Add(x);\n        await _context.SaveChangesAsync();\n    }\n} \n```\n\nZiggurat.SqlServer ensures that the processed messages are tracked by the EF Core `DbContext`. Calling `SaveChangesAsync` will save the changes made to the business objects and the processed message to the DB.\n\nThe message type must implements the interface `IMessage`.\n\nIt's also required that the consumers are setup on the dependency injection configuration. Besides, it's necessary to add the CAP filter that enriches the message with the required information.\n\n\n```c#\nservices\n    .AddConsumerService\u003cMyMessage, MyConsumerService\u003e(\n        options =\u003e\n        {\n            options.UseEntityFrameworkIdempotency\u003cMyMessage, MyDbContext\u003e();\n        });\nservices.\n    .AddCap(x =\u003e ...)\n    .AddSubscribeFilter\u003cBootstrapFilter\u003e();\n```\n\nAnd finally, the the message tracking DbSet must be added to the DbContext:\n\n```c#\npublic class MyDbContext : DbContext\n{\n    public DbSet\u003cMessageTracking\u003e Messages { get; set; }\n    ...\n\n    protected override void OnModelCreating(ModelBuilder modelBuilder)\n    {\n        modelBuilder.MapMessageTracker();\n    }\n}\n```\n\n### MongoDB\n\nUsing Ziggurat with MongoDB has some differences compared to SQL Server. The dependency injection registration must call the method `UseMongoDbIdempotency`:\n\n```c#\nbuilder.Services.AddConsumerService\u003cMyMessage, ConsumerService\u003e(\n    options =\u003e options.UseMongoDbIdempotency(\"databaseName\"));\n```\n\nTo keep the consumer operation atomic, is necessary to use the method `StartIdempotentTransaction``:\n\n```c#\n\npublic class MyMessageConsumerService : IConsumerService\u003cMyMessage\u003e\n{\n    private readonly IMongoClient _client;\n\n    public MyMessageConsumerService(IMongoClient client)\n    {\n        _client = client;\n    }\n\n    public async Task ProcessMessageAsync(MyMessage message)\n    {\n        using var session = _client.StartIdempotentTransaction(message);\n        // save business object\n        var collection = _client.GetDatabase(\"databaseName\").GetCollection\u003cSomeEntity\u003e(\"someEntity\");\n        await collection.InsertOneAsync(session, x);\n        // must commit transaction\n        await session.CommitTransactionAsync();\n    }\n}\n```\n### Logging middleware\n\nSince version 8.0.0, Ziggurat has a built-in middleware to log the message processing. It's possible to use it by calling the method `UseLoggingMiddleware`:\n\n```c#\nservices\n    .AddConsumerService\u003cMyMessage, MyConsumerService\u003e(\n        options =\u003e\n        {\n            options.UseLoggingMiddleware\u003cMyMessage\u003e();\n        });\n```\n\n### Custom middleware\n\nIt's possible to create custom middleware for the consumers.\n\n```c#\npublic class MyMiddleware\u003cTMessage\u003e : IConsumerMiddleware\u003cTMessage\u003e\n    where TMessage : IMessage\n{\n   public async Task OnExecutingAsync(TMessage message, ConsumerServiceDelegate\u003cTMessage\u003e next)\n    {\n        // Do something before\n        await next(message);\n        // Do something after\n    }\n}\n```\n\nAlso, it's required to register the middleware on the dependency injection configuration.\n\n```c#\n.AddConsumerService\u003cMyMessage, MyMessageConsumerService\u003e(\n    options =\u003e\n    {\n        options.Use\u003cLoggingMiddleware\u003cMyMessage\u003e\u003e();\n    });\n```\n\nImportant to note that multiple middlewares can be registered to the same consumer. They are executed following the order of the registration.\n\nYou can look at the samples folder to see more examples of usage.\n\n### Clean old message tracking records\n\nThe library provides a method to clean old message tracking records. A background service can be added using the extension method `AddZigguratCleaner`:\n\n```c#\nservices.AddZigguratCleaner(options =\u003e {\n    options.CleaningInterval = TimeSpan.FromMinutes(15);\n    options.ExpireAfterInDays = 7;\n    options.BatchSize = 100_000; // Only works with SQL Server\n});\n```\n\n## Run tests\n\n```shell\ndocker compose up -d mongoclustersetup sqlserver\ndotnet test\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frafaelpadovezi%2Fziggurat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frafaelpadovezi%2Fziggurat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frafaelpadovezi%2Fziggurat/lists"}