Cephalon.Data.Neo4j
Maturity:
M1· Ownership: provider-managed · Family:data-and-cdc· See audit, matrix.
Cephalon.Data.Neo4j is the Neo4j graph-store companion pack for Cephalon, proving that the companion-pack pattern established by Cephalon.Data.MongoDB and Cephalon.Data.Redis extends cleanly to graph-oriented databases without any changes to Cephalon.Engine or Cephalon.Abstractions.
What it owns
Section titled “What it owns”- registers a singleton
IDriverfrom a Bolt URI and credentials usingTryAddsemantics so a host-owned driver is never displaced - registers a scoped
IOutboxbacked by Neo4j:OutboxMessagegraph nodes whenRegisterOutboxis enabled; node labels honour the optionalLabelPrefix - registers a scoped
IEventDispatchStoreagainst the same Neo4j outbox nodes whenRegisterOutboxis enabled so consumer-managed or adapter-managed dispatch can read pending staged messages and persist durable dispatch outcomes without a relational bridge - registers a scoped
IInboxbacked by Neo4j:InboxReceiptgraph nodes whenRegisterInboxis enabled; node labels honour the optionalLabelPrefix - ensures that outbox staging is idempotent via Cypher
MERGEonmessageId— theON CREATE SETclause fires only for new nodes, making repeatedEnqueueAsynccalls with the same message id safe no-ops - ensures that inbox tracking is idempotent via the same
MERGEpattern onmessageId - bootstraps a uniqueness constraint on each managed label on first use (via
CREATE CONSTRAINT ... IF NOT EXISTS) - exposes operator-facing outbox and inbox descriptors through the engine runtime surfaces
- projects the outbox descriptor through the
event-driven-integrationtechnology surface as outbox producers when that technology is active - projects the inbox descriptor through the same technology surface as inbox stores when the technology is active
- publishes capability metadata
data.neo4j,data.graph-store, and optionallydata.outbox.neo4janddata.inbox.neo4jintrospectable at runtime through the manifest
Main surfaces
Section titled “Main surfaces”Configuration/Neo4jDataOptions.csModules/Neo4jDataModule.csRegistration/Neo4jDataEngineBuilderExtensions.csServices/Neo4jOutbox.csServices/Neo4jEventDispatchStore.csServices/Neo4jOutboxRuntimeSurfaceContributor.csServices/Neo4jInbox.csServices/Neo4jInboxRuntimeSurfaceContributor.cs
How it fits
Section titled “How it fits”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.Neo4j adds the Neo4j-backed outbox and inbox persistence paths that let event-driven workloads stage and track messages against a graph store without switching to a relational or document-oriented provider.
The slice is intentionally narrow: it proves the companion-pack pattern works for graph-oriented stores, ships idempotent outbox and inbox implementations, adds a consumer-managed dispatch-store baseline for staged outboxes, and exposes the same runtime introspection surfaces as the MongoDB and Redis providers.
Registration
Section titled “Registration”engine.AddNeo4jData( uri: "bolt://localhost:7687", username: "neo4j", password: "secret");To enable the outbox and inbox paths:
engine.AddNeo4jData( uri: "bolt://localhost:7687", username: "neo4j", password: "secret", configure: options => { options.RegisterOutbox = true; options.RegisterInbox = true; options.LabelPrefix = "MyApp"; // optional — prefix all Cephalon node labels });To resolve the Bolt URI from the root Uris section:
engine.AddNeo4jData(options =>{ options.UriName = "Graph"; options.Username = "neo4j"; options.Password = "secret"; options.RegisterOutbox = true;});{ "Uris": { "Graph": "bolt://localhost:7687" }, "Engine": { "Data": { "Neo4j": { "UriName": "Graph", "Username": "neo4j", "Password": "secret" } } }}UriName and Uri are mutually exclusive. If both are set, the pack throws during service
registration. Leaving both unset falls back to bolt://localhost:7687.
Configuration options
Section titled “Configuration options”| Option | Type | Default | Description |
|---|---|---|---|
UriName | string? | null | Root Uris key to resolve for Neo4j |
Uri | string? | null | Inline Neo4j Bolt URI |
Username | string | "neo4j" | Neo4j username |
Password | string | "" | Neo4j password |
LabelPrefix | string | "Cephalon" | Prefix applied to all managed node labels |
RegisterOutbox | bool | false | Register IOutbox backed by :{LabelPrefix}OutboxMessage nodes |
RegisterInbox | bool | false | Register IInbox backed by :{LabelPrefix}InboxReceipt nodes |
OutboxMessage node schema (:{LabelPrefix}OutboxMessage)
Section titled “OutboxMessage node schema (:{LabelPrefix}OutboxMessage)”The node label is {LabelPrefix}OutboxMessage (default: :CephalonOutboxMessage).
| Property | Type | Notes |
|---|---|---|
messageId | string | Unique idempotency key; uniqueness constraint prevents duplicate staging |
channelId | string | Target channel identifier |
messageType | string | Fully qualified CLR message type name |
payload | string | System.Text.Json-serialized message body |
contentType | string | MIME content type (empty string when not provided) |
correlationId | string | Optional causality tracking identifier |
tenantId | string | Optional multi-tenancy discriminator |
occurredAtUtc | string | ISO 8601 UTC timestamp when the domain event occurred |
createdAtUtc | string | ISO 8601 UTC timestamp when the message was staged |
dispatchAttemptCount | int | Initialized to 0; incremented by dispatch adapters |
dispatchedAtUtc | string | Optional ISO 8601 UTC timestamp written when dispatch succeeds or is skipped |
nextAttemptAtUtc | string | Optional ISO 8601 UTC timestamp written when dispatch is delayed for retry |
headersJson | string | System.Text.Json-serialized headers dictionary |
metadataJson | string | System.Text.Json-serialized metadata dictionary |
InboxReceipt node schema (:{LabelPrefix}InboxReceipt)
Section titled “InboxReceipt node schema (:{LabelPrefix}InboxReceipt)”The node label is {LabelPrefix}InboxReceipt (default: :CephalonInboxReceipt).
| Property | Type | Notes |
|---|---|---|
messageId | string | Processed message id; uniqueness constraint enforces exactly-once semantics |
channelId | string | Channel the message arrived on |
messageType | string | Fully qualified CLR message type name |
correlationId | string | Optional causality tracking identifier |
tenantId | string | Optional multi-tenancy discriminator |
receivedAtUtc | string | ISO 8601 UTC timestamp when the message was received |
processedAtUtc | string | ISO 8601 UTC timestamp when the message was first processed |
Idempotency semantics
Section titled “Idempotency semantics”Both IOutbox.EnqueueAsync and IInbox.MarkProcessedAsync use Cypher MERGE on messageId:
MERGE (m:CephalonOutboxMessage {messageId: $messageId})ON CREATE SET m.channelId = $channelId, ...ON CREATE SET fires only when the MERGE creates a new node. When the node already exists (i.e., the message was already staged or processed), MERGE matches it and the ON CREATE SET block is skipped — making repeated calls with the same id safe no-ops. No exception is thrown and no special error code handling is required.
A uniqueness constraint is created on first use via CREATE CONSTRAINT ... IF NOT EXISTS, ensuring the MERGE key is indexed and enforced at the database level.
Runtime capabilities
Section titled “Runtime capabilities”When Neo4jDataModule is active, the following capability keys appear in the runtime manifest:
| Capability key | When registered |
|---|---|
data.neo4j | Always |
data.graph-store | Always |
data.outbox.neo4j | RegisterOutbox = true |
data.inbox.neo4j | RegisterInbox = true |
Runtime surface entries
Section titled “Runtime surface entries”When the event-driven-integration technology is active, the following entries appear under /engine/snapshot:
| Surface | Entry id | provider metadata |
|---|---|---|
outbox-producers | neo4j-outbox | neo4j |
inbox-stores | neo4j-inbox | neo4j |
Not shipped in this slice
Section titled “Not shipped in this slice”This pack intentionally does not claim:
- graph traversal queries or relationship modeling between domain entities
- full-text search or spatial index queries
- APOC procedure integration
IReadStore/IWriteStoredispatch backed by Neo4j — query and command handlers should useIDriverdirectly- transaction-scoped outbox staging spanning multiple graph operations
- adapter-owned dispatch loops or broker retry scheduling beyond the shipped consumer-managed dispatch-store baseline
- change-data-capture or Neo4j streams integration
These remain explicit later slices to keep the initial provider claim honest.