1 | 'use strict';
|
2 |
|
3 | const uuid = require('uuid/v4');
|
4 | const formats = require('formats');
|
5 |
|
6 | const MESSAGE_TYPE = 'event';
|
7 |
|
8 | const EVENT_TYPES = {
|
9 | CRUD: 'crud',
|
10 | DOMAIN: 'domain',
|
11 | DENORMALIZER: 'denormalizer',
|
12 | SYSTEM: 'system',
|
13 | };
|
14 |
|
15 | const reversedEventType = Object.entries(EVENT_TYPES).reduce((rev, [, value]) => {
|
16 | rev[value] = true;
|
17 | return rev;
|
18 | }, {});
|
19 |
|
20 | const fullNameGenerator = {
|
21 | [EVENT_TYPES.DOMAIN]: e => `${EVENT_TYPES.DOMAIN}.${e.context}.${e.aggregate.name}.${e.name}`,
|
22 | [EVENT_TYPES.DENORMALIZER]: e => `${EVENT_TYPES.DENORMALIZER}.${e.readmodel.collection}.${e.name}`,
|
23 | [EVENT_TYPES.SYSTEM]: e => `${EVENT_TYPES.SYSTEM}.${e.name}`,
|
24 | [EVENT_TYPES.CRUD]: e => `${EVENT_TYPES.CRUD}.${e.context}.${e.readmodel.collection}.${e.name}`,
|
25 | };
|
26 |
|
27 | class Event {
|
28 | constructor({
|
29 | type,
|
30 | id,
|
31 | context,
|
32 | aggregate,
|
33 | event,
|
34 | command,
|
35 | name,
|
36 | readmodel = {},
|
37 | payload = {},
|
38 | metadata = {},
|
39 | }) {
|
40 | if (!type || !reversedEventType[type])
|
41 | throw new Error('Invalid event type');
|
42 |
|
43 | if (!formats.isAlphanumeric(name, { minLength: 1 }))
|
44 | throw new Error('Command name is missing.');
|
45 |
|
46 | if (!formats.isObject(payload))
|
47 | throw new Error('Payload must be an object.');
|
48 |
|
49 | if (!formats.isObject(metadata))
|
50 | throw new Error('Metadata must be an object.');
|
51 |
|
52 | this.type = type;
|
53 | this.id = id || uuid();
|
54 | this.name = name;
|
55 | this.payload = payload;
|
56 | this.metadata = metadata;
|
57 | this.command = command;
|
58 |
|
59 | if (type === EVENT_TYPES.CRUD) {
|
60 | if (!formats.isAlphanumeric(context, { minLength: 1 }))
|
61 | throw new Error('Invalid context.');
|
62 |
|
63 | if (!formats.isObject(readmodel))
|
64 | throw new Error('Invalid readmodel.');
|
65 |
|
66 | if (!formats.isAlphanumeric(readmodel.collection, { minLength: 1 }))
|
67 | throw new Error('Readmodel collection is missing.');
|
68 |
|
69 | this.context = context;
|
70 | this.readmodel = readmodel;
|
71 | }
|
72 |
|
73 | if (type === EVENT_TYPES.DENORMALIZER || type === EVENT_TYPES.DOMAIN) {
|
74 | if (!formats.isAlphanumeric(context, { minLength: 1 }))
|
75 | throw new Error('Invalid context.');
|
76 |
|
77 | if (!formats.isObject(aggregate))
|
78 | throw new Error('Invalid aggregate.');
|
79 |
|
80 | if (!formats.isAlphanumeric(aggregate.name, { minLength: 1 }))
|
81 | throw new Error('Aggregate name is missing.');
|
82 |
|
83 | if (!formats.isString(aggregate.id, { minLength: 1 }))
|
84 | throw new Error('Aggregate id is missing.');
|
85 |
|
86 | this.context = context;
|
87 | this.aggregate = { name: aggregate.name, id: aggregate.id, revision: aggregate.revision };
|
88 |
|
89 | if (type === EVENT_TYPES.DENORMALIZER) {
|
90 | if (!formats.isObject(event))
|
91 | throw new Error('Invalid event.');
|
92 |
|
93 | if (!formats.isAlphanumeric(event.name, { minLength: 1 }))
|
94 | throw new Error('Event name is missing.');
|
95 |
|
96 | if (!formats.isString(event.id, { minLength: 1 }))
|
97 | throw new Error('Event id is missing.');
|
98 |
|
99 | if (!formats.isObject(readmodel))
|
100 | throw new Error('Invalid readmodel.');
|
101 |
|
102 | if (!formats.isAlphanumeric(readmodel.collection, { minLength: 1 }))
|
103 | throw new Error('Readmodel collection is missing.');
|
104 |
|
105 | this.event = { name: event.name, id: event.id };
|
106 | this.readmodel = readmodel;
|
107 | }
|
108 | }
|
109 | this.fullname = fullNameGenerator[type](this);
|
110 | this.routingKey = `event.${this.fullname}`;
|
111 | }
|
112 |
|
113 | get messageType() {
|
114 | return MESSAGE_TYPE;
|
115 | }
|
116 |
|
117 | static get fullNameGenerator() {
|
118 | return fullNameGenerator;
|
119 | }
|
120 |
|
121 | serialize() {
|
122 | return JSON.parse(JSON.stringify(this));
|
123 | }
|
124 |
|
125 | setCorrelationId(v) {
|
126 | this.metadata.correlationId = v;
|
127 | return this;
|
128 | }
|
129 |
|
130 | static deserialize({
|
131 | type,
|
132 | id,
|
133 | context,
|
134 | aggregate,
|
135 | event,
|
136 | name,
|
137 | readmodel,
|
138 | payload,
|
139 | metadata,
|
140 | command,
|
141 | }) {
|
142 | return new Event({
|
143 | type,
|
144 | id,
|
145 | context,
|
146 | aggregate,
|
147 | event,
|
148 | name,
|
149 | readmodel,
|
150 | payload,
|
151 | metadata,
|
152 | command,
|
153 | });
|
154 | }
|
155 |
|
156 | static definition() {
|
157 | return {
|
158 | id: 'id',
|
159 | context: 'context',
|
160 |
|
161 |
|
162 | aggregateId: 'aggregate.id',
|
163 | aggregate: 'aggregate.name',
|
164 | revision: 'aggregate.revision',
|
165 |
|
166 |
|
167 | event: 'event.name',
|
168 | eventId: 'event.id',
|
169 |
|
170 |
|
171 | collection: 'readmodel.collection',
|
172 |
|
173 |
|
174 | name: 'name',
|
175 |
|
176 | payload: 'payload',
|
177 |
|
178 |
|
179 | meta: 'metadata',
|
180 | correlationId: 'metadata.correlationId',
|
181 | };
|
182 | }
|
183 |
|
184 | static get EVENT_TYPES() {
|
185 | return EVENT_TYPES;
|
186 | }
|
187 | }
|
188 |
|
189 | module.exports = Event;
|