From 0c658c119709ef2e0476948917db154875840814 Mon Sep 17 00:00:00 2001 From: Oskar Dudycz Date: Sat, 10 Feb 2024 18:12:30 +0100 Subject: [PATCH] Added more description about the event process --- .vscode/settings.json | 4 +-- docs/api-docs.md | 20 +++++++++++ docs/getting-started.md | 4 +++ docs/snippets/api/event.ts | 21 +++++++++++ docs/snippets/shoppingCart.ts | 65 ++++++++++++++++++++--------------- package-lock.json | 37 ++++++++++++++++++-- package.json | 8 +++-- samples/webapi/package.json | 23 ++++--------- tsconfig.json | 9 +++-- 9 files changed, 138 insertions(+), 53 deletions(-) create mode 100644 docs/snippets/api/event.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 8f179bcc..53a18ddf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,8 +13,8 @@ "files.exclude": { "node_modules/": true, - "**/node_modules/": true - // "dist/": true + "**/node_modules/": true, + "dist/": true }, "files.eol": "\n", diff --git a/docs/api-docs.md b/docs/api-docs.md index 7879ce20..9fa10963 100644 --- a/docs/api-docs.md +++ b/docs/api-docs.md @@ -3,3 +3,23 @@ outline: deep --- # API docs + +## Event + +**Events are the centrepiece of event-sourced systems.** They represent both critical points of the business process but are also used as the state. That enables you to reflect your business into the code better, getting the synergy. Let's model a simple business process: a shopping cart. You can open it, add or remove the product from it and confirm or cancel. + +Event type helps to keep the event definition aligned. It's not a must, but it helps to ensure that it has a type name defined (e.g. `ProductItemAddedToShoppingCart`) and read-only payload data. + +You can use it as follows + +<<< @/snippets/api/event.ts#event-type + +The type is a simple wrapper to ensure the structure's correctness. It defines: + +- **type** - event type name, +- **data** - represents the business data the event contains. It has to be a record structure; primitives are not allowed, +- **metadata** - represents the generic data event contains. It can represent telemetry, user id, tenant id, timestamps and other information that can be useful for running infrastructure. It has to be a record structure; primitives are not allowed. + +See more context in [getting started guide](./getting-started.md#events) + +<<< @./../src/typing/event.ts diff --git a/docs/getting-started.md b/docs/getting-started.md index 53960b88..04eff891 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -39,3 +39,7 @@ $ bun add -D @event-driven-io/emmett We could define it as follows: <<< @/snippets/shoppingCart.ts#getting-started-events + +It shows that clients can add or remove products to our shopping cart and confirm or cancel them. All events represent facts that happened and tell the story of the shopping cart. To highlight that, we're grouping all type definitions with the `ShoppingCartEvent` union type. It tells that either of those events may happen. + +We're using [Event type](/api-docs.md#event), which helps to keep the event definition aligned. It's not a must, but it helps to ensure that it has a type name defined (e.g. `ProductItemAddedToShoppingCart`) and read-only payload data. diff --git a/docs/snippets/api/event.ts b/docs/snippets/api/event.ts new file mode 100644 index 00000000..ccca4f67 --- /dev/null +++ b/docs/snippets/api/event.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +// #region event-type +import type { Event } from '@event-driven-io/emmett'; + +type ProductItemAddedToShoppingCart = Event< + 'ProductItemAddedToShoppingCart', + { + shoppingCartId: string; + productItem: PricedProductItem; + } +>; +// #endregion event-type + +export interface ProductItem { + productId: string; + quantity: number; +} + +export type PricedProductItem = ProductItem & { + price: number; +}; diff --git a/docs/snippets/shoppingCart.ts b/docs/snippets/shoppingCart.ts index 396fd156..0c0a9a0c 100644 --- a/docs/snippets/shoppingCart.ts +++ b/docs/snippets/shoppingCart.ts @@ -1,34 +1,43 @@ // #region getting-started-events +import type { Event } from '@event-driven-io/emmett'; + +type ProductItemAddedToShoppingCart = Event< + 'ProductItemAddedToShoppingCart', + { + shoppingCartId: string; + productItem: PricedProductItem; + } +>; + +type ProductItemRemovedFromShoppingCart = Event< + 'ProductItemRemovedFromShoppingCart', + { + shoppingCartId: string; + productItem: PricedProductItem; + } +>; + +type ShoppingCartConfirmed = Event< + 'ShoppingCartConfirmed', + { + shoppingCartId: string; + confirmedAt: Date; + } +>; + +type ShoppingCartCanceled = Event< + 'ShoppingCartCanceled', + { + shoppingCartId: string; + canceledAt: Date; + } +>; export type ShoppingCartEvent = - | { - type: 'ProductItemAddedToShoppingCart'; - data: { - shoppingCartId: string; - productItem: PricedProductItem; - }; - } - | { - type: 'ProductItemRemovedFromShoppingCart'; - data: { - shoppingCartId: string; - productItem: PricedProductItem; - }; - } - | { - type: 'ShoppingCartConfirmed'; - data: { - shoppingCartId: string; - confirmedAt: Date; - }; - } - | { - type: 'ShoppingCartCanceled'; - data: { - shoppingCartId: string; - canceledAt: Date; - }; - }; + | ProductItemAddedToShoppingCart + | ProductItemRemovedFromShoppingCart + | ShoppingCartConfirmed + | ShoppingCartCanceled; export interface ProductItem { productId: string; diff --git a/package-lock.json b/package-lock.json index 0736a899..7f849a39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,21 @@ { "name": "@event-driven-io/emmett", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@event-driven-io/emmett", - "version": "0.1.0", + "version": "0.1.1", + "workspaces": [ + "samples/*" + ], "dependencies": { "express": "4.18.2", "express-async-errors": "3.1.1" }, "devDependencies": { + "@event-driven-io/emmett": "0.1.1", "@faker-js/faker": "8.4.1", "@types/express": "4.17.21", "@types/jest": "29.5.0", @@ -33,6 +37,15 @@ "vitepress": "1.0.0-rc.42" } }, + "docs/snippets": { + "name": "emmett-webapi", + "version": "1.0.0", + "extraneous": true, + "license": "ISC", + "dependencies": { + "@event-driven-io/emmett": "0.1.0" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -971,6 +984,10 @@ } } }, + "node_modules/@emmett/webapi": { + "resolved": "samples/webapi", + "link": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -1395,6 +1412,16 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@event-driven-io/emmett": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@event-driven-io/emmett/-/emmett-0.1.1.tgz", + "integrity": "sha512-CkC6uDibgx3kT6EqKlKfWYQU3WR4s0U+/eKSCUzea/HcrRcdqtbxVU2BUlEg/BdMnyAX+elr44DEnjEeZy2glw==", + "dev": true, + "dependencies": { + "express": "4.18.2", + "express-async-errors": "3.1.1" + } + }, "node_modules/@faker-js/faker": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", @@ -9107,6 +9134,12 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "samples/webapi": { + "name": "@emmett/webapi", + "version": "1.0.0", + "license": "ISC", + "devDependencies": {} } } } diff --git a/package.json b/package.json index 427085fe..d62522d7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "setup": "cat .nvmrc | nvm install; nvm use", "build": "tsup", - "build:ts": "tsc", + "build:ts": "tsc --build --verbose tsconfig.build.json", "build:ts:watch": "tsc --watch", "start": "ts-node -r tsconfig-paths/register ./src/index.ts", "lint": "npm run lint:eslint && npm run lint:prettier", @@ -49,6 +49,7 @@ "express-async-errors": "3.1.1" }, "devDependencies": { + "@event-driven-io/emmett": "0.1.1", "@faker-js/faker": "8.4.1", "@types/jest": "29.5.0", "@types/node": "20.11.17", @@ -68,5 +69,8 @@ "tsup": "8.0.2", "typescript": "5.3.3", "vitepress": "1.0.0-rc.42" - } + }, + "workspaces": [ + "samples/*" + ] } diff --git a/samples/webapi/package.json b/samples/webapi/package.json index d83c5de3..91aaca15 100644 --- a/samples/webapi/package.json +++ b/samples/webapi/package.json @@ -1,24 +1,13 @@ { - "name": "emmett-webapi", + "name": "@emmett/webapi", "version": "1.0.0", - "description": "Sample showing basic Web Api with Emmett", + "description": "", "main": "index.js", + "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "repository": { - "type": "git", - "url": "git+https://github.com/event-driven-io/emmett.git" - }, - "keywords": [ - "event sourcing", - "emmett", - "nodejs" - ], - "author": "oskardudycz", - "license": "ISC", - "bugs": { - "url": "https://github.com/event-driven-io/emmett/issues" - }, - "homepage": "https://github.com/event-driven-io/emmett#readme", + "keywords": [], + "author": "", + "license": "ISC" } diff --git a/tsconfig.json b/tsconfig.json index 5d572f5a..60cb5bee 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,7 @@ // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist" /* Redirect output structure to the directory. */, // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ + "composite": true /* Enable project compilation */, // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ "noEmit": true /* Do not emit outputs. */, @@ -75,5 +75,10 @@ "skipLibCheck": true /* Skip type checking of declaration files. */, "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, - "include": ["./src/**/*", "./tsup.config.ts", "./docs/snippets/**/*"] + "include": [ + "./src/**/*", + "./tsup.config.ts", + "./docs/snippets/**/*", + "./samples/**/*" + ] }