Skip to content

Cephalon.Data.Elasticsearch

Maturity: M1 · Ownership: provider-managed · Family: data-and-cdc · See audit, matrix.

Cephalon.Data.Elasticsearch is the Elasticsearch search-store companion pack for Cephalon, proving that the companion-pack pattern established by Cephalon.Data.MongoDB extends cleanly to search-oriented document stores without any changes to Cephalon.Engine or Cephalon.Abstractions.

  • registers a singleton ElasticsearchClient (from Elastic.Clients.Elasticsearch 8.17.0) using TryAdd semantics so a host-owned client is never displaced
  • optionally configures Basic authentication via ElasticsearchClientSettings.Authentication(new BasicAuthentication(...)) when Username is set
  • registers a scoped IOutbox backed by an Elasticsearch index when RegisterOutbox is enabled; documents are staged with op_type=create for idempotency — HTTP 409 is swallowed silently
  • registers a scoped IEventDispatchStore over that same outbox index when RegisterOutbox is enabled, so staged Elasticsearch events can be read and durable dispatch outcomes can be written back through the runtime-neutral eventing contract
  • registers a scoped IInbox backed by a separate Elasticsearch index when RegisterInbox is enabled; existence is checked via GetAsync by document id; marking uses op_type=create with 409 swallow
  • exposes operator-facing outbox and inbox descriptors through the engine runtime surfaces
  • projects outbox and inbox descriptors through the event-driven-integration technology surface when that technology is active
  • publishes capability metadata data.elasticsearch, data.search-store, and optionally data.outbox.elasticsearch and data.inbox.elasticsearch introspectable at runtime through the manifest
  • Configuration/ElasticsearchDataOptions.cs
  • Modules/ElasticsearchDataModule.cs
  • Registration/ElasticsearchDataEngineBuilderExtensions.cs
  • Services/ElasticsearchOutbox.cs
  • Services/ElasticsearchEventDispatchStore.cs
  • Services/ElasticsearchOutboxRuntimeSurfaceContributor.cs
  • Services/ElasticsearchInbox.cs
  • Services/ElasticsearchInboxRuntimeSurfaceContributor.cs

This pack sits on top of Cephalon.Data, not in place of it. Cephalon.Data still owns the runtime-neutral IReadStore / IWriteStore dispatching surface. Cephalon.Data.Elasticsearch adds the Elasticsearch-backed outbox and inbox persistence paths that let event-driven workloads stage and track messages against a search index without switching to a relational or document-oriented provider, and it now also exposes the same staged outbox through IEventDispatchStore so consumer-managed or adapter-owned dispatch loops can read pending items and persist durable dispatch outcomes without Elasticsearch-specific host glue.

engine.AddElasticsearchData("http://localhost:9200");

To enable outbox and inbox paths:

engine.AddElasticsearchData("http://localhost:9200", options =>
{
options.RegisterOutbox = true;
options.RegisterInbox = true;
options.IndexPrefix = "myapp-"; // optional — prefix all Cephalon-managed index names
});

With Basic authentication:

engine.AddElasticsearchData("https://my-cluster.es.io:9200", options =>
{
options.Username = "elastic";
options.Password = "secret";
options.RegisterOutbox = true;
});

To resolve the node URI from the root Uris section:

engine.AddElasticsearchData(options =>
{
options.UriName = "SearchCluster";
options.Username = "elastic";
options.Password = "secret";
options.RegisterOutbox = true;
});
{
"Uris": {
"SearchCluster": "https://my-cluster.es.io:9200"
},
"Engine": {
"Data": {
"Elasticsearch": {
"UriName": "SearchCluster",
"IndexPrefix": "myapp-"
}
}
}
}

UriName and Uri are mutually exclusive. If both are set, the pack throws during service registration. Leaving both unset falls back to http://localhost:9200.

OptionTypeDefaultDescription
UriNamestring?nullRoot Uris key to resolve for Elasticsearch
Uristring?nullInline Elasticsearch node URI
Usernamestring?nullOptional username for Basic authentication
Passwordstring?nullOptional password for Basic authentication
IndexPrefixstring""Prefix applied to all Cephalon-managed index names
RegisterOutboxboolfalseRegister IOutbox backed by {IndexPrefix}outbox-messages
RegisterInboxboolfalseRegister IInbox backed by {IndexPrefix}inbox-receipts

Outbox document schema ({IndexPrefix}outbox-messages)

Section titled “Outbox document schema ({IndexPrefix}outbox-messages)”
FieldJSON nameTypeNotes
MessageIdmessage_idstringDocument _id; idempotency key via op_type=create
ChannelIdchannel_idstringTarget channel identifier
MessageTypemessage_typestringFully qualified CLR message type name
PayloadpayloadstringSystem.Text.Json-serialized message body
ContentTypecontent_typestringMIME content type
CorrelationIdcorrelation_idstring?Optional causality tracking identifier
TenantIdtenant_idstring?Optional multi-tenancy discriminator
OccurredAtUtcoccurred_at_utcDateTimeWhen the domain event occurred
CreatedAtUtccreated_at_utcDateTimeWhen the message was staged
DispatchAttemptCountdispatch_attempt_countintInitialized to 0
HeadersJsonheaders_jsonstringSystem.Text.Json-serialized headers dictionary
MetadataJsonmetadata_jsonstringSystem.Text.Json-serialized metadata dictionary

Inbox document schema ({IndexPrefix}inbox-receipts)

Section titled “Inbox document schema ({IndexPrefix}inbox-receipts)”
FieldJSON nameTypeNotes
MessageIdmessage_idstringDocument _id; idempotency key
ChannelIdchannel_idstringChannel the message arrived on
MessageTypemessage_typestringFully qualified CLR message type name
ReceivedAtUtcreceived_at_utcDateTimeWhen the message was received
ProcessedAtUtcprocessed_at_utcDateTimeWhen the message was first processed
CorrelationIdcorrelation_idstring?Optional causality tracking identifier
TenantIdtenant_idstring?Optional multi-tenancy discriminator

Both IOutbox.EnqueueAsync and IInbox.MarkProcessedAsync use op_type=create when indexing documents:

  • HTTP 409 (document already exists) is swallowed silently — making repeated calls with the same message id safe no-ops
  • Any other non-success response is a real error and throws InvalidOperationException

IInbox.HasProcessedAsync uses GetAsync by document id — the Found property on the response determines whether the message has been processed.

When ElasticsearchDataModule is active, the following capability keys appear in the runtime manifest:

Capability keyWhen registered
data.elasticsearchAlways
data.search-storeAlways
data.outbox.elasticsearchRegisterOutbox = true
data.inbox.elasticsearchRegisterInbox = true

The companion event-store pack is Cephalon.EventSourcing.Elasticsearch. It registers IEventStore with compound document ids {streamId}#{streamVersion} for uniqueness and application-layer optimistic concurrency.

builder.Services.AddCephalonElasticsearchEventSourcing(
uri: "http://localhost:9200",
indexName: "event-streams");

This pack intentionally does not claim:

  • full-text search, aggregations, or IReadStore / IWriteStore dispatch backed by Elasticsearch
  • index lifecycle management (ILM) or template provisioning
  • cross-cluster search or multi-index querying
  • pack-owned dispatch loops or broker-specific retry scheduling beyond the runtime-neutral IEventDispatchStore bridge
  • bulk indexing or pipeline ingestion