Documentation
¶
Overview ¶
Package shift provides the persistence layer for a simple "finite state machine" domain model with validation, explicit fields and reflex events per state change.
shift.NewFSM builds a FSM instance that allows specific mutations of the domain model in the underlying sql table via inserts and updates. All mutations update the status of the model, mutates some fields and inserts a reflex event. Note that FSM is opinionated and has the following restrictions: only a single insert status, no transitions back to insert status, only a single transition per pair of statuses.
shift.NewArcFSM builds a ArcFSM instance which is the same as an FSM but without its restrictions. It supports arbitrary transitions.
Index ¶
- Variables
- func NewArcFSM(events eventInserter[int64], opts ...option) arcbuilder
- func NewFSM(events eventInserter[int64], opts ...option) initer[int64]
- func NewGenFSM[T primary](events eventInserter[T], opts ...option) initer[T]
- func TestFSM(_ testing.TB, dbc *sql.DB, fsm *FSM) error
- func WithMetadata() option
- func WithValidation() option
- type ArcFSM
- func (fsm *ArcFSM) Insert(ctx context.Context, dbc *sql.DB, st Status, inserter Inserter[int64]) (int64, error)
- func (fsm *ArcFSM) InsertTx(ctx context.Context, tx *sql.Tx, st Status, inserter Inserter[int64]) (int64, rsql.NotifyFunc, error)
- func (fsm *ArcFSM) IsValidTransition(from Status, to Status) bool
- func (fsm *ArcFSM) Update(ctx context.Context, dbc *sql.DB, from, to Status, updater Updater[int64]) error
- func (fsm *ArcFSM) UpdateTx(ctx context.Context, tx *sql.Tx, from, to Status, updater Updater[int64]) (rsql.NotifyFunc, error)
- type FSM
- type GenFSM
- func (fsm *GenFSM[T]) Insert(ctx context.Context, dbc *sql.DB, inserter Inserter[T]) (T, error)
- func (fsm *GenFSM[T]) InsertTx(ctx context.Context, tx *sql.Tx, inserter Inserter[T]) (T, rsql.NotifyFunc, error)
- func (fsm *GenFSM[T]) IsValidTransition(from Status, to Status) bool
- func (fsm *GenFSM[T]) Update(ctx context.Context, dbc *sql.DB, from Status, to Status, updater Updater[T]) error
- func (fsm *GenFSM[T]) UpdateTx(ctx context.Context, tx *sql.Tx, from Status, to Status, updater Updater[T]) (rsql.NotifyFunc, error)
- type Inserter
- type MetadataInserter
- type MetadataUpdater
- type Status
- type Updater
- type ValidatingInserter
- type ValidatingUpdater
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidStateTransition = errors.New("invalid state transition", j.C("ERR_be8211db784bfb67"))
ErrInvalidStateTransition indicates a state transition that hasn't been registered with the FSM.
var ErrInvalidType = errors.New("invalid type", j.C("ERR_baf1a1f2e99951ec"))
ErrInvalidType indicates that the provided request type isn't valid, and can't be used for the requested transition.
var ErrRowCount = errors.New("unexpected number of rows updated", j.C("ERR_fcb8af57223847b1"))
ErrRowCount is returned by generated shift code when an update failed due unexpected number of rows updated (n != 1). This is usually due to the row not being in the expected from state anymore.
var ErrUnknownStatus = errors.New("unknown status", j.C("ERR_198a4c2d8a654b17"))
ErrUnknownStatus indicates that the status hasn't been registered with the FSM.
Functions ¶
func NewArcFSM ¶
func NewArcFSM(events eventInserter[int64], opts ...option) arcbuilder
NewArcFSM returns a new ArcFSM builder.
func NewGenFSM ¶
func NewGenFSM[T primary](events eventInserter[T], opts ...option) initer[T]
NewGenFSM returns a new FSM initer. The type T should match the type of the user table's primary key.
func TestFSM ¶
TestFSM tests the provided FSM instance by driving it through all possible state transitions using fuzzed data. It ensures all states are reachable and that the sql queries match the schema.
func WithMetadata ¶
func WithMetadata() option
WithMetadata provides an option to enable event metadata with an FSM.
func WithValidation ¶
func WithValidation() option
WithValidation provides an option to enable insert/update validation.
Types ¶
type ArcFSM ¶
type ArcFSM struct {
// contains filtered or unexported fields
}
ArcFSM is a defined Finite-State-Machine that allows specific mutations of the domain model in the underlying sql table via inserts and updates. All mutations update the status of the model, mutates some fields and inserts a reflex event.
ArcFSM doesn't have the restriction of FSM and can be defined with arbitrary transitions.
func (*ArcFSM) IsValidTransition ¶
IsValidTransition validates status transition without committing the transaction
type GenFSM ¶
type GenFSM[T primary] struct {
// contains filtered or unexported fields
}
GenFSM is a defined Finite-State-Machine that allows specific mutations of the domain model in the underlying sql table via inserts and updates. All mutations update the status of the model, mutates some fields and inserts a reflex event.
The type of the GenFSM is the type of the primary key used by the user table.
Note that this FSM is opinionated and has the following restrictions: only a single insert status, no transitions back to insert status, only a single transition per pair of statuses.
func (*GenFSM[T]) IsValidTransition ¶
IsValidTransition validates status transition without committing the transaction
type Inserter ¶
type Inserter[T primary] interface { // Insert inserts a new row with status and returns an id or an error. Insert(ctx context.Context, tx *sql.Tx, status Status) (T, error) }
Inserter provides an interface for inserting new state machine instance rows.
type MetadataInserter ¶
type MetadataInserter[T primary] interface { Inserter[T] // GetMetadata returns the metadata to be inserted with the reflex event for the insert. GetMetadata(ctx context.Context, tx *sql.Tx, id T, status Status) ([]byte, error) }
MetadataInserter extends inserter with additional metadata inserted with the reflex event.
type MetadataUpdater ¶
type MetadataUpdater[T primary] interface { Updater[T] // GetMetadata returns the metadata to be inserted with the reflex event for the update. GetMetadata(ctx context.Context, tx *sql.Tx, from Status, to Status) ([]byte, error) }
MetadataUpdater extends updater with additional metadata inserted with the reflex event.
type Status ¶
Status is an individual state in the FSM.
The canonical implementation is:
type MyStatus int func (s MyStatus) ShiftStatus() int { return int(s) } func (s MyStatus) ReflexType() int { return int(s) } const ( StatusUnknown MyStatus = 0 StatusInsert MyStatus = 1 )
type Updater ¶
type Updater[T primary] interface { // Update updates the status of an existing row returns an id or an error. Update(ctx context.Context, tx *sql.Tx, from Status, to Status) (T, error) }
Updater provides an interface for updating existing state machine instance rows.
type ValidatingInserter ¶
type ValidatingInserter[T primary] interface { Inserter[T] // Validate returns an error if the insert is not valid. Validate(ctx context.Context, tx *sql.Tx, id T, status Status) error }
ValidatingInserter extends inserter with validation. Assuming the majority validations will be successful, the validation is done after event insertion to allow maximum flexibility sacrificing invalid path performance.
type ValidatingUpdater ¶
type ValidatingUpdater[T primary] interface { Updater[T] // Validate returns an error if the update is not valid. Validate(ctx context.Context, tx *sql.Tx, from Status, to Status) error }
ValidatingUpdater extends updater with validation. Assuming the majority validations will be successful, the validation is done after event insertion to allow maximum flexibility sacrificing invalid path performance.