Skip to content

Commit 00f2477

Browse files
committed
refactor: code improvements and update import paths
1 parent 383c238 commit 00f2477

7 files changed

+88
-66
lines changed

src/decorators/inject-repository.decorator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Inject, Type } from '@nestjs/common';
22

3-
import { getRepositoryToken } from '../utils/repository';
3+
import { getRepositoryToken } from '../utils';
44

55
export const InjectAggregateRepository = (
66
aggregate: Type<unknown>,

src/domain/models/event.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ export class Event<P = unknown> implements IEvent {
1616
private _payload: P;
1717
private _metadata: Metadata;
1818

19-
public constructor(aggregateId: string, payload: P) {
19+
public constructor(aggregateId: string, payload?: P) {
2020
this.eventId = uuid.v4();
21-
this._payload = Object.assign({}, payload);
21+
this._payload = { ...payload };
2222
this.eventType = Object.getPrototypeOf(this).constructor.name;
2323
this._metadata = {
2424
_aggregate_id: aggregateId,

src/eventstore-core.module.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { CqrsModule, EventBus } from '@nestjs/cqrs';
1010
import { ExplorerService } from '@nestjs/cqrs/dist/services/explorer.service';
1111
import { MongooseModule } from '@nestjs/mongoose';
1212

13-
import { KEYS, KeySchema, KeyService } from './crypto';
13+
import { KEYS, KeySchema } from './crypto';
1414
import { Event } from './domain';
1515
import { EventStore } from './eventstore';
1616
import { EventStoreCli } from './eventstore.cli';
@@ -20,11 +20,8 @@ import {
2020
EVENTSTORE_SETTINGS_TOKEN,
2121
} from './eventstore.constants';
2222
import { EventStoreMapper } from './eventstore.mapper';
23-
import {
24-
ConfigService,
25-
EventStoreModuleAsyncOptions,
26-
} from './interfaces/eventstore-module.interface';
27-
import { ProjectionsService, TransformerService } from './services';
23+
import { ConfigService, EventStoreModuleAsyncOptions } from './interfaces';
24+
import { KeyService, ProjectionsService, TransformerService } from './services';
2825

2926
@Global()
3027
@Module({

src/eventstore.cli.ts

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,40 @@
1-
import { EventStoreDBClient, FORWARDS, START } from '@eventstore/db-client';
2-
import { Inject } from '@nestjs/common';
1+
import {
2+
EventStoreDBClient,
3+
FORWARDS,
4+
ResolvedEvent,
5+
START,
6+
} from '@eventstore/db-client';
7+
import { Inject, Logger } from '@nestjs/common';
38
import { Command, Console } from 'nestjs-console';
49

10+
import { Event } from './domain';
511
import { Config } from './eventstore.config';
612
import { EVENTSTORE_SETTINGS_TOKEN } from './eventstore.constants';
713
import { EventStoreMapper } from './eventstore.mapper';
814
import { ProjectionsService } from './services';
915

1016
@Console()
1117
export class EventStoreCli {
12-
private client: EventStoreDBClient;
13-
private category: string;
18+
private readonly category: string;
19+
private readonly client: EventStoreDBClient;
20+
private readonly logger = new Logger(EventStoreCli.name);
21+
private readonly eventHandlers;
1422

1523
constructor(
1624
private readonly mapper: EventStoreMapper,
17-
private readonly projections: ProjectionsService,
18-
@Inject(EVENTSTORE_SETTINGS_TOKEN) private readonly config: Config,
25+
projections: ProjectionsService,
26+
@Inject(EVENTSTORE_SETTINGS_TOKEN) config: Config,
1927
) {
2028
this.client = EventStoreDBClient.connectionString(config.connection);
2129
this.category = config.category;
30+
this.eventHandlers = projections.eventHandlers();
2231
}
2332

2433
@Command({
2534
command: 'eventstore:readmodel:restore',
2635
description: 'Restore read model',
2736
})
2837
async restore(): Promise<void> {
29-
const eventHandlers = this.projections.eventHandlers();
30-
3138
let position: any = START;
3239
const MAX_COUNT = 1000;
3340

@@ -46,27 +53,35 @@ export class EventStoreCli {
4653
break;
4754
}
4855

49-
const lastResolvedEvent = resolvedEvents[resolvedEvents.length - 1];
50-
position = this.incrementRevision(lastResolvedEvent.link.revision);
56+
await this.handleResolvedEvents(resolvedEvents);
57+
position = this.calculateNextPosition(resolvedEvents);
58+
}
5159

52-
const events = await this.mapper.resolvedEventsToDomainEvents(
53-
resolvedEvents,
54-
);
60+
this.logger.log('Projections have been restored!');
61+
process.exit(0);
62+
}
5563

56-
for (const event of events) {
57-
const key = event.constructor.name;
64+
private async handleResolvedEvents(resolvedEvents: ResolvedEvent[]) {
65+
for (const resolvedEvent of resolvedEvents) {
66+
const event = await this.mapper.resolvedEventToDomainEvent(resolvedEvent);
5867

59-
for (const eventHandler of eventHandlers[key]) {
60-
await eventHandler.handle(event);
61-
}
62-
}
68+
if (!event) continue;
69+
70+
await this.handleEvent(event);
6371
}
72+
}
6473

65-
console.log('View db has been restored!');
66-
process.exit(0);
74+
private async handleEvent(event: Event) {
75+
const key = event.constructor.name;
76+
for (const eventHandler of this.eventHandlers[key]) {
77+
await eventHandler.handle(event);
78+
}
6779
}
6880

69-
private incrementRevision(revision: bigint): bigint {
81+
private calculateNextPosition(resolvedEvents: ResolvedEvent[]): bigint {
82+
const lastResolvedEvent = resolvedEvents[resolvedEvents.length - 1];
83+
const revision = lastResolvedEvent.link.revision;
84+
7085
return BigInt(Number(revision) + 1);
7186
}
7287
}

src/eventstore.mapper.ts

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
import { ResolvedEvent } from '@eventstore/db-client';
2-
import { Inject, Injectable } from '@nestjs/common';
1+
import { JSONType, ResolvedEvent } from '@eventstore/db-client';
2+
import { Inject, Injectable, Logger } from '@nestjs/common';
33

4-
import { KeyService } from './crypto';
54
import { Event, Metadata } from './domain';
5+
import { KeyNotFoundError } from './errors';
66
import { Config } from './eventstore.config';
77
import { EVENTSTORE_SETTINGS_TOKEN } from './eventstore.constants';
8-
import { TransformerService } from './services';
8+
import { KeyService, TransformerService } from './services';
99

1010
@Injectable()
1111
export class EventStoreMapper {
12+
private readonly logger = new Logger(EventStoreMapper.name);
13+
1214
constructor(
1315
@Inject(EVENTSTORE_SETTINGS_TOKEN) private readonly config: Config,
1416
private readonly transformers: TransformerService,
@@ -17,39 +19,48 @@ export class EventStoreMapper {
1719

1820
public async resolvedEventToDomainEvent(
1921
resolvedEvent: ResolvedEvent,
20-
): Promise<Event> | undefined {
22+
): Promise<Event> | null {
2123
if (
2224
resolvedEvent.event === undefined ||
2325
resolvedEvent.event.type.startsWith('$') ||
2426
!resolvedEvent.event.streamId.startsWith(this.config.category)
2527
) {
26-
return undefined;
28+
return null;
2729
}
2830

29-
const metadata = resolvedEvent.event.metadata as Metadata;
30-
let payload = resolvedEvent.event.data;
31+
try {
32+
const metadata = resolvedEvent.event.metadata as Metadata;
33+
const payload = await this.extractPayload(resolvedEvent);
34+
const transformer =
35+
this.transformers.getTransformerToEvent(resolvedEvent);
3136

32-
if (metadata._aggregate_encrypted) {
33-
payload = await this.keyService.decryptPayload(
34-
metadata._aggregate_id,
35-
metadata._encrypted_payload,
36-
);
37-
}
37+
const event = transformer?.(
38+
new Event(metadata._aggregate_id, payload),
39+
).withMetadata(metadata);
40+
41+
return event;
42+
} catch (error) {
43+
if (error instanceof KeyNotFoundError) {
44+
this.logger.error(
45+
`Error during decrypting ${resolvedEvent.event.type}: ${error.message}`,
46+
);
3847

39-
const event = this.transformers.repo[resolvedEvent.event.type]?.(
40-
new Event(metadata._aggregate_id, payload),
41-
).withMetadata(metadata);
48+
return null;
49+
}
4250

43-
return event;
51+
throw error;
52+
}
4453
}
4554

46-
public async resolvedEventsToDomainEvents(
47-
resolvedEvents: ResolvedEvent[],
48-
): Promise<Event[]> {
49-
return await Promise.all(
50-
resolvedEvents
51-
.map((resolvedEvent) => this.resolvedEventToDomainEvent(resolvedEvent))
52-
.filter((event) => event !== undefined),
53-
);
55+
private async extractPayload(
56+
resolvedEvent: ResolvedEvent,
57+
): Promise<JSONType | Uint8Array> {
58+
const metadata = resolvedEvent.event.metadata as Metadata;
59+
return metadata._aggregate_encrypted
60+
? await this.keyService.decryptPayload(
61+
metadata._aggregate_id,
62+
metadata._encrypted_payload,
63+
)
64+
: resolvedEvent.event.data;
5465
}
5566
}

src/eventstore.module.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ import { CqrsModule, EventPublisher } from '@nestjs/cqrs';
33
import { ConsoleModule } from 'nestjs-console';
44

55
import { AggregateRepository } from './aggregate.repository';
6-
import { KeyService } from './crypto';
76
import { AggregateRoot } from './domain';
87
import { EventStore } from './eventstore';
98
import { Config } from './eventstore.config';
109
import { EventStoreCoreModule } from './eventstore-core.module';
1110
import { EventStoreModuleAsyncOptions, TransformerRepo } from './interfaces';
12-
import { EVENT_STORE_TRANSFORMERS_TOKEN } from './services/transformer.service';
13-
import { getRepositoryToken } from './utils/repository';
11+
import { EVENT_STORE_TRANSFORMERS_TOKEN, KeyService } from './services';
12+
import { getRepositoryToken } from './utils';
1413

1514
@Module({})
1615
export class EventStoreModule {

src/eventstore.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import { IEventPublisher, IMessageSource } from '@nestjs/cqrs';
1414
import { Subject } from 'rxjs';
1515
import { v4 as uuid } from 'uuid';
1616

17-
import { KeyService } from './crypto';
1817
import { AggregateRoot, Event } from './domain';
1918
import { Config } from './eventstore.config';
2019
import { EVENTSTORE_SETTINGS_TOKEN } from './eventstore.constants';
2120
import { EventStoreMapper } from './eventstore.mapper';
21+
import { KeyService } from './services';
2222

2323
@Injectable()
2424
export class EventStore
@@ -84,12 +84,12 @@ export class EventStore
8484
entity.loadFromHistory(events);
8585

8686
return entity;
87-
} catch (err) {
88-
if (err?.type === ErrorType.STREAM_NOT_FOUND) {
87+
} catch (error) {
88+
if (error?.type === ErrorType.STREAM_NOT_FOUND) {
8989
return null;
9090
}
9191

92-
this.logger.debug(err);
92+
this.logger.debug(error);
9393
}
9494

9595
return null;
@@ -111,8 +111,8 @@ export class EventStore
111111
resolveLinkTos: true,
112112
})
113113
.on('data', onEvent);
114-
} catch (err) {
115-
this.logger.debug(err);
114+
} catch (error) {
115+
this.logger.debug(error);
116116
}
117117
}
118118
}

0 commit comments

Comments
 (0)