Reference
Wiring the façade
How to assemble a go-sequent application with the sequent package: sequent.New, options such as WithStore, WithIdempotency, WithAuthorizer and WithObs, Register methods, and middleware order.
Wiring the façade
Package: sequent
The sequent package builds a ready-to-use App: event registry, upcaster chain, command router, repository, publisher and command.Service. Your main configures it and registers your domain.
What New builds
app := sequent.New(sequent.Config{PublisherMode: sequent.SyncInTxn},
sequent.WithStore(store, tx),
sequent.WithAuthorizer(auth),
sequent.WithIdempotency(idemp, false),
sequent.WithObs(inst),
)
App fields:
| Field | Type | Role |
|---|---|---|
Config |
sequent.Config |
Options. PublisherMode is SyncInTxn, the only mode and the default. |
Events |
*event.Registry |
Event type registry |
Upcasters |
*upcast.Chain |
Payload upcasters |
Commands |
*command.Router |
Command to handler routing |
Repo |
*aggregate.Repository |
Loads and commits aggregates |
Publisher |
*publish.SyncInTxn |
Projectors and workflows |
Service |
*command.Service |
The entry point: Service.Execute |
Obs |
*obs.Instruments |
Optional telemetry |
Options
| Option | Effect |
|---|---|
WithStore(store, tx) |
Sets Repo.Store, Service.Tx, and Repo.Publisher. If the store also implements eventstore.SnapshotStore, sets Repo.Snapshots. |
WithAuthorizer(auth) |
Appends security.Filter{Auth: auth} to Service.Filters. See AuthZ. |
WithIdempotency(store, strict) |
Appends idempotency middleware to Service.Middleware. See Idempotency. |
WithObs(inst) |
Sets App.Obs and Service.Obs. See OpenTelemetry. |
Options apply in the order you pass them. Middleware and filters append, so order matters.
Registering your domain
bank.RegisterEvents(app.Events)
app.RegisterAggregate("bank.Account", func(id uuid.UUID) aggregate.Root { return bank.NewAccount(id) })
app.RegisterHandler(&bank.Handler{Repo: app.Repo})
app.RegisterProjector(balanceProjector)
app.RegisterWorkflow(notifyWorkflow)
app.RegisterUpcaster(myUpcaster)
| Method | Purpose |
|---|---|
RegisterAggregate(type, factory) |
Lets the repository rebuild aggregates by type name |
RegisterHandler(h) |
Maps each command type in h.Handles() to h. Panics on a duplicate type. |
RegisterProjector(h) |
Registers on SyncInTxn. Included in projection rebuild. |
RegisterWorkflow(w) |
Registers on SyncInTxn. Excluded from rebuild. |
RegisterUpcaster(u) |
Adds to app.Upcasters |
All registration is explicit. There is no reflection-based auto-registration.
The store and the registry
The store needs the app’s event registry to decode events, and New creates that registry. So a common sequence is:
app := sequent.New(sequent.Config{PublisherMode: sequent.SyncInTxn})
bank.RegisterEvents(app.Events)
store, err := postgres.Open(ctx, dsn, app.Events)
if err != nil { /* ... */ }
if err := store.Migrate(ctx); err != nil { /* ... */ }
store.SetUpcasters(app.Upcasters)
app.Repo.Store = store
app.Repo.Snapshots = store
app.Repo.Publisher = app.Publisher
app.Service.Tx = store
app.Service.Repo = app.Repo
WithStore does the first four assignments for you when you already hold a store. The bank demo takes a StoreFactory that receives app.Events, so the store always shares the registry:
type StoreFactory func(reg *event.Registry) (eventstore.Store, eventstore.TxManager, eventstore.SnapshotStore, error)
Middleware order
Service.Middleware runs outer-first, and Service.Filters run after the whole chain. A typical order:
tenancy.Middleware: setsMeta.TenantIDfrom the context.- Optionally
security.Middleware(auth), if you need AuthZ on replays too. - Idempotency middleware: needs the tenant.
app.Service.Middleware = append(
[]command.Middleware{tenancy.Middleware},
app.Service.Middleware...,
)
Filters (from WithAuthorizer) then run before Validate.
Running a command
ctx = tenancy.WithTenant(ctx, tenantID)
err := app.Service.Execute(ctx, &bank.Deposit{AccountID: id, Amount: types.MustMoney("10.00", "CNY")})
Bank helper
bank.NewDemoApp(bank.DemoOptions{...}) does all of the above for the bank sample, and returns a DemoApp with App, Projector and Workflow. Read examples/bank/app.go as a worked example of the whole wiring. See Bank example.
No transport
The façade does not start servers. Call Service.Execute from your own HTTP, gRPC or queue handlers.
Related
Docs track the go-sequent library. When APIs drift, the source wins.