@fraktalio/fmodel-ts
Version:
Functional domain modeling with TypeScript. Optimized for event sourcing and CQRS
109 lines (108 loc) • 4.8 kB
TypeScript
/**
* `IView` Interface
*
* Represents the event handling algorithm,
* responsible for translating the events into denormalized state,
* which is more adequate for querying.
*
* @typeParam S - State
* @typeParam E - Event
*
* @param evolve - A function/lambda that takes input state of type `Si` and input event of type `Ei` as parameters, and returns the output/new state `So`
* @param initialState - A starting point / An initial state of type `So`
*
* @author Иван Дугалић / Ivan Dugalic / @idugalic
*/
export interface IView<S, E> {
readonly evolve: (state: S, event: E) => S;
readonly initialState: S;
}
/**
* `View` is a datatype that represents the event handling algorithm,
* responsible for translating the events into denormalized state,
* which is more adequate for querying.
*
* @typeParam S - State
* @typeParam E - Event
*
* ### Example
* ```typescript
* export const orderView: View<OrderView | null, OrderEvent> = new View<
* OrderView | null,
* OrderEvent
* >(
* (currentState, event) => {
* switch (event.kind) {
* case "OrderCreatedEvent":
* return {
* orderId: event.id,
* restaurantId: event.restaurantId,
* menuItems: event.menuItems,
* status: "CREATED",
* };
* case "OrderNotCreatedEvent":
* return currentState;
* case "OrderPreparedEvent":
* return currentState !== null
* ? {
* orderId: currentState.orderId,
* restaurantId: currentState.restaurantId,
* menuItems: currentState.menuItems,
* status: "PREPARED",
* }
* : currentState;
* case "OrderNotPreparedEvent":
* return currentState;
* default:
* // Exhaustive matching of the event type
* const _: never = event;
* return currentState;
* }
* },
* null,
* );
* ```
*
* @author Иван Дугалић / Ivan Dugalic / @idugalic
*/
export declare class View<S, E> implements IView<S, E> {
readonly evolve: (state: S, event: E) => S;
readonly initialState: S;
constructor(evolve: (state: S, event: E) => S, initialState: S);
/**
* Contra (Left) map on E/Event parameter - Contravariant
*
* @typeParam En - New Event
*/
mapContraOnEvent<En>(f: (en: En) => E): View<S, En>;
/**
* Dimap on S/State parameter - Profunctor
*
* @typeParam Sn - New State
*/
dimapOnState<Sn>(fl: (sn: Sn) => S, fr: (s: S) => Sn): View<Sn, E>;
/**
* Combines Views into one bigger View - Monoid
*
* Combines state via intersection (S & S2). Check alternative method `combineViaTuples`.
*
* 1. Flexibility: If you anticipate needing to access individual components of the combined state separately, using tuples might be more appropriate, as it allows you to maintain separate types for each component. However, if you primarily need to treat the combined state as a single entity with all properties accessible at once, intersections might be more suitable.
*
* 2. Readability: Consider which approach makes your code more readable and understandable to other developers who may be working with your codebase. Choose the approach that best communicates your intentions and the structure of your data.
*
* 3. Compatibility: Consider the compatibility of your chosen approach with other libraries, frameworks, or tools you're using in your TypeScript project. Some libraries or tools might work better with one approach over the other.
*/
combine<S2, E2>(view2: View<S2, E2>): View<S & S2, E | E2>;
/**
* Combines Views into one bigger View - Monoid
*
* Combines state via tuple [S, S2]. Check alternative method `combine`
*
* 1. Flexibility: If you anticipate needing to access individual components of the combined state separately, using tuples might be more appropriate, as it allows you to maintain separate types for each component. However, if you primarily need to treat the combined state as a single entity with all properties accessible at once, intersections might be more suitable.
*
* 2. Readability: Consider which approach makes your code more readable and understandable to other developers who may be working with your codebase. Choose the approach that best communicates your intentions and the structure of your data.
*
* 3. Compatibility: Consider the compatibility of your chosen approach with other libraries, frameworks, or tools you're using in your TypeScript project. Some libraries or tools might work better with one approach over the other.
*/
combineViaTuples<S2, E2>(view2: View<S2, E2>): View<readonly [S, S2], E | E2>;
}