UNPKG

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