Add or Review a Persistence Provider / Serializer
When to invoke
- User asks to create a new persistence backend (e.g., MongoDB, PostgreSQL).
- User asks to create a new serializer (e.g., MessagePack, Protobuf).
- Code review of an existing provider or serializer against repository conventions.
Checklist
For a new Serializer
Implement
ISerialize(src/NEventStore/Serialization/ISerialize.cs):public interface ISerialize { void Serialize<T>(Stream output, T graph) where T : notnull; T? Deserialize<T>(Stream input); }Create a dedicated project under
src/NEventStore.Serialization.<Name>/:- Target
netstandard2.0to stay consistent with the core library. - Set
GenerateAssemblyInfo=false(version is managed by GitVersion). - Add
PackageId,Authors,Description,PackageTagsmatching the existing.csprojfiles. - Include
icon.png,license.txt, andReadme.mdas pack items pointing to the root files. - Enable
GenerateDocumentationFile,IncludeSymbols,SymbolPackageFormat=snupkg. - Reference
NEventStore.Core.csproj.
- Target
Write the wireup extension in a class
<Name>SerializationWireupExtension:public static class <Name>SerializationWireupExtension { public static SerializationWireup Using<Name>Serialization( this PersistenceWireup wireup, /* optional settings */) { return wireup.UsingCustomSerialization(new <Name>Serializer(/* ... */)); } }- Return
SerializationWireup, notPersistenceWireup, so.Compress()and.EncryptWith()remain available.
- Return
Known-types pattern: if your serializer needs explicit type registration (like the JSON serializer does for
List<EventMessage>andDictionary<string, object>), accept them via aparams Type[]? knownTypesparameter.Logging: inject
LogFactory.BuildLogger(typeof(MySerializer))and guard every log call withLogger.IsEnabled(LogLevel.X)before formatting the message. Never useConsole.Write*.Tests: create
src/NEventStore.Serialization.<Name>.Tests/using the sameDefineConstants=NUNITpattern. RunNEventStore.Persistence.AcceptanceTests.SerializationTestsagainst the new serializer to verify round-trip correctness.
For a new Persistence Provider
Implement
IPersistStreams(src/NEventStore/Persistence/IPersistStreams.cs), which composesIPersistStreamsSyncandIPersistStreamsAsync. Both sides must be implemented.Create a dedicated project (same conventions as a serializer project above).
Wireup extension on
Wireup, returningPersistenceWireup:public static PersistenceWireup Using<Name>Persistence(this Wireup wireup, /* connection options */) { wireup.Register<IPersistStreams>(new <Name>PersistenceEngine(/* ... */)); return new PersistenceWireup(wireup); }Acceptance tests: reference
NEventStore.Persistence.AcceptanceTestsand provide aPersistenceEngineFixturethat wires up your engine. AllPersistenceEngineConcern-based tests must pass green against your implementation.Thread safety:
IPersistStreamsmust be multi-thread safe. Use thread-safe collections or locking; document any remaining single-threaded constraints in XML doc comments.Initialize()must be synchronous and complete before the method returns. The wireup calls it duringBuild().Checkpoint tokens: must be monotonically increasing and comparable. Do not use random GUIDs as checkpoint values.
Review Checklist
-
ISerialize/IPersistStreamsfully implemented (noNotImplementedExceptionstubs left). - Wireup extension returns correct
Wireupsubclass (SerializationWireuporPersistenceWireup). -
GenerateAssemblyInfo=falsein.csproj. -
icon.png,license.txt,Readme.mdincluded as pack items. - All public types have XML doc comments.
- Nullable reference types respected (
?annotations correct). -
Guard.NotNullused at public boundaries. - Logging uses
LogFactory.BuildLoggerwithIsEnabledguards. - Acceptance tests pass (serialization round-trip or persistence contract).
-
dotnet build ./src/NEventStore.Core.sln -c Release --no-restoresucceeds. -
dotnet test ./src/NEventStore.Core.sln -c Release --no-buildpasses.