Upgrading to v0.9
goes v0.9.0 migrates the MongoDB backend to version 2 of the official MongoDB Go driver and deprecates the natsjs backend.
go.mongodb.org/mongo-driver → go.mongodb.org/mongo-driver/v2No data migration is required. The MongoDB backend keeps its database names, collections, indexes, and stored BSON fields — existing data remains fully usable after the upgrade.
That storage compatibility does not make application-defined BSON codecs source-compatible. Custom value codec method signatures must still be updated as described below.
Who Is Affected?
| If you... | Then... |
|---|---|
don't use the MongoDB or natsjs backend | upgrade goes — nothing else to do |
use the MongoDB backend only through goes options like mongo.URL(...) | upgrade goes and run go mod tidy |
| exchange MongoDB driver types with goes — clients, options, ObjectIDs, sessions, collections | migrate your driver usage — see below |
implement MarshalBSONValue or UnmarshalBSONValue | update the method signatures and verify the stored BSON type — see below |
use the natsjs backend | plan a move to a supported event store — see natsjs deprecation |
Let an AI agent do it
This migration is well-suited for an AI coding agent. Give the agent this page and the MongoDB migration guide linked below, then ask it to update the driver dependency and affected APIs, search for custom BSON codec implementations, run go mod tidy, compile all code behind the mongo build tag, run the test suite, and review the resulting diff.
MongoDB: Migrating to Driver v2
MongoDB maintains a comprehensive Go Driver 2.0 migration guide. The sections below cover the changes most relevant to goes applications.
1. Upgrade the modules and imports
go get github.com/modernice/goes@v0.9.0 go.mongodb.org/mongo-driver/v2@v2.8.0
go mod tidyAdd /v2 to every driver import:
// Before
import "go.mongodb.org/mongo-driver/mongo"
// After
import "go.mongodb.org/mongo-driver/v2/mongo"The same applies to packages such as bson, mongo/options, and mongo/readpref.
2. Update direct mongo.Connect calls
mongo.Connect no longer accepts a context.Context:
// Before
client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
// After
client, err := mongo.Connect(options.Client().ApplyURI(uri))This only affects direct calls to the driver. The backend's (*EventStore).Connect(ctx, opts...) and (*SnapshotStore).Connect(ctx) methods still accept a context.
3. Replace primitive.ObjectID with bson.ObjectID
The former bson/primitive package was merged into bson:
// Before
import "go.mongodb.org/mongo-driver/bson/primitive"
id := primitive.NewObjectID()
var modelID primitive.ObjectID
// After
import "go.mongodb.org/mongo-driver/v2/bson"
id := bson.NewObjectID()
var modelID bson.ObjectIDThis is especially relevant when bson.ObjectID is the ID type of a model repository.
4. Update custom BSON value codecs
Driver v2 changed the bson.ValueMarshaler and bson.ValueUnmarshaler interfaces to represent the BSON type as a byte:
type ValueMarshaler interface {
MarshalBSONValue() (typ byte, data []byte, err error)
}
type ValueUnmarshaler interface {
UnmarshalBSONValue(typ byte, data []byte) error
}In v1 these methods used bsontype.Type. Change the signatures to byte, then convert at calls that still use bson.Type:
func (v Value) MarshalBSONValue() (byte, []byte, error) {
typ, data, err := bson.MarshalValue(v.String())
return byte(typ), data, err
}
func (v *Value) UnmarshalBSONValue(typ byte, data []byte) error {
return bson.UnmarshalValue(bson.Type(typ), data, &v.value)
}WARNING
Do not use bson.Type in these method signatures. Although its underlying type is byte, it is a distinct named type, so the methods no longer implement the driver interfaces. Code can still compile while the driver silently falls back to its default encoding or decoding behavior.
Add compile-time assertions so future driver upgrades cannot silently disable the custom codec:
var (
_ bson.ValueMarshaler = Value{}
_ bson.ValueUnmarshaler = (*Value)(nil)
)Test both the exact stored BSON type and decoding data in its existing on-disk shape. A round-trip test alone can miss this regression when the default codec can encode and decode the same unintended representation.
5. Update sessions and transaction callbacks
In v2, mongo.Session is a struct used through *mongo.Session, and session callbacks receive a standard context.Context. Retrieve the session with mongo.SessionFromContext:
err := client.UseSession(ctx, func(ctx context.Context) error {
session := mongo.SessionFromContext(ctx)
if err := session.StartTransaction(); err != nil {
return err
}
// Use ctx for operations that belong to the session.
return session.CommitTransaction(ctx)
})Accordingly, Transaction.Session() from the MongoDB backend now returns *mongo.Session. Transaction hooks still receive mongo.TransactionContext.
6. Update query option interceptors
Driver v2 represents operation options as builders. A WithQueryOptions callback now receives and returns *options.FindOptionsBuilder:
store := mongo.NewEventStore(enc,
mongo.WithQueryOptions(func(opts *options.FindOptionsBuilder) *options.FindOptionsBuilder {
return opts.SetBatchSize(500)
}),
)Code that only constructs options with calls such as options.Find().SetLimit(...) keeps the same shape. Code that reads option fields must first apply the builder's setters to the corresponding options value.
7. Decode Distinct results when using the driver directly
Collection.Distinct now returns a result that must be decoded:
var names []string
if err := collection.Distinct(ctx, "name", bson.D{}).Decode(&names); err != nil {
return err
}Checklist
- Replace every
go.mongodb.org/mongo-driver/...import withgo.mongodb.org/mongo-driver/v2/.... - Remove the context argument from direct
mongo.Connectcalls. - Replace
primitive.ObjectIDandprimitive.NewObjectIDwith theirbsonequivalents. - Update every
bson.ValueMarshalerandbson.ValueUnmarshalerimplementation to usebyte, add compile-time interface assertions, and verify the stored BSON type and decoding of existing data. - Change stored or returned sessions to
*mongo.Session. - Replace
mongo.SessionContextcallback parameters withcontext.Contextand usemongo.SessionFromContext. - Update
WithQueryOptionscallbacks to use*options.FindOptionsBuilder. - Run
go mod tidy, then compile and test code behind themongobuild tag.
natsjs Deprecation
The natsjs backend (the JetStream-backed event store) is deprecated in v0.9 and will be removed in a future release. It has architectural flaws that risk data corruption and silent event loss: missing optimistic concurrency control, non-atomic writes, and process-local stream discovery that leaves subscribers blind to aggregate types first created by other services and makes unfiltered queries return incomplete results.
It also scales poorly in larger systems: broker memory grows with the total number of stored events, every insert costs two synchronous round trips, and every aggregate load re-reads the aggregate's full history — snapshots do not reduce the amount of data fetched.
WARNING
Do not start new projects on the natsjs backend. Existing projects should plan a migration.
If you use it:
- Use the MongoDB or PostgreSQL backend as the event store.
- Keep using the core NATS backend as the event bus — it is unaffected.
In v0.9 the deprecation is compile-time only: existing code keeps working, but deprecation notices appear in your editor and in linters such as staticcheck. See the natsjs README for the full list of issues.