https://github.com/theunderscorer/functional-cqrs
CQRS concept in JavaScript/Typescript
https://github.com/theunderscorer/functional-cqrs
command-handlers events-bus functional-cqrs queries-bus
Last synced: about 2 months ago
JSON representation
CQRS concept in JavaScript/Typescript
- Host: GitHub
- URL: https://github.com/theunderscorer/functional-cqrs
- Owner: TheUnderScorer
- Created: 2020-02-01T17:37:32.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2022-12-14T06:00:45.000Z (over 2 years ago)
- Last Synced: 2025-03-28T16:04:41.749Z (3 months ago)
- Topics: command-handlers, events-bus, functional-cqrs, queries-bus
- Language: TypeScript
- Homepage:
- Size: 42 MB
- Stars: 5
- Watchers: 0
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# Functional Cqrs
This library provides functional approach to CQRS (Command Query Responsibility Segregation).
It includes all basic components - Commands Bus, Queries Bus and Events Bus with advanced typescript support.

[](https://coveralls.io/github/TheUnderScorer/functional-cqrs?branch=master)## ✨ Getting Started
### Prerequsites
You will need:
- [nodejs](https://nodejs.org/en/) (>=12.13.1, not tested on older versions, but might work). It should also work in browsers (not tested).
### Installation
`npm install functional-cqrs` or `yarn add functional-cqrs`
## 🚀 Usage
Let's say that we have a simple to-do app:
```ts
import {
Command,
CommandContext,
createCqrs,
Event,
EventContext,
} from 'functional-cqrs';
import { connectToDatabse } from 'your-database-library';interface Task {
id: string;
title: string;
done: boolean;
}// Command name - required for type inference
const AddTask = 'AddTask' as const;// Your command for adding new Task
class AddTaskCommand implements Command {
readonly name = AddTask;constructor(readonly payload: Task) {}
}// Event emitted after Task have been created
class TaskAddedEvent implements Event {
constructor(readonly payload: Task) {}
}// Your handler for adding new Task
const addTaskHandler = (connection: Connection) => async (
command: AddTaskCommand,
// CommandContext includes EventsBus for dispatching events
ctx: CommandContext
) => {
await connection.saveTask(command.payload);await ctx.eventsBus.dispatch(new TaskAddedEvent(command.payload));
// Note that we return created task here
return command.payload;
};const onTaskAdded = (
event: TaskAddedEvent,
// EventContext includes CommandsBus so we can execute commands from here
context: EventContext
) => {
// Log created Task into console
console.log(event.payload);
};// Let's bootstrap our app
const connection = connectToDatabse();
const cqrs = createCqrs({
commandHandlers: {
// Here we create and register our command handler
[AddTask]: addTaskHandler(connection),
},
eventHandlers: {
// Events can have multiple handlers
[TaskAddedEvent.name]: [onTaskAdded],
},
});const task = await cqrs.buses.commandsBus.execute(
new AddTaskCommand({
id: '1',
done: false,
title: 'Test task',
})
);// Typescript knows that this is a "Task"!
console.log(task.id);
```
You can very quickly integrate above example with any kind of application.Now, you might ask:
> Why do we need to set command name as "'AddTask' as const;" and then use it to register the command handler, but we don't do the same for events?In order to get the type inference from Typescript, we need a way to map command to handler - and we use it's name for it.
We have to define it "as const" so Typescript knows it's exact value.We don't do that for events, because they always return `void | Promise` :)
## 📖 Learn more
* [📥 Commands](docs/commands.md)
* [📤 Queries](docs/commands.md)
* [🗓 Events](docs/commands.md)Need more? Check [examples](examples) 😎.