Skip to content

Commit

Permalink
Added full implementation of the Workflow pattern as described by @yr…
Browse files Browse the repository at this point in the history
…eynhout

Updated the example of group checkout to use it.
  • Loading branch information
oskardudycz committed Jan 28, 2024
1 parent fbc9275 commit 183b7a3
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 152 deletions.
2 changes: 1 addition & 1 deletion samples/hotelManagement/src/core/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export type Command<
CommandData extends Record<string, unknown> = Record<string, unknown>,
> = Flavour<
Readonly<{
type: Readonly<CommandType>;
type: CommandType;
data: Readonly<CommandData>;
}>,
'Command'
Expand Down
2 changes: 1 addition & 1 deletion samples/hotelManagement/src/core/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export type Event<
EventData extends Record<string, unknown> = Record<string, unknown>,
> = Flavour<
Readonly<{
type: Readonly<EventType>;
type: EventType;
data: Readonly<EventData>;
}>,
'Event'
Expand Down
103 changes: 97 additions & 6 deletions samples/hotelManagement/src/core/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,108 @@ import { Event } from './event';

/// Inspired by https://blog.bittacklr.be/the-workflow-pattern.html

export type WorkflowEvent<Output extends Command | Event> = Extract<
Output,
{ __brand?: 'Event' }
>;

export type Workflow<
Input extends Event | Command,
State,
Output extends Event | Command,
> = {
decide: (command: Input, state: State) => Output[];
decide: (command: Input, state: State) => WorkflowOutput<Output>[];
evolve: (currentState: State, event: WorkflowEvent<Output>) => State;
getInitialState: () => State;
};

export type WorkflowEvent<Output extends Command | Event> = Extract<
Output,
{ __brand?: 'Event' }
>;

export type WorkflowCommand<Output extends Command | Event> = Extract<
Output,
{ __brand?: 'Command' }
>;

export type Reply = Command | Event;

export type WorkflowOutput<TOutput extends Command | Event> =
| { kind: 'Reply'; message: TOutput }
| { kind: 'Send'; message: WorkflowCommand<TOutput> }
| { kind: 'Publish'; message: WorkflowEvent<TOutput> }
| {
kind: 'Schedule';
message: TOutput;
when: { afterInMs: number } | { at: Date };
}
| { kind: 'Complete' }
| { kind: 'Accept' }
| { kind: 'Ignore'; reason: string }
| { kind: 'Error'; reason: string };

export const reply = <TOutput extends Command | Event>(
message: TOutput,
): WorkflowOutput<TOutput> => {
return {
kind: 'Reply',
message,
};
};

export const send = <TOutput extends Command | Event>(
message: WorkflowCommand<TOutput>,
): WorkflowOutput<TOutput> => {
return {
kind: 'Send',
message,
};
};

export const publish = <TOutput extends Command | Event>(
message: WorkflowEvent<TOutput>,
): WorkflowOutput<TOutput> => {
return {
kind: 'Publish',
message,
};
};

export const schedule = <TOutput extends Command | Event>(
message: TOutput,
when: { afterInMs: number } | { at: Date },
): WorkflowOutput<TOutput> => {
return {
kind: 'Schedule',
message,
when,
};
};

export const complete = <
TOutput extends Command | Event,
>(): WorkflowOutput<TOutput> => {
return {
kind: 'Complete',
};
};

export const ignore = <TOutput extends Command | Event>(
reason: string,
): WorkflowOutput<TOutput> => {
return {
kind: 'Ignore',
reason,
};
};

export const error = <TOutput extends Command | Event>(
reason: string,
): WorkflowOutput<TOutput> => {
return {
kind: 'Error',
reason,
};
};

export const accept = <
TOutput extends Command | Event,
>(): WorkflowOutput<TOutput> => {
return { kind: 'Accept' };
};
Loading

0 comments on commit 183b7a3

Please sign in to comment.