Class Decider<C, S, E>

Decider is a datatype that represents the main decision-making algorithm. It has three generic parameters C, S, E , representing the type of the values that Decider may contain or use. Decider can be specialized for any type C or S or E because these types does not affect its behavior. Decider behaves the same for C=Int or C=YourCustomType, for example.

Decider is a pure domain component.

Param: decide

A function/lambda that takes command of type C and input state of type S as parameters, and returns/emits the list of output events E[]>

Param: evolve

A function/lambda that takes input state of type S and input event of type E as parameters, and returns the output/new state S

Param: initialState

A starting point / An initial state of type S

Author

Иван Дугалић / Ivan Dugalic /

Idugalic

Example

export const orderDecider: Decider<OrderCommand, Order | null, OrderEvent> =
new Decider<OrderCommand, Order | null, OrderEvent>(
(command, currentState) => {
switch (command.kind) {
case "CreateOrderCommand":
return currentState == null
? [
{
version: 1,
decider: "Order",
kind: "OrderCreatedEvent",
id: command.id,
restaurantId: command.restaurantId,
menuItems: command.menuItems,
final: false,
},
]
: [
{
version: 1,
decider: "Order",
kind: "OrderNotCreatedEvent",
id: command.id,
restaurantId: command.restaurantId,
menuItems: command.menuItems,
final: false,
reason: "Order already exist!",
},
];
case "MarkOrderAsPreparedCommand":
return currentState !== null
? [
{
version: 1,
decider: "Order",
kind: "OrderPreparedEvent",
id: currentState.orderId,
final: false,
},
]
: [
{
version: 1,
decider: "Order",
kind: "OrderNotPreparedEvent",
id: command.id,
reason: "Order does not exist!",
final: false,
},
];
default:
// Exhaustive matching of the command type
const _: never = command;
return [];
}
},
(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,
);

Type Parameters

  • C

    Command type

  • S

    State type

  • E

    Event type

Implements

Constructors

  • Type Parameters

    • C
    • S
    • E

    Parameters

    • decide: ((command, state) => readonly E[])
        • (command, state): readonly E[]
        • Parameters

          • command: C
          • state: S

          Returns readonly E[]

    • evolve: ((state, event) => S)
        • (state, event): S
        • Parameters

          • state: S
          • event: E

          Returns S

    • initialState: S

    Returns Decider<C, S, E>

Properties

decide: ((command, state) => readonly E[])

Type declaration

    • (command, state): readonly E[]
    • Parameters

      • command: C
      • state: S

      Returns readonly E[]

evolve: ((state, event) => S)

Type declaration

    • (state, event): S
    • Parameters

      • state: S
      • event: E

      Returns S

initialState: S

Methods

  • Combine multiple Deciders into one Decider - Monoid

    State/S is combined via intersection / (S & S2). It only makes sense if S ans S2 are objects, not primitives. Check alternative method combineViaTuples

    Intersections provide more flexibility and can handle more complex scenarios, while tuples are more straightforward and may be more suitable for simple cases.

    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.

    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.

    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.

    Type Parameters

    • C2
    • S2
    • E2

    Parameters

    Returns Decider<C | C2, S & S2, E | E2>

  • Combine multiple Deciders into one Decider - Monoid

    State/S is combined via tuples / [S, S2]. Check alternative method combine

    Tuples are more straightforward and may be more suitable for simple cases, while intersections provide more flexibility and can handle more complex scenarios.

    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.

    Type Parameters

    • C2
    • S2
    • E2

    Parameters

    Returns Decider<C | C2, readonly [S, S2], E | E2>

Generated using TypeDoc