Skip to content

Reference

Locked decisions

The design decisions that fix go-sequent's behaviour: SyncInTxn ledger projections, decimal money, hash-chain integrity, shared-schema tenancy, tombstone GDPR, Postgres outbox, pure library and MIT licensing.

Locked decisions

These choices were settled before implementation and the code follows them. The rest of the documentation assumes them. If you need something that contradicts one, go-sequent is probably the wrong tool, or you will be maintaining a fork.

The ten decisions

# Decision Meaning
Q1 Ledger projections run in SyncInTxn Balances update in the write transaction
Q2 Money is decimal types.Money, and float64 is banned
Q3 Integrity is a hash chain Per aggregate; no event signatures
Q4 Tenancy is shared schema plus tenant_id No schema-per-tenant or database-per-tenant
Q5 GDPR is tombstone only Not crypto-shredding
Q6 The outbox is Postgres table polling No LISTEN/NOTIFY or broker in the framework
Q7 Pure library No HTTP or gRPC in the repository
Q8 Snapshots are JSON Versioned independently
Q9 Never import Ruby Sequent databases Conceptual inspiration only
Q10 Open source under the MIT license Public repository and documentation

Q1: SyncInTxn for ledger projections

Projectors and workflows run inside the write transaction. A failure rolls everything back. The result is that balances never lag the event that caused them, and a client reads its own write.

The cost is that projector work counts against write latency, and a slow or broken projector blocks writes. Keep ledger projectors small and deterministic. Asynchronous delivery exists through the outbox, for non-ledger paths. See Publisher.

Q2: Decimal money

Money is a decimal amount with a currency, serialized as a string. The framework does not use integer minor units. float64 is banned in money fields, and testkit.BanFloat64Money and types.AssertNoFloatMoney check for it. Rounding policy belongs to your domain, and historical amounts are never recomputed from floats. See Money.

Q3: Hash chain, no signatures

Each aggregate stream has a SHA-256 chain over canonical event fields. Tampering is detectable on load or with VerifyChain. Event signatures are out of scope. Database access control, encrypted backups and optional WORM storage are complementary controls at the operations layer. See EventStore and Threat model.

Q4: Shared schema and tenant_id

One set of tables. Every command, aggregate, idempotency row and projection carries a tenant. Unique-key scopes include the tenant. partition_key can map to tenant for physical partitioning inside the same schema. Row-level security is optional and left to you. See Tenancy and Partitioning.

Q5: Tombstone-only GDPR

To honour an erasure request, emit a tombstone event such as gdpr.SubjectTombstoned and have projectors scrub or stop serving the personal data. Events stay immutable. Crypto-shredding is not provided. Physical deletion of event rows is an exceptional operation that needs stream-repair tooling and breaks the hash chain unless handled deliberately. The optional PayloadProtector supports field encryption of PII without implying anything about erasure or authenticity.

Q6: Postgres outbox polling

When you use an outbox, messages are rows in a Postgres table written in the write transaction and drained by polling with Claim, Ack and Nack. Delivery is at-least-once, and consumers must be idempotent. See Publisher.

Q7: Pure library

go-sequent ships no HTTP server, no gRPC stubs, no UI and no ORM. The CLI is an ops tool for migrations, replay schema stages and outbox polling. You embed command.Service in your own process.

Q8: JSON snapshots

Snapshot payloads are JSON with a SnapshotVersion that you bump on incompatible changes. See Snapshotting.

Q9: Never import Ruby Sequent databases

The conceptual model comes from Sequent. The schema, serialization and APIs do not. There is no dump or wire compatibility, and none is planned. Upcasters exist for your own event history only. See vs Sequent (Ruby).

Q10: Open source, MIT license

The project is open source under the MIT license, and the documentation is public. Contributions and issues are welcome at github.com/go-sequent/go-sequent.

Baseline decisions (D1 to D10)

ID Decision
D1 Module path is github.com/go-sequent/go-sequent
D2 PostgreSQL is the first and only production event store backend
D3 Default publish mode is sync projectors in the write transaction
D4 The UnitOfWork is context-scoped, not process-global
D5 JSON is the default codec for commands and events
D6 Compensating events over mutable history for business corrections
D7 float64 banned in money fields; money type is decimal
D8 Explicit handler registration, with no mandatory reflection auto-registration
D9 Design before implementation
D10 Conceptual Sequent inspiration only, never a Ruby Sequent database import

Principles behind them

  1. Events are the source of truth. Projections are disposable.
  2. Aggregates do not read other aggregates’ state while handling a command.
  3. A command, its events and its synchronous projections share one database transaction. Asynchronous modes are opt-in.
  4. Finance defaults are on: decimal money, idempotency keys, audit metadata on every command.
  5. Use plain Go: interfaces, constructors, context.Context, and no global mutable singleton.

Docs track the go-sequent library. When APIs drift, the source wins.