diff --git a/docs/docs/1-introduction.md b/docs/docs/1-introduction.md index be0c9eef..f7f2de50 100644 --- a/docs/docs/1-introduction.md +++ b/docs/docs/1-introduction.md @@ -22,9 +22,9 @@ After years of using it at [Theodo](https://dev.to/slsbytheodo), we have grown t With Castore, you'll be able to: -- Define your [event stores](/docs/the-basics#eventstore) -- Fetch and push new [events](/docs/the-basics#events) seamlessly -- Implement and test your [commands](/docs/the-basics#command) +- Define your [event stores](./3-event-sourcing/3-event-stores.md) +- Fetch and push new [events](./3-event-sourcing/1-events.md) seamlessly +- Implement and test your [commands](./3-event-sourcing/5-pushing-events.md) - ...and much more! All that with first-class developer experience and minimal boilerplate ✨ diff --git a/docs/docs/3-event-sourcing/1-events.md b/docs/docs/3-event-sourcing/1-events.md index 894b7ea2..26181e3b 100644 --- a/docs/docs/3-event-sourcing/1-events.md +++ b/docs/docs/3-event-sourcing/1-events.md @@ -53,7 +53,7 @@ const pokemonAppearedEventType = new EventType< :::info -Note that we only provided TS types for `payload` and `metadata` properties. That is because, as stated in the [core design](/docs/introduction#-core-design), **Castore is meant to be as flexible as possible**, and that includes the validation library you want to use (if any): The `EventType` class can be used directly if no validation is required, or implemented by [other classes](../5-resources.md) which will add run-time validation methods to it 👍 +Note that we only provided TS types for `payload` and `metadata` properties. That is because, as stated in the [core design](../1-introduction.md#-core-design), **Castore is meant to be as flexible as possible**, and that includes the validation library you want to use (if any): The `EventType` class can be used directly if no validation is required, or implemented by [other classes](../5-packages.md#-event-types) which will add run-time validation methods to it 👍 ::: diff --git a/docs/docs/3-event-sourcing/3-event-stores.md b/docs/docs/3-event-sourcing/3-event-stores.md index 36246e0a..591493ad 100644 --- a/docs/docs/3-event-sourcing/3-event-stores.md +++ b/docs/docs/3-event-sourcing/3-event-stores.md @@ -4,7 +4,7 @@ sidebar_position: 3 # 📙 Event Store -Once you've defined your [event types](#eventtype) and how to [aggregate](#reducer) them, you can bundle them together in an `EventStore` class. Each event store in your application represents a business entity. +Once you've defined your [event types](./1-events.md) and how to [aggregate](./2-aggregates-reducers.md) them, you can bundle them together in an `EventStore` class. Each event store in your application represents a business entity. :::note @@ -14,7 +14,7 @@ Think of event stores as _"what tables would be in CRUD"_, except that instead o ![Event Store](../../assets/docSchemas/eventStore.png) -In Castore, `EventStore` classes are NOT responsible for actually storing data (this will come with [event storage adapters](#eventstorageadapter)). But rather to provide a boilerplate-free and type-safe interface to perform many actions such as: +In Castore, `EventStore` classes are NOT responsible for actually storing data (this will come with [event storage adapters](./4-fetching-events.md)). But rather to provide a boilerplate-free and type-safe interface to perform many actions such as: - Listing aggregate ids - Accessing events of an aggregate @@ -39,7 +39,7 @@ const pokemonsEventStore = new EventStore({ :::info -> ☝️ The `EventStore` class is the heart of Castore, it even gave it its name! +☝️ The `EventStore` class is the heart of Castore, it even gave it its name! ::: @@ -51,9 +51,9 @@ const pokemonsEventStore = new EventStore({ > > - eventStoreId (string): A string identifying the event store > - eventStoreEvents (EventType[]): The list of event types in the event store -> - reduce (EventType[]): A reducer function that can be applied to the store event types +> - reduce (EventType[]): A reducer function that can be applied to the store event types > - onEventPushed ?(pushEventResponse: PushEventResponse => Promise(void)): To run a callback after events are pushed (input is exactly the return value of the pushEvent method) -> - storageAdapter (?EventStorageAdapter): See EventStorageAdapter +> - storageAdapter (?EventStorageAdapter): See EventStorageAdapter > > ☝️ The return type of the `reducer` is used to infer the `Aggregate` type of the `EventStore`, so it is important to type it explicitely. > @@ -87,7 +87,7 @@ const pokemonsEventStore = new EventStore({ > // => undefined (we did not provide one in this example) > ``` > -> - storageAdapter ?EventStorageAdapter: See EventStorageAdapter +> - storageAdapter ?EventStorageAdapter: See EventStorageAdapter > > ```ts > const storageAdapter = pokemonsEventStore.storageAdapter; @@ -115,11 +115,11 @@ const pokemonsEventStore = new EventStore({ > const myPikachuAggregate = pokemonsEventStore.buildAggregate(myPikachuEvents); > ``` > -> - groupEvent ((eventDetail: EventDetail, opt?: OptionsObj = {}) => GroupedEvent): See Event Groups. +> - groupEvent ((eventDetail: EventDetail, opt?: OptionsObj = {}) => GroupedEvent): See Event Groups. > > **Async Methods:** > -> The following methods interact with the data layer of your event store through its [`EventStorageAdapter`](#eventstorageadapter). They will throw an `UndefinedStorageAdapterError` if you did not provide one. +> The following methods interact with the data layer of your event store through its [`EventStorageAdapter`](./4-fetching-events.md). They will throw an `UndefinedStorageAdapterError` if you did not provide one. > > - getEvents ((aggregateId: string, opt?: OptionsObj = {}) => Promise(ResponseObj)): Retrieves the events of an aggregate, ordered by version. Returns an empty array if no event is found for this aggregateId. > @@ -208,13 +208,13 @@ const pokemonsEventStore = new EventStore({ > > `OptionsObj` contains the following properties: > -> - prevAggregate (?Aggregate): The aggregate at the current version, i.e. before having pushed the event. Can be useful in some cases like when using the ConnectedEventStore class +> - prevAggregate (?Aggregate): The aggregate at the current version, i.e. before having pushed the event. Can be useful in some cases like when using the ConnectedEventStore class > - force (?boolean): To force push the event even if one already exists for the corresponding aggregateId and version. Any existing event will be overridden, so use with extra care, mainly in data migrations. > > `ResponseObj` contains the following properties: > > - event (EventDetail): The complete event (includes the timestamp) -> - nextAggregate (?Aggregate): The aggregate at the new version, i.e. after having pushed the event. Returned only if the event is an initial event, if the prevAggregate option was provided, or when using a ConnectedEventStore class connected to a state-carrying message bus or queue +> - nextAggregate (?Aggregate): The aggregate at the new version, i.e. after having pushed the event. Returned only if the event is an initial event, if the prevAggregate option was provided, or when using a ConnectedEventStore class connected to a state-carrying message bus or queue > > ```ts > const { event: completeEvent, nextAggregate } = diff --git a/docs/docs/3-event-sourcing/4-fetching-events.md b/docs/docs/3-event-sourcing/4-fetching-events.md index ed67982f..c2eb3716 100644 --- a/docs/docs/3-event-sourcing/4-fetching-events.md +++ b/docs/docs/3-event-sourcing/4-fetching-events.md @@ -24,11 +24,12 @@ const pokemonsEventStore = new EventStore({ pokemonsEventStore.storageAdapter = mySuperStorageAdapter; const { events } = await pokemonsEventStore.getEvents('pikachu1'); +const { aggregate } = await pokemonsEventStore.getAggregate('pikachu1'); // 🙌 Will work! ``` :::info -You can choose to build an event storage adapter that suits your usage. However, we highly recommend using an [off-the-shelf adapter](../5-resources.md) (if the storage solution that you use does not have an adapter yet, feel free to create/upvote an issue, or contribute 🤗). +You can choose to build an event storage adapter that suits your usage. However, we highly recommend using an [off-the-shelf adapter](../5-packages.md#-event-storage-adapters) (if the storage solution that you use does not have an adapter yet, feel free to create/upvote an issue, or contribute 🤗). ::: diff --git a/docs/docs/3-event-sourcing/5-pushing-events.md b/docs/docs/3-event-sourcing/5-pushing-events.md index 8c044f0e..d46fd99a 100644 --- a/docs/docs/3-event-sourcing/5-pushing-events.md +++ b/docs/docs/3-event-sourcing/5-pushing-events.md @@ -47,11 +47,11 @@ const catchPokemonCommand = new Command({ :::info -Note that we only provided TS types for `Input` and `Output` properties. That is because, as stated in the [core design](/docs/introduction#-core-design), **Castore is meant to be as flexible as possible**, and that includes the validation library you want to use (if any): The `Command` class can be used directly if no validation is required, or implemented by [other classes](../5-resources.md) which will add run-time validation methods to it 👍 +Note that we only provided TS types for `Input` and `Output` properties. That is because, as stated in the [core design](../1-introduction.md#-core-design), **Castore is meant to be as flexible as possible**, and that includes the validation library you want to use (if any): The `Command` class can be used directly if no validation is required, or implemented by [other classes](../5-packages.md#-commands) which will add run-time validation methods to it 👍 ::: -`Commands` handlers should NOT use [read models](/docs/advanced-usage#read-models) when validating that a modification is acceptable. Read models are like cache: They are not the source of truth, and may not represent the freshest state. +`Commands` handlers should NOT use [read models](../4-reacting-to-events/6-read-models.md) when validating that a modification is acceptable. Read models are like cache: They are not the source of truth, and may not represent the freshest state. Fetching and pushing events non-simultaneously exposes your application to [race conditions](https://en.wikipedia.org/wiki/Race_condition). To counter that, commands are designed to be retried when an `EventAlreadyExistsError` is triggered (which is part of the `EventStorageAdapter` interface). diff --git a/docs/docs/4-reacting-to-events/1-messages.md b/docs/docs/4-reacting-to-events/1-messages.md index 495c183f..bc9668d2 100644 --- a/docs/docs/4-reacting-to-events/1-messages.md +++ b/docs/docs/4-reacting-to-events/1-messages.md @@ -87,4 +87,4 @@ type PokemonEventStateCarryingMessage = EventStoreStateCarryingMessage< >; ``` -All types of message can be published through message channels, i.e. [Message Queues](#messagequeue) or [Message Buses](#messagebus). +All types of message can be published through message channels, i.e. [Message Queues](./2-message-queues.md) or [Message Buses](./3-message-buses.md). diff --git a/docs/docs/4-reacting-to-events/2-message-queues.md b/docs/docs/4-reacting-to-events/2-message-queues.md index 42cfbcd8..f28e3962 100644 --- a/docs/docs/4-reacting-to-events/2-message-queues.md +++ b/docs/docs/4-reacting-to-events/2-message-queues.md @@ -53,7 +53,7 @@ await messageQueue.publishMessage(...); :::info -You can code your own `MessageQueueAdapter` (simply implement the `MessageChannelAdapter` interface), but we highly recommend using an [off-the-shelf adapter](../5-resources.md) (if the messaging solution that you use does not have an adapter yet, feel free to create/upvote an issue, or contribute 🤗). +You can code your own `MessageQueueAdapter` (simply implement the `MessageChannelAdapter` interface), but we highly recommend using an [off-the-shelf adapter](../5-packages.md#-message-queue-adapters) (if the messaging solution that you use does not have an adapter yet, feel free to create/upvote an issue, or contribute 🤗). ::: @@ -82,7 +82,7 @@ const appMessagesWorker = async ({ Records }: SQSMessageQueueMessage) => { > > - messageQueueId (string): A string identifying the message queue > - sourceEventStores (EventStore[]): List of event stores that the message queue will broadcast events from -> - messageQueueAdapter (?MessageChannelAdapter): See section on MessageQueueAdapters +> - messageQueueAdapter (?MessageChannelAdapter): Message queue adapter > > **Properties:** > @@ -100,7 +100,7 @@ const appMessagesWorker = async ({ Records }: SQSMessageQueueMessage) => { > // => [pokemonsEventStore, trainersEventStore...] > ``` > -> - messageChannelAdapter ?MessageChannelAdapter: See section on MessageQueueAdapters +> - messageChannelAdapter ?MessageChannelAdapter: Returns the associated message queue adapter (potentially undefined) > > ```ts > const appMessageQueueAdapter = appMessageQueue.messageChannelAdapter; diff --git a/docs/docs/4-reacting-to-events/3-message-buses.md b/docs/docs/4-reacting-to-events/3-message-buses.md index 6bfcfb88..7ef17da3 100644 --- a/docs/docs/4-reacting-to-events/3-message-buses.md +++ b/docs/docs/4-reacting-to-events/3-message-buses.md @@ -53,7 +53,7 @@ await messageBus.publishMessage(...); :::info -You can code your own `MessageBusAdapter` (simply implement the `MessageChannelAdapter` interface), but we highly recommend using an [off-the-shelf adapter](../5-resources.md) (if the messaging solution that you use is missing, feel free to create/upvote an issue, or contribute 🤗). +You can code your own `MessageBusAdapter` (simply implement the `MessageChannelAdapter` interface), but we highly recommend using an [off-the-shelf adapter](../5-packages.md#-message-buses-adapters) (if the messaging solution that you use is missing, feel free to create/upvote an issue, or contribute 🤗). ::: @@ -82,7 +82,7 @@ const pokemonMessagesListener = async ( > > - messageBusId (string): A string identifying the message bus > - sourceEventStores (EventStore[]): List of event stores that the message bus will broadcast events from -> - messageBusAdapter (?MessageChannelAdapter): See section on MessageBusAdapters +> - messageBusAdapter (?MessageChannelAdapter): Message bus adapter > > **Properties:** > @@ -100,7 +100,7 @@ const pokemonMessagesListener = async ( > // => [pokemonsEventStore, trainersEventStore...] > ``` > -> - messageChannelAdapter ?MessageChannelAdapter: See section on MessageBusAdapters +> - messageChannelAdapter ?MessageChannelAdapter: Returns the associated message bus adapter (potentially undefined) > > ```ts > const appMessageBusAdapter = appMessageBus.messageChannelAdapter;