@lickle/streamkit
v0.0.1
Published
Minimal service coordination system with event-driven architecture and reactive streams
Maintainers
Readme
@lickle/streamkit
Minimal service coordination system with event-driven architecture and reactive streams.
Install
npm install @lickle/streamkitQuick Start
import { createRegistry } from '@lickle/streamkit'
// Define event types
type Events = {
'user.created': { id: string; name: string }
'user.deleted': { id: string }
}
type Context = { db: Database }
// Create registry and register services
const registry = createRegistry<Events, Context>()
registry.register(userService)
registry.register(loggingService)
// Start system with context
const system = registry.start({ db: myDatabase })
// Emit events
system.emit('user.created', { id: '123', name: 'Alice' })
// Clean up
system.stop()Core Concepts
Events
Events are typed messages that flow through the system. Services emit events and observe events from other services.
type EventMap = {
'data.updated': { id: string; value: number }
'task.completed': { taskId: string }
}Services
Services are self-contained units that communicate through events. Services can observe events, emit events, expose APIs, and depend on other services.
import { Service, on, toEvent } from '@lickle/streamkit'
import { map } from '@lickle/rx'
import { pipe } from '@lickle/rx/util'
const userService: Service<
Events,
Context,
{ name: 'users'; outbound: 'user.created' | 'user.deleted' }
> = {
name: 'users',
observer: pipe(
on('user.created'),
map((user) => {
console.log('User created:', user)
return toEvent('user.created')(user)
}),
),
}System
The system manages service lifecycle and coordinates event flow. It provides a shared event bus and context.
const system = createSystem<Events, Context>({ db: myDatabase })
// Register services
system.register(userService)
system.register(loggingService)
// Subscribe to all events
system.events((event) => {
console.log(event.type, event.data)
})Service Definition
Services specify their event subscriptions, emissions, and dependencies through a type definition:
type ServiceDef = {
name: 'my-service'
inbound: 'user.created' // Events this service observes
outbound: 'email.sent' // Events this service emits
dependencies: { name: 'mailer'; api: MailerAPI } // Services this depends on
}Service Runtime
Services receive a runtime environment with typed access to events and dependencies:
const emailService: Service<
Events,
Context,
{
name: 'email'
inbound: 'user.created'
outbound: 'email.sent'
dependencies: { name: 'mailer'; api: MailerAPI }
}
> = {
name: 'email',
observer: pipe(
on('user.created'),
map((user) => {
// Access dependency
const mailer = runtime.service('mailer')
mailer.send(user.email, 'Welcome!')
return toEvent('email.sent')({ userId: user.id })
}),
),
}Service APIs
Services can expose APIs for direct service-to-service interaction:
type LoggerAPI = {
log: (message: string) => void
}
const logger: Service<Events, Context, { name: 'logger' }, LoggerAPI> = {
name: 'logger',
api: {
log: (message) => console.log(message),
},
}
// Other services can access the API
const otherService: Service<
Events,
Context,
{ name: 'other'; dependencies: { name: 'logger'; api: LoggerAPI } }
> = {
name: 'other',
run: (runtime) => {
const logger = runtime.service('logger')
logger.log('Hello from other service')
},
}Registry Pattern
Use the registry pattern to define services before system initialization:
import { createRegistry } from '@lickle/streamkit'
const registry = createRegistry<Events, Context>()
// Register services in order
registry.register(coreService)
registry.register(dataService)
registry.register(uiService)
// Start with context
const system = registry.start({ db, config })
// Stop all services
system.stop()Helpers
toEvent
Creates an event object from type and data:
const emit = toEvent('user.created')
return emit({ id: '123', name: 'Alice' })on
Observes a specific event type in reactive service definitions:
observer: pipe(
on('user.created'),
map((user) => console.log('User:', user)),
)License
MIT © Dan Beaven
