This repository was archived by the owner on Jan 5, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy path_events.ts
97 lines (86 loc) · 3.38 KB
/
_events.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* eslint-disable @typescript-eslint/no-explicit-any */
// https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/domain-events-design-implementation
import * as T from "@effect-ts/core/Effect"
import * as EO from "@effect-ts-app/core/EffectOption"
import { flow, identity, tuple } from "@effect-ts-app/core/Function"
import * as O from "@effect-ts-app/core/Option"
import * as MO from "@effect-ts-app/core/Schema"
import { TaskEvents, TaskId, User, UserId, UserTask } from "@effect-ts-demo/todo-types"
import { TodoContext } from "@/services"
const matchTaskEvent = TaskEvents.Events.matchOne
const eventHandlers = tuple(
// Sample: Update the "User" aggregate to store the user state for this Task.
matchTaskEvent("TaskCreated", ({ myDay, taskId, userId }) =>
EO.genUnit(function* ($) {
const { Users } = yield* $(TodoContext.TodoContext)
const md = yield* $(myDay)
yield* $(
Users.update(
userId,
User.modifyUserTask(taskId, UserTask.lenses.myDay.set(O.some(md)))
)
)
})
),
matchTaskEvent("TaskUpdated", ({ taskId, userChanges, userId }) =>
EO.genUnit(function* ($) {
const { Users } = yield* $(TodoContext.TodoContext)
const { myDay, reminder } = userChanges
if (!myDay && !reminder) {
return
}
yield* $(
Users.update(
userId,
User.modifyUserTask(
taskId,
flow(
myDay ? UserTask.lenses.myDay.set(myDay) : identity,
reminder ? UserTask.lenses.reminder.set(reminder) : identity
)
)
)
)
})
),
// This is here just to test multiple handlers, and the merging of R and E accordingly.
matchTaskEvent("TaskCreated", () => TodoContext.getLoggedInUser["|>"](T.asUnit)),
// Sample for dispatching an Integration Event.
// On_TaskCreated_SendTaskCreatedEmail
matchTaskEvent("TaskCreated", ({ taskId, userId }) =>
publishIntegrationEvent(new SendTaskCreatedEmail({ taskId, userId }))
)
)
export function handleEvents<T extends TaskEvents.Events>(events: readonly T[]) {
return T.forEach_(events, (evt) => T.forEach_(eventHandlers, (x) => T.union(x(evt))))
}
//////
// Play with Integration events
function publishIntegrationEvent(evt: IntegrationEvents) {
// TODO:
// 1. store to database within same transaction to ensure capturing the event
// 2. background worker picks up the event from database
// 3. background worker executes the required steps for this integration event, e.g;
// - publish the event on a message bus, to be picked up by other micro services
// - send email via e.g SendGrid
return unimplemented("publishIntegrationEvent: " + JSON.stringify(evt, undefined, 2))
}
export function IntegrationEventProps<T extends string>(tag: T) {
return {
_tag: MO.prop(MO.literal(tag)),
id: MO.defaultProp(MO.UUID),
createdAt: MO.defaultProp(MO.date),
}
}
export class SendTaskCreatedEmail extends MO.Model<SendTaskCreatedEmail>()({
...IntegrationEventProps("SendTaskCreatedEmail"),
taskId: MO.prop(TaskId),
userId: MO.prop(UserId),
}) {}
export const IntegrationEvents = MO.union({
SendTaskCreatedEmail,
})
export type IntegrationEvents = MO.ParsedShapeOf<typeof IntegrationEvents>
function unimplemented(message: string) {
return T.succeedWith(() => console.warn("Called unimplemented: " + message))
}