{"id":17081474,"url":"https://github.com/benfoster/odyssey","last_synced_at":"2025-04-12T20:53:35.896Z","repository":{"id":62642027,"uuid":"557747989","full_name":"benfoster/odyssey","owner":"benfoster","description":"Odyssey enables Azure Cosmos DB to be used as an Event Store","archived":false,"fork":false,"pushed_at":"2024-02-29T15:14:20.000Z","size":116,"stargazers_count":6,"open_issues_count":5,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-12T20:53:31.399Z","etag":null,"topics":["cosmosdb","event-sourcing","eventstore"],"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/benfoster.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":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-10-26T08:22:24.000Z","updated_at":"2024-01-15T11:29:39.000Z","dependencies_parsed_at":"2024-01-12T22:27:44.571Z","dependency_job_id":null,"html_url":"https://github.com/benfoster/odyssey","commit_stats":{"total_commits":44,"total_committers":3,"mean_commits":"14.666666666666666","dds":0.09090909090909094,"last_synced_commit":"24bded23f3ca7b98b536b3c61e6298eac0a37e2d"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benfoster%2Fodyssey","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benfoster%2Fodyssey/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benfoster%2Fodyssey/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benfoster%2Fodyssey/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benfoster","download_url":"https://codeload.github.com/benfoster/odyssey/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248631725,"owners_count":21136560,"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":["cosmosdb","event-sourcing","eventstore"],"created_at":"2024-10-14T12:53:01.855Z","updated_at":"2025-04-12T20:53:35.874Z","avatar_url":"https://github.com/benfoster.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Odyssey\n\nOdyssey enables Azure Cosmos DB to be used as an Event Store.\n\n## Quick Start\n\n### Register Odyssey at startup:\n\n```c#\nbuilder.Services.AddOdyssey(cosmosClientFactory: _ =\u003e CreateClient(builder.Configuration));\n\nstatic CosmosClient CreateClient(IConfiguration configuration)\n{\n    return new(\n        accountEndpoint: configuration[\"Cosmos:Endpoint\"],\n        authKeyOrResourceToken: configuration[\"Cosmos:Token\"]\n    );\n}\n```\n\nYou can provide a factory to create and register the underlying `CosmosClient` instance as per the above example, otherwise you must register this yourself.\n\n#### Initialization\n\nIf you want Odyssey to auto-create the database and/or container, call `IEventStore.Initialize` at startup:\n\n```c#\nawait builder.Services.GetRequiredService\u003cIEventStore\u003e().Initialize();\n```\n\n### Take a dependency on `IEventStore`\n\n```c#\napp.MapPost(\"/payments\", async (PaymentRequest payment, IEventStore eventStore) =\u003e\n{\n    var initiated = new PaymentInitiated(Id.NewId(\"pay\"), payment.Amount, payment.Currency, payment.Reference);\n\n    var result = await eventStore.AppendToStream(initiated.Id.ToString(), new[] { Map(initiated) }, StreamState.NoStream);\n\n    return result.Match(\n        success =\u003e Results.Ok(new\n        {\n            initiated.Id,\n            Status = \"initiated\"\n        }),\n        unexpected =\u003e Results.Conflict()\n    );\n});\n```\n\n## Configuration\n\nBy default Odyssey will attempt to create a Cosmos Database named `odyssey` and container named `events`.\n\nYou can control these settings as well as the auto-create settings using the .NET configuration system, for example, in `appsettings.json`:\n\n```json\n  \"Odyssey\": {\n    \"DatabaseId\": \"payments\",\n    \"ContainerId\": \"payment-events\",\n    \"AutoCreateDatabase\": false,\n    \"AutoCreateContainer\": false\n  },\n```\n\nTo initialize Odyssey with these settings, pass the relevant configuration section to Odyssey during initialization:\n\n```c#\nbuilder.Services.AddOdyssey(\n    configureOptions: options =\u003e options.DatabaseThroughputProperties = ThroughputProperties.CreateAutoscaleThroughput(1000),\n    cosmosClientFactory: _ =\u003e CreateClient(builder.Configuration),\n    builder.Configuration.GetSection(\"Odyssey\")\n);\n\nstatic CosmosClient CreateClient(IConfiguration configuration)\n{\n    return new(\n        accountEndpoint: configuration[\"Cosmos:Endpoint\"],\n        authKeyOrResourceToken: configuration[\"Cosmos:Token\"]\n    );\n}\n```\n\nNote that this also demonstrates how to specify the throughput properties of the created Container.\n\n### Type Resolvers\n\nBy default, events are deserialized to the fully qualified type of the original event using the automatically generated metadata field `_clr_type`.\n\nIn some cases such as event consumers, you may not have access to the original assembly in which the event types reside. Instead, you can choose provide your own type map which will make use of the `_clr_type_name` (the _non_-qualified type name) to resolve the type, for example:\n\n```c#\nvar typeMap = new Dictionary\u003cstring, Type\u003e {\n    { nameof(TestEvent), typeof(SomeOtherEvent) }\n}.ToImmutableDictionary();\n\nbuilder.Services.AddOdyssey(\n    configureOptions: options =\u003e options.TypeResolver = TypeResolvers.UsingTypeMap(typeMap),\n    cosmosClientFactory: _ =\u003e CreateClient(builder.Configuration),\n    builder.Configuration.GetSection(\"Odyssey\")\n);\n```\n\n#### Handling unresolved types\n\nIn some cases it may not be possible or desirable to resolve an event's type. This could be the case for consumers where you wish to ignore certain events or in producers where an event has been deprecated.\nThe default strategy is to throw. This can be overridden by providing your own `UnresolvedTypeStrategy` or using the provided strategy, `UnresolvedTypeStrategy.Skip`:\n\n```c#\nbuilder.Services.AddOdyssey(\n    configureOptions: options =\u003e options.UnresolvedTypeStrategy = UnresolvedTypeStrategies.Skip,\n    cosmosClientFactory: _ =\u003e CreateClient(builder.Configuration),\n    builder.Configuration.GetSection(\"Odyssey\")\n);\n```\n\nWhen using `Skip`, any event types that have failed to resolve using the provided `TypeResolver` will resolve to an instance of `UnresolvedEvent`.\n\nAlternatively, if you are using the type map resolver above, you can specify a fallback type:\n\n```c#\nTypeResolvers.UsingTypeMap(typeMap, typeof(MyFallbackType))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenfoster%2Fodyssey","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenfoster%2Fodyssey","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenfoster%2Fodyssey/lists"}