0.0.2 • Published 11 months ago

@spikedpunch/stacks v0.0.2

Weekly downloads
-
License
ISC
Repository
-
Last release
11 months ago

stacks

A data modeling system that allows you to separate your data from your logic. Define your data structures (Models) in your Stack, and use them throughout your codebase as you normally would. A single line of code selects your storage/caching options. Create, query, and delete the data objects as you use them,

Install

npm install @spikedpunch/stacks

Usage

Basic

import { Stack, StackObject } from '@spikedpunch/stacks'
import { PostgresPlugin } from '@spikedpunch/stacks-postgres'

// Create a new Stack
let stack = Stack.create()

// Add multiple backends or caches - even at the same time
await stack.use(new PostgresPlugin({
   database: "data",
   host: "localhost,
   port: 5432
   user: "admin",
   password: "admin"
}))

// Define Types as usual, just extend the type with
// the StackObject type
type SodaPop = {
   name: string
   cost: number
   diet: boolean
} & StackObject

// Models represent the shape of the data. Creating a Model
// allows you to create objects off that Model. The values
// that are passed in become the default values for new objects.
let Soda = await stack.create.model('soda', {
   name: '',
   cost: 5,
   diet: false
})

// create() creates an object with default values
let coke = await Soda.create<SodaPop>()
coke.name = 'coke'

// Optionally pass in new values
let pepsi = await Soda.create<SodaPop>({
   name: 'pepsi',
   cost: 6
})

for await(let soda of Soda.getAll<SodaPop>()) {
   // prints:
   //    coke
   //    pepsi
   console.log(soda.name)
}

// Deletes the object
await Soda.delete(pepsi)

Advanced

let stack = Stack.create()

let redis = new RedisPlugin()
let dynamo = new DynamoPlugin()

// Plugins listen for events and respond to them
await stack.use(redis)
await stack.use(dynamo)

type SodaPop = {
   name: string
   cost: number
   diet: boolean
} & StackObject

let Soda = await stack.create.model('soda', {
   name: '',
   cost: 0,
   diet: false
})

// Models can contain metadata, called Symbols, to help configure
// plugin specific options.
Soda.symbols.push(...[
   { name: 'dynamo:partitionKeyField', value: 'cost' },
   { name: 'dynamo:partitionKeyType', value: 'number' }
])

let Vendor = await stack.create.model('vendor', {
   soda: Soda,    // To set a reference to a Soda Type
   anotherWayToSoda: ({ ref }) => ref(Soda.name),  // Same as the previous
   sodas: [Soda], // To set a reference to an Array of Sodas
})

// Bootstrap is called after all Models have been created.
// This allows Plugins to run any initialization code based
// on the Models that have been defined in the Stack.
await stack.bootstrap()

let coke = await Sode.create()

coke.cost = 100

// This SodaPop object is saved in Redis and Dynamo
// If the save() fails for any plugin, the save()
// is rolled back across all plugins.
await coke.save()

// Redis will be the first plugin to receive this event
// It will supply the value. The Dynamo plugin will notice
// the value is already provided, and not pull the value from
// the DB.
coke = await Soda.get(coke.id)

Models

Creating Models

When creating Models, a unique name is passed in, as well as the set of parameters. The values that are set when creating the Model, become the default values for any Objects that are created from them.

When setting the values for a Model, the system needs to know the Type and Value that is being set. For string, number, boolean, Stacks can infer the Type. For more complex Objects, like References to other Models, we need to specify the Type and Value.

let model = await stack.create.model('name', {
   // The Type information can be infered on these Types
   string: `I'm a string`,
   int: -1,
   uint: 12,
   bool: true,
   // Arrays
   stringArray: ['']    // TODO: Need a better way to set default arrays
   // consider
   stringArray: ({ string }) => [string]
})

Events

EventDescription
bootstrapRaised when bootstrap() is called
get-many-objects

export enum EventSet { Bootstrap = 'bootstrap', GetManyObjects = 'get-many-objects', GetModel = 'get-model', GetObject = 'get-object', GetStoreContext = 'get-store-context', HasId = 'has-id', ModelCreated = 'model-created', ModelDeleted = 'model-deleted', ModelUpdated = 'model-updated', ObjectCreated = 'object-created', ObjectDeleted = 'object-deleted', ObjectUpdated = 'object-updated', ObjectSaved = 'object-saved' }

Custom Queries

Most likely there will come a time when you'll want to customize your queries outside of what stacks provides. You can set a custom Query Object on the stack that can be retrieved anywhere you need.

Each stack has a setQuery<T>(query: T): void and getQuery<T>(): T | undefined