Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/workshops_event_driven_architecture.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Workshops - Event Driven Architecture

on:
# run it on push to the default repository branch
push:
branches: [main]
paths:
- "workshops/event_driven_architecture/**"
# run it during pull request
pull_request:
paths:
- "workshops/event_driven_architecture/**"

defaults:
run:
# relative path to the place where source code (with package.json) is located
working-directory: workshops/event_driven_architecture

jobs:
build-and-test-code:
name: Build application code
# use system defined below in the tests matrix
runs-on: ${{ matrix.os }}

strategy:
# define the test matrix
matrix:
# selected operation systems to run CI
os: [ubuntu-latest] #, windows-latest, macos-latest]
# selected node version to run CI
node-version: [20.10.x]

steps:
- name: Check Out Repo
uses: actions/checkout@v4

- name: Start containers
run: docker compose -f "docker-compose.yml" up -d

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
# use the node version defined in matrix above
node-version: ${{ matrix.node-version }}

- name: Install dependencies
run: npm ci

- name: Run linting (ESlint and Prettier)
run: npm run lint

- name: Build
run: npm run build:ts

- name: Test
run: npm run test:solved
5 changes: 5 additions & 0 deletions workshops/event_driven_architecture/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Editor configuration, see http://editorconfig.org
root = true

[*]
end_of_line = lf
1 change: 1 addition & 0 deletions workshops/event_driven_architecture/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20.11.1
3 changes: 3 additions & 0 deletions workshops/event_driven_architecture/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/dist/
**/lib/
**/cache/
4 changes: 4 additions & 0 deletions workshops/event_driven_architecture/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"tabWidth": 2,
"singleQuote": true
}
7 changes: 7 additions & 0 deletions workshops/event_driven_architecture/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"orta.vscode-jest"
]
}
60 changes: 60 additions & 0 deletions workshops/event_driven_architecture/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug All Tests (Node)",
"type": "node",
"request": "launch",
"skipFiles": ["<node_internals>/**"],
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "test", "--inspect-brk=9229"], // Use --inspect-brk for debugging
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"cwd": "${workspaceFolder}/src/",
"sourceMaps": true
},
{
"name": "Debug Current Test File",
"type": "node",
"request": "launch",
"env": { "DUMBO_LOG_LEVEL": "DEBUG" },
"skipFiles": ["<node_internals>/**"],
"runtimeExecutable": "npm",
"runtimeArgs": [
"run-script",
"test:file",
"--",
"${file}",
"--inspect-brk=9229"
], // Use --inspect-brk for debugging
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"cwd": "${workspaceFolder}/src/",
"sourceMaps": true
},
{
"name": "Jest all tests",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
"args": ["--verbose", "-i", "--no-cache", "--watchAll"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"name": "Jest current test",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
"args": [
"${fileBasenameNoExtension}",
"--verbose",
"-i",
"--no-cache",
"--watchAll"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
58 changes: 58 additions & 0 deletions workshops/event_driven_architecture/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": false,
"editor.formatOnSave": true,

"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit",
"source.addMissingImports": "always"
},

"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": false,
"editor.formatOnSave": true,

"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit",
"source.addMissingImports": "always"
}
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": false,
"editor.formatOnSave": true,

"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit",
"source.addMissingImports": "always"
}
},

"editor.tabSize": 2,

"files.exclude": {
"node_modules/": true,
"**/node_modules/": true,
"**/dist/": true,
"**/*.tsbuildinfo": true
},
"files.eol": "\n",

"typescript.preferences.importModuleSpecifier": "relative",
"eslint.workingDirectories": ["./"],
"debug.javascript.terminalOptions": {
"nodeVersionHint": 20
},
"nodejs-testing.include": ["./**/**"],
"nodejs-testing.extensions": [
{
"extensions": ["ts", "mts"],
"filePatterns": ["**/*.spec.ts", "**/*.spec.mts"],
"parameters": ["--import", "tsx"]
}
]
}
13 changes: 13 additions & 0 deletions workshops/event_driven_architecture/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build:ts:watch",
"group": "build",
"problemMatcher": [],
"label": "npm: build:ts:watch",
"detail": "tsc -b --watch"
}
]
}
76 changes: 76 additions & 0 deletions workshops/event_driven_architecture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
[<img src="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" height="20px" />](https://www.linkedin.com/in/oskardudycz/) [![Github Sponsors](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&link=https://github.com/sponsors/oskardudycz/)](https://github.com/sponsors/oskardudycz/) [![blog](https://img.shields.io/badge/blog-event--driven.io-brightgreen)](https://event-driven.io/?utm_source=event_sourcing_jvm) [![blog](https://img.shields.io/badge/%F0%9F%9A%80-Architecture%20Weekly-important)](https://www.architecture-weekly.com/?utm_source=event_sourcing_net)

# Introduction to Event Sourcing Self-Paced Kit

Event Sourcing is perceived as a complex pattern. Some believe that it's like Nessie, everyone's heard about it, but rarely seen it. In fact, Event Sourcing is a pretty practical and straightforward concept. It helps build predictable applications closer to business. Nowadays, storage is cheap, and information is priceless. In Event Sourcing, no data is lost.

The workshop aims to build the knowledge of the general concept and its related patterns for the participants. The acquired knowledge will allow for the conscious design of architectural solutions and the analysis of associated risks.

The emphasis will be on a pragmatic understanding of architectures and applying it in practice using EventStoreDB.

You can do the workshop as a self-paced kit. That should give you a good foundation for starting your journey with Event Sourcing and learning tools like EventStoreDB.

**If you'd like to get full coverage with all nuances of the private workshop, check [training page on my blog for more details](https://event-driven.io/en/training/) feel free to contact me via [email](mailto:oskar@event-driven.io).**

Read also more in my article [Introduction to Event Sourcing - Self Paced Kit](https://event-driven.io/en/introduction_to_event_sourcing/?utm_source=event_sourcing_nodejs).

## Exercises

Follow the instructions in exercises folders.

1. [Events definition](./src/01_events_definition/).
2. [Getting State from events](./src/02_getting_state_from_events/).
3. Appending Events:
- [Raw EventStoreDB](./src/03_appending_events_eventstoredb/)
- [Emmett with various storages (PostgreSQL, EventStoreDB, MongoDB)](./src/04_appending_events_emmett/)
4. Getting State from events
- [Raw EventStoreDB](./src/05_getting_state_from_events_eventstoredb/)
- [Emmett with various storages (PostgreSQL, EventStoreDB, MongoDB)](./src/06_getting_state_from_events_emmett/)
5. Business Logic:
- [Writing](./src/07_business_logic/)
- [Testing](./src/08_business_logic/)
6. Application logic:
- [EventStoreDB](./src/09_application_logic_eventstoredb/)
7. Optimistic Concurrency:
- [EventStoreDB](./src/10_optimistic_concurrency_eventstoredb/)
8. Projections:
- [General](./src/11_projections_single_stream/)
- [Idempotency](./src/12_projections_single_stream_idempotency/)
- [Eventual Consistency](./src/13_projections_single_stream_eventual_consistency/)

## Prerequisites

1. Install git - https://git-scm.com/downloads.
2. Clone this repository.
3. Install Node.js 20.10 - https://Node.js.org/en/download/ (or better using NVM).
- if you're using Node.js higher than v20, you can update `tsconfig.json`'s `compilerOptions.lib|target` in order to use newer JavaScript standard APIs, e.g. `Array.fromAsync`
4. Install VSCode, WebStorm or other prefered IDE.
5. Install docker - https://docs.docker.com/engine/install/.
6. Open the current folder in IDE.

## Setup

1. Install NPM packages by running: `npm install`.
2. Build source codes: `npm run build`.
3. If you're using VSCode, you may consider [importing profile](https://code.visualstudio.com/updates/v1_75#_profiles) from the [./.vscode/Node.js.code-profile](./.vscode/Node.js.code-profile) to get all recommended plugins.

## Ensuring that all is setup correctly

1. Run `docker compose up` (or for Mac `docker compose -f docker-compose.arm.yml up`) to start PostgreSQL, EventStoreDB, MongoDB docker images.

2. Run `npm run test:solved`. If all is fine then all tests should be green.

## Running

1. Run: `docker compose up` to start EventStoreDB docker image.You should automatically get:

- EventStoreDB UI: http://localhost:2113/
- Mongo Express UI: http://localhost:8081/
- UI Credentials: login: `admin`, password: `pass`
- PgAdmin: http://localhost:5050/
- UI Credentials: login: `admin@pgadmin.org`, password: `admin`
- Database Credentials: host: `postgres`, login: `postgres`, password: `postgres`

2. You can get build watch by running `npm run build:ts:watch`.
3. To run test for exercises run `npm run test:exercise`. For solutions run `npm run test:solved`, for all `npm run test`.
4. Whe you're working with exercise and want to have tests running on file change run `npm run test:exercise:watch`.
96 changes: 96 additions & 0 deletions workshops/event_driven_architecture/docker-compose.arm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
version: '3'

services:
#######################################################
# EventStoreDB
#######################################################
eventstoredb:
# image: eventstore/eventstore:24.10.0-bookworm-slim
# use this image if you're running ARM-based proc like Apple M1
image: eventstore/eventstore:24.10.0-alpha-arm64v8
environment:
- EVENTSTORE_CLUSTER_SIZE=1
- EVENTSTORE_RUN_PROJECTIONS=All
- EVENTSTORE_START_STANDARD_PROJECTIONS=true
- EVENTSTORE_NODE_PORT=2113
- EVENTSTORE_INSECURE=true
- EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=true
ports:
- '1113:1113'
- '2113:2113'
volumes:
- type: volume
source: eventstore-volume-data
target: /var/lib/eventstore
- type: volume
source: eventstore-volume-logs
target: /var/log/eventstore
networks:
- esdb_network

#######################################################
# MongoDB
#######################################################
mongodb:
image: mongo:6.0.12
# environment:
# MONGO_INITDB_ROOT_USERNAME: root
# MONGO_INITDB_ROOT_PASSWORD: rootpassword
ports:
- 27017:27017
volumes:
- mongodb-data:/data/db
networks:
- mongodb_network

mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
# ME_CONFIG_MONGODB_ADMINUSERNAME: root
# ME_CONFIG_MONGODB_ADMINPASSWORD: example
# ME_CONFIG_MONGODB_URL: mongodb://root:example@mongodb:27017/
ME_CONFIG_MONGODB_URL: mongodb://mongodb:27017/
networks:
- mongodb_network

#######################################################
# PostgreSQL
#######################################################
postgres:
image: postgres:15.1-alpine
container_name: postgres
environment:
- POSTGRES_DB=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
ports:
- 5432:5432
networks:
- postgresql_network

pgadmin:
image: dpage/pgadmin4
container_name: pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-admin@pgadmin.org}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin}
ports:
- '${PGADMIN_PORT:-5050}:80'
networks:
- postgresql_network

networks:
esdb_network:
driver: bridge
postgresql_network:
driver: bridge
mongodb_network:
driver: bridge

volumes:
eventstore-volume-data:
eventstore-volume-logs:
mongodb-data:
Loading