Designing XDB API
In this article, we will build on developer experience described in Building XDB and define the XDB Go API.
XDB has three primary abstractions: Key, Attribute and Value.
Key
Key is a unique reference to an node. Key is made up of two parts: Kind and ID. Kind is a string that identifies the type of node. ID is a string that uniquely identifies an node within a kind.
A Key
is an immutable struct instantiated with kind
and id
or by parsing an encoded key.
key := xdb.NewKey("User", "123")
// or
key := xdb.MustParseKey("User:123")
Attribute
Attribute is a named property of an node. Attribute is made up of three parts: Key, Name and Value. Key is a unique identifier for an node. Name is a string that identifies the attribute. Value is byte encoded value of the attribute.
Attribute
is shortened to Attr
in the type definitions, function names and method names. This reduces the line length and makes the code more readable.
key := xdb.NewKey("User", "123")
name := xdb.NewAttr(key, "name", xdb.String("Ravi")
expireAt := xdb.ExpireAt(time.Now().Add(24 * time.Hour))
muteAlerts := xdb.NewAttr(key, "mute_alerts", xdb.Bool(true), expireAt)
Value
A value is a byte encoded value of an attribute. API provides functions to encode and decode values for common types.
nameAttr := xdb.NewAttr(key, "name", String("Ravi"))
// Value() returns the byte encoded value
nameAttr.Value() // []byte("Ravi")
// String() decodes the value as string
nameAttr.String() // "Ravi"
Similarly, there are functions to encode and decode values for other types.
attr := xdb.NewAttr(key, "age", xdb.Int(20))
attr.Int() // 20
attr := xdb.NewAttr(key, "height", xdb.Float(5.7))
attr.Float() // 5.7
attr := xdb.NewAttr(key, "active", xdb.Bool(true))
attr.Bool() // true
attr := xdb.NewAttr(key, "last_seen", xdb.Time(time.Now()))
attr.Time() // time.Time("2023-05-05T00:00:00Z")
Store APIs
The APIs are broken down into two interfaces: Store
and Query
.
All storage implementations must implement the Store
interface. Store
interface provides methods to get, put and delete attributes individually or in bulk.
type Store interface {
Get(ctx context.Context, attrs ...*Attr) ([]*Attr, error)
Put(ctx context.Context, attrs ...*Attr) error
Delete(ctx context.Context, attrs ...*Attr) error
}
For Get
and Delete
methods, a partial Attr
can be passed. The Attr
must have the Key
and Name
fields set. The Value
field is ignored.
key := xdb.NewKey("User", "123")
nameAttr := xdb.NewAttr(key, "name")
attrs, err := store.Get(ctx, nameAttr)
We will defer the definition of Query
interface to later.