envelope

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 31, 2025 License: MIT Imports: 8 Imported by: 2

README

Envelope

Go Report Card

This Go library is designed to simplify the serialization and deserialization of concrete types into interfaces.

This library is for all those times you've wanted to unmarshal some data, and all you know ahead of time is that it's an interface type.

Example:

type Event interface {
	EventType() string
}

// dozens of Event implementations

var event Event
err := json.Unmarshal(data, &event)
// 💣 json: cannot unmarshal object into Go value of type Event

If you could know ahead of time that the data was a UserCreated event, you could unmarshal it into that type, but when you're working with lists of serialized data, you don't know what type you're going to get.

A pattern that provides a solution is to wrap the concrete type in an envelope that includes the type information.

type Envelope struct {
	Type string
	Data json.RawMessage
}

This library provides an easy-to-use implementation of this pattern.

Example Usage:

type Event interface {
	EventType() string
}

type UserCreated struct {
	FirstName string
	LastName  string
}

// Register the type with the registry
reg := envelope.NewRegistry()
reg.Register(UserCreated{})

// Serialize the type
userCreated := UserCreated{
	FirstName: "John",
	LastName:  "Doe",
}
data, err := reg.Serialize(userCreated)
if err != nil {
	fmt.Println(err)
	return
}

// Deserialize the type
event, err := reg.Deserialize(data)
if err != nil {
	fmt.Println(err)
	return
}

switch e := event.(type) {
case *UserCreated:
	fmt.Println(e.FirstName, e.LastName)
}

Features

  • Serialize and deserialize concrete types into interfaces
  • Simple type registration
  • Safely serialize and deserialize concrete types into databases, message queues, etc.
  • Customizable serialization and deserialization

Usage

Add the Envelope package to your project:

go get github.com/stackus/envelope@latest
Create a Registry

Create a new registry and register the types you want to serialize and deserialize.

reg := envelope.NewRegistry()

You can provide custom Serde implementations for your types and/or for the Envelope type.

// the envelope.Serde interface
type Serde interface {
	Serialize(any) ([]byte, error)
	Deserialize([]byte, any) error
}

reg := envelope.NewRegistry(
	envelope.WithSerde(envelope.JsonSerde{}),
	envelope.WithEnvelopeSerde(envelope.ProtoSerde{}),
)

A JsonSerde and ProtoSerde are provided out of the box.

By default, the JsonSerde is used for the types and the ProtoSerde is used for the envelope.

Use your own custom serde that implements the Serde interface.

Type Registration

Register the types you want to serialize and deserialize.

type UserCreated struct {
	FirstName string
	LastName  string
}

// Register the type with the registry using the types reflected name
reg.Register(UserCreated{})

// Complex types that require some initialization can be registered with a factory function
reg.RegisterFactory(func() any {
	return &UserCreated{
		FirstName: "Unknown",
	}
})

You may also register types with a custom name by adding the following method on the type:

func (UserCreated) EnvelopeKey() string {
	return "myEntity.userCreated"
}
Serialize & Deserialize

With your types registered, you can now serialize and deserialize them.

// UserCreated implements the Event interface
userCreated := UserCreated{
	FirstName: "John",
	LastName: "Doe",
}

// Serialize the user
data, err := reg.Serialize(userCreated)
if err != nil {
	fmt.Println(err)
	return
}

// store the data in a database, message queue, etc. then read it back in a later process

// Deserialize the event
event, err := reg.Deserialize(data)
if err != nil {
	fmt.Println(err)
	return
}
switch e := event.(type) {
case *UserCreated:
	fmt.Println(e.FirstName, e.LastName)
default:
	fmt.Printf("Unknown event type: %T\n", e)
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var File_envelope_proto protoreflect.FileDescriptor

Functions

This section is empty.

Types

type Envelope

type Envelope struct {
	Key     *string `protobuf:"bytes,1,opt,name=getKey" json:"getKey,omitempty"`
	Payload []byte  `protobuf:"bytes,2,opt,name=payload" json:"payload,omitempty"`
	// contains filtered or unexported fields
}

func (*Envelope) Descriptor deprecated

func (*Envelope) Descriptor() ([]byte, []int)

Deprecated: Use Envelope.ProtoReflect.Descriptor instead.

func (*Envelope) GetKey

func (x *Envelope) GetKey() string

func (*Envelope) GetPayload

func (x *Envelope) GetPayload() []byte

func (*Envelope) ProtoMessage

func (*Envelope) ProtoMessage()

func (*Envelope) ProtoReflect

func (x *Envelope) ProtoReflect() protoreflect.Message

func (*Envelope) Reset

func (x *Envelope) Reset()

func (*Envelope) String

func (x *Envelope) String() string

type ErrFactoryDoesNotReturnPointer

type ErrFactoryDoesNotReturnPointer string

func (ErrFactoryDoesNotReturnPointer) Error

type ErrFactoryReturnsNil

type ErrFactoryReturnsNil string

func (ErrFactoryReturnsNil) Error

func (e ErrFactoryReturnsNil) Error() string

type ErrReregisteredKey

type ErrReregisteredKey string

func (ErrReregisteredKey) Error

func (e ErrReregisteredKey) Error() string

type ErrUnregisteredKey

type ErrUnregisteredKey string

func (ErrUnregisteredKey) Error

func (e ErrUnregisteredKey) Error() string

type JsonSerde

type JsonSerde struct{}

JsonSerde is a Serde implementation for JSON

It uses the encoding/json package to serialize and deserialize data.

func (JsonSerde) Deserialize

func (s JsonSerde) Deserialize(data []byte, v any) error

func (JsonSerde) Serialize

func (s JsonSerde) Serialize(v any) ([]byte, error)

type ProtoSerde

type ProtoSerde struct{}

ProtoSerde is a Serde implementation for Protocol Buffers

It uses the google.golang.org/protobuf/proto package to serialize and deserialize data.

func (ProtoSerde) Deserialize

func (s ProtoSerde) Deserialize(data []byte, v any) error

func (ProtoSerde) Serialize

func (s ProtoSerde) Serialize(v any) ([]byte, error)

type Registry

type Registry interface {
	Register(vs ...any) error
	RegisterFactory(fns ...func() any) error
	Serialize(v any) ([]byte, error)
	Deserialize(data []byte) (any, error)
	IsRegistered(v any) bool
	Build(key string) (any, error)
}

func NewRegistry

func NewRegistry(opts ...RegistryOption) Registry

NewRegistry creates a new envelope registry.

The registry is used to register types that can be serialized as concrete types, then deserialized back into their original types without knowing ahead of time what those types are.

type RegistryOption

type RegistryOption func(*registry)

func WithEnvelopeSerde

func WithEnvelopeSerde(serde Serde) RegistryOption

func WithSerde

func WithSerde(serde Serde) RegistryOption

type Serde

type Serde interface {
	Serialize(any) ([]byte, error)
	Deserialize([]byte, any) error
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL