Skip to content

Commit

Permalink
Used PostgreSQL storage in documentation instead of EventStoreDB, as …
Browse files Browse the repository at this point in the history
…it's the most popular choice and the most feature rich option
  • Loading branch information
oskardudycz committed Jan 26, 2025
1 parent 1dae4a8 commit f3e926b
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 46 deletions.
50 changes: 30 additions & 20 deletions src/docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,20 @@ The essential difference between Event Sourcing and Event Streaming is that in E

**Emmett provides a lightweight abstraction for event stores.** We don't intend to provide the lowest common denominator but streamline the typical usage patterns. It's OK if you use your preferred event store or client for the cases where those parts do not suffice your needs. Still, what's there should take you far enough.

Here is the general definition of it:

<<< @./../packages/emmett/src/eventStore/eventStore.ts#event-store

It brings you three most important methods:

- `readStream` - reads events for the specific stream. By default, it reads all events, but through options, you can specify the event range you want to get (`from`, `to`, `maxCount`). You can also specify the expected stream version.
- `appendToStream` - appends new events at the end of the stream. All events should be appended as an atomic operation. You can specify the expected stream version for an [optimistic concurrency check](https://event-driven.io/en/optimistic_concurrency_for_pessimistic_times/). We're also getting the next stream version as a result.
- `aggregateStream` - builds the current state from events. Internally, event store implementation should read all events in the stream based on the passed initial state and the `evolve` function. It also supports all the same options as the `readStream` method.

Emmett provides you with out-of-the-box support for the following storage:

- PostgreSQL with [emmett-postgresql](https://www.npmjs.com/package/@event-driven-io/emmett-postgresql) package,
- EventStoreDB with [emmett-esdb](https://www.npmjs.com/package/@event-driven-io/emmett-esdb) package,
- MongoDB with [emmett-mongodb](https://www.npmjs.com/package/@event-driven-io/emmett-mongodb) package,
- SQLite with [emmett-sqlite](https://www.npmjs.com/package/@event-driven-io/emmett-sqlite) package,
- In-Memory with regular [emmett](https://www.npmjs.com/package/@event-driven-io/emmett) package.

Read more about how event stores are built in the [article](https://event-driven.io/en/lets_build_event_store_in_one_hour/).

## Command Handling
Expand Down Expand Up @@ -267,7 +271,7 @@ That clearly explains what dependencies this API needs, and by reading the file,

<<< @/snippets/gettingStarted/webApi/apiSetup.ts#getting-started-api-setup

We're using the simplest option for this guide: an in-memory event store. For a real application, you'd need to use another, e.g. [EventStoreDB](https://developers.eventstore.com/) implementation.
We're using the simplest option for this guide: an in-memory event store. For a real application, you'd need to use another, e.g. PostgreSQL implementation.

Sounds like we have all the building blocks to define our API; let's do it!

Expand Down Expand Up @@ -384,7 +388,7 @@ Complete tests will look like this:

You can use those tests as complementary to the business logic (e.g., testing the most important scenarios), or you may even replace unit tests with them. As they're in memory, they're fast enough to be run continuously.

You can also replace the in-memory store with the real one (e.g. [EventStoreDB](https://developers.eventstore.com/)) and test your module in isolation from other modules. The choice is yours!
You can also replace the in-memory store with the real one (e.g. PostgreSQL) and test your module in isolation from other modules. The choice is yours!

Again, in Emmett, we don't want to force you to anything but give you options and the recommended safe path.

Expand All @@ -402,58 +406,64 @@ You may say:
And we answer: sure, why not! We also give you help with that.

Let's start by adding some flavour and use [EventStoreDB](https://developers.eventstore.com/) this time. We need to install two more packages. One for adding implementation of the [EventStoreDB](https://developers.eventstore.com/) event store:
Let's start by adding some flavour and finally use a real database, so PostgreSQL this time. We need to install two more packages. One for adding implementation of the PostgreSQL event store:

::: code-group

```sh [npm]
$ npm add @event-driven-io/@event-driven-io/emmett-esdb
$ npm add @event-driven-io/@event-driven-io/emmett-postgresql
```

```sh [pnpm]
$ pnpm add @event-driven-io/@event-driven-io/emmett-esdb
$ pnpm add @event-driven-io/@event-driven-io/emmett-postgresql
```

```sh [yarn]
$ yarn add @event-driven-io/@event-driven-io/emmett-esdb
$ yarn add @event-driven-io/@event-driven-io/emmett-postgresql
```

```sh [bun]
$ bun add @event-driven-io/@event-driven-io/emmett-esdb
$ bun add @event-driven-io/@event-driven-io/emmett-postgresql
```

:::

Now, we need to switch the in-memory implementation to [EventStoreDB](https://developers.eventstore.com/) in WebApi setup. Updated will look as follows:
Now, we need to switch the in-memory implementation to PostgreSQL in WebApi setup. Updated will look as follows:

<<< @/snippets/gettingStarted/webApi/apiSetupWithESDB.ts#getting-started-api-setup
<<< @/snippets/gettingStarted/webApi/apiSetupWithPostgreSQL.ts#getting-started-api-setup

It's as simple as that; we're injecting just a different implementation.

As [EventStoreDB](https://developers.eventstore.com/) is a real database, we need to set it up for our tests. The simplest option is to use a Docker container. You can do it in multiple ways, but the fastest can be using [TestContainers](https://node.testcontainers.org/). The library allows us to easily set up containers for our tests. It automatically randomise ports, helps in teardown etc.
As PostgreSQL is a real database, we need to set it up for our tests. The simplest option is to use a Docker container. You can do it in multiple ways, but the fastest can be using [TestContainers](https://node.testcontainers.org/). The library allows us to easily set up containers for our tests. It automatically randomise ports, helps in teardown etc.

Emmett provides the package with additional test containers like the one for [EventStoreDB](https://developers.eventstore.com/). You need to install:
For PostgreSQL you'll need to install:

::: code-group

```sh [npm]
$ npm add @event-driven-io/@event-driven-io/emmett-testcontainers
$ npm add @testcontainers/postgresql
```

```sh [pnpm]
$ pnpm add @event-driven-io/@event-driven-io/emmett-testcontainers
$ pnpm add @testcontainers/postgresql
```

```sh [yarn]
$ yarn add @event-driven-io/@event-driven-io/emmett-testcontainers
$ yarn add @testcontainers/postgresql
```

```sh [bun]
$ bun add @event-driven-io/@event-driven-io/emmett-testcontainers
$ bun add @testcontainers/postgresql
```

:::

::: info EventStoreDB testing

Emmett provides the package with additional test containers like the one for [EventStoreDB](https://developers.eventstore.com/). If you're using EventStoreDB, install [emmett-testcontainers](https://www.npmjs.com/package/@event-driven-io/emmett-testcontainers) and get the test container for it.

:::

Having that, we can set our test container with:

<<< @/snippets/gettingStarted/webApi/apiBDDE2EGiven.ts#test-container
Expand All @@ -470,4 +480,4 @@ Complete tests will look like this:

<<< @/snippets/gettingStarted/webApi/apiBDD.e2e.spec.ts#getting-started-e2e-tests

Check also the [full sample in Emmett repository](https://github.com/event-driven-io/emmett/tree/main/samples/webApi/expressjs-with-esdb).
Check also the [full sample in Emmett repository](https://github.com/event-driven-io/emmett/tree/main/samples/webApi/expressjs-with-postgresql).
16 changes: 8 additions & 8 deletions src/docs/snippets/gettingStarted/webApi/apiBDD.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@ import { ShoppingCartStatus } from './shoppingCart';
import { shoppingCartApi } from './simpleApi';

// #region getting-started-e2e-tests
import { getEventStoreDBEventStore } from '@event-driven-io/emmett-esdb';
import {
ApiE2ESpecification,
expectResponse,
getApplication,
type TestRequest,
} from '@event-driven-io/emmett-expressjs';
import { getPostgreSQLEventStore } from '@event-driven-io/emmett-postgresql';
import {
EventStoreDBContainer,
StartedEventStoreDBContainer,
} from '@event-driven-io/emmett-testcontainers';
PostgreSqlContainer,
type StartedPostgreSqlContainer,
} from '@testcontainers/postgresql';
import { randomUUID } from 'node:crypto';

void describe('ShoppingCart E2E', () => {
const unitPrice = 100;
let clientId: string;
let shoppingCartId: string;
let esdbContainer: StartedEventStoreDBContainer;
let postgreSQLContainer: StartedPostgreSqlContainer;
let given: ApiE2ESpecification;

before(async () => {
esdbContainer = await new EventStoreDBContainer().start();
postgreSQLContainer = await new PostgreSqlContainer().start();

given = ApiE2ESpecification.for(
() => getEventStoreDBEventStore(esdbContainer.getClient()),
() => getPostgreSQLEventStore(postgreSQLContainer.getConnectionUri()),
(eventStore) =>
getApplication({
apis: [
Expand All @@ -48,7 +48,7 @@ void describe('ShoppingCart E2E', () => {
});

after(() => {
return esdbContainer.stop();
return postgreSQLContainer.stop();
});

void describe('When opened with product item', () => {
Expand Down
16 changes: 8 additions & 8 deletions src/docs/snippets/gettingStarted/webApi/apiBDDE2EGiven.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,34 @@ const now = new Date();

// #region test-container
import {
EventStoreDBContainer,
type StartedEventStoreDBContainer,
} from '@event-driven-io/emmett-testcontainers';
PostgreSqlContainer,
StartedPostgreSqlContainer,
} from '@testcontainers/postgresql';

let esdbContainer: StartedEventStoreDBContainer;
let postgreSQLContainer: StartedPostgreSqlContainer;
void describe('ShoppingCart E2E', () => {
// Set up a container before all tests
before(async () => {
esdbContainer = await new EventStoreDBContainer().start();
postgreSQLContainer = await new PostgreSqlContainer().start();
});

// Stop container once we finished testing
after(() => {
return esdbContainer.stop();
return postgreSQLContainer.stop();
});
// (...) Tests will go here
});
// #endregion test-container

// #region given
import { getEventStoreDBEventStore } from '@event-driven-io/emmett-esdb';
import {
ApiE2ESpecification,
getApplication,
} from '@event-driven-io/emmett-expressjs';
import { getPostgreSQLEventStore } from '@event-driven-io/emmett-postgresql';

const given = ApiE2ESpecification.for(
() => getEventStoreDBEventStore(esdbContainer.getClient()),
() => getPostgreSQLEventStore(postgreSQLContainer.getConnectionUri()),
(eventStore) =>
getApplication({
apis: [
Expand Down
8 changes: 4 additions & 4 deletions src/docs/snippets/gettingStarted/webApi/apiBDDE2ETest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import { describe, it } from 'node:test';
import type { PricedProductItem } from '../events';
import { shoppingCartApi } from './simpleApi';

const esdbContainer: StartedEventStoreDBContainer = undefined!;
const postgreSQLContainer: StartedPostgreSqlContainer = undefined!;
const clientId = randomUUID();
const now = new Date();
const unitPrice = Math.random() * 10;

const given = ApiE2ESpecification.for(
() => getEventStoreDBEventStore(esdbContainer.getClient()),
() => getPostgreSQLEventStore(postgreSQLContainer.getConnectionUri()),
(eventStore) =>
getApplication({
apis: [
Expand All @@ -38,9 +38,9 @@ const getRandomProduct = (): PricedProductItem => {
const productItem = getRandomProduct();

// #region test
import { getEventStoreDBEventStore } from '@event-driven-io/emmett-esdb';
import { expectResponse } from '@event-driven-io/emmett-expressjs';
import type { StartedEventStoreDBContainer } from '@event-driven-io/emmett-testcontainers';
import { getPostgreSQLEventStore } from '@event-driven-io/emmett-postgresql';
import type { StartedPostgreSqlContainer } from '@testcontainers/postgresql';

void describe('When opened with product item', () => {
const openedShoppingCartWithProduct: TestRequest = (request) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ const getUnitPrice = (_productId: string) => {
};

// #region getting-started-api-setup
import { getEventStoreDBEventStore } from '@event-driven-io/emmett-esdb';
import { EventStoreDBClient } from '@eventstore/db-client';
import { getPostgreSQLEventStore } from '@event-driven-io/emmett-postgresql';

const eventStoreDBClient = EventStoreDBClient.connectionString(
`esdb://localhost:2113?tls=false`,
);
const eventStore = getEventStoreDBEventStore(eventStoreDBClient);
const connectionString =
process.env.POSTGRESQL_CONNECTION_STRING ??
'postgresql://localhost:5432/postgres';

const eventStore = getPostgreSQLEventStore(connectionString);

const shoppingCarts = shoppingCartApi(
eventStore,
Expand Down

0 comments on commit f3e926b

Please sign in to comment.