refactor broker to use a single exchange

This commit is contained in:
sbriat 2023-03-15 17:01:23 +01:00
parent 9c2a2f96e5
commit 705de1bcf8
14 changed files with 66 additions and 124 deletions

View File

@ -8,6 +8,7 @@ DATABASE_URL="postgresql://user:user@v3-user-db:5432/user?schema=public"
# RABBIT MQ # RABBIT MQ
RMQ_URI=amqp://v3-broker:5672 RMQ_URI=amqp://v3-broker:5672
RMQ_EXCHANGE=mobicoop
# POSTGRES # POSTGRES
POSTGRES_IMAGE=postgres:15.0 POSTGRES_IMAGE=postgres:15.0

View File

@ -1,11 +1,15 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { IMessageBroker } from '../../domain/interfaces/message-broker'; import { IMessageBroker } from '../../domain/interfaces/message-broker';
@Injectable() @Injectable()
export class LoggingMessager extends IMessageBroker { export class Messager extends IMessageBroker {
constructor(private readonly _amqpConnection: AmqpConnection) { constructor(
super('logging'); private readonly _amqpConnection: AmqpConnection,
configService: ConfigService,
) {
super(configService.get<string>('RMQ_EXCHANGE'));
} }
publish(routingKey: string, message: string): void { publish(routingKey: string, message: string): void {

View File

@ -1,14 +0,0 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Injectable } from '@nestjs/common';
import { IMessageBroker } from '../../domain/interfaces/message-broker';
@Injectable()
export class UserMessager extends IMessageBroker {
constructor(private readonly _amqpConnection: AmqpConnection) {
super('user');
}
publish(routingKey: string, message: string): void {
this._amqpConnection.publish(this.exchange, routingKey, message);
}
}

View File

@ -1,8 +1,7 @@
import { Mapper } from '@automapper/core'; import { Mapper } from '@automapper/core';
import { InjectMapper } from '@automapper/nestjs'; import { InjectMapper } from '@automapper/nestjs';
import { CommandHandler } from '@nestjs/cqrs'; import { CommandHandler } from '@nestjs/cqrs';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager'; import { Messager } from '../../adapters/secondaries/messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository'; import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { CreateUserCommand } from '../../commands/create-user.command'; import { CreateUserCommand } from '../../commands/create-user.command';
import { CreateUserRequest } from '../dtos/create-user.request'; import { CreateUserRequest } from '../dtos/create-user.request';
@ -12,8 +11,7 @@ import { User } from '../entities/user';
export class CreateUserUseCase { export class CreateUserUseCase {
constructor( constructor(
private readonly _repository: UsersRepository, private readonly _repository: UsersRepository,
private readonly _userMessager: UserMessager, private readonly _messager: Messager,
private readonly _loggingMessager: LoggingMessager,
@InjectMapper() private readonly _mapper: Mapper, @InjectMapper() private readonly _mapper: Mapper,
) {} ) {}
@ -26,15 +24,15 @@ export class CreateUserUseCase {
try { try {
const user = await this._repository.create(entity); const user = await this._repository.create(entity);
this._userMessager.publish('create', JSON.stringify(user)); this._messager.publish('user.create', JSON.stringify(user));
this._loggingMessager.publish('user.create.info', JSON.stringify(user)); this._messager.publish('logging.user.create.info', JSON.stringify(user));
return user; return user;
} catch (error) { } catch (error) {
let key = 'user.create.crit'; let key = 'logging.user.create.crit';
if (error.message.includes('Already exists')) { if (error.message.includes('Already exists')) {
key = 'user.create.warning'; key = 'logging.user.create.warning';
} }
this._loggingMessager.publish( this._messager.publish(
key, key,
JSON.stringify({ JSON.stringify({
command, command,

View File

@ -1,6 +1,5 @@
import { CommandHandler } from '@nestjs/cqrs'; import { CommandHandler } from '@nestjs/cqrs';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager'; import { Messager } from '../../adapters/secondaries/messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository'; import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { DeleteUserCommand } from '../../commands/delete-user.command'; import { DeleteUserCommand } from '../../commands/delete-user.command';
import { User } from '../entities/user'; import { User } from '../entities/user';
@ -9,22 +8,24 @@ import { User } from '../entities/user';
export class DeleteUserUseCase { export class DeleteUserUseCase {
constructor( constructor(
private readonly _repository: UsersRepository, private readonly _repository: UsersRepository,
private readonly _userMessager: UserMessager, private readonly _messager: Messager,
private readonly _loggingMessager: LoggingMessager,
) {} ) {}
async execute(command: DeleteUserCommand): Promise<User> { async execute(command: DeleteUserCommand): Promise<User> {
try { try {
const user = await this._repository.delete(command.uuid); const user = await this._repository.delete(command.uuid);
this._userMessager.publish('delete', JSON.stringify({ uuid: user.uuid })); this._messager.publish(
this._loggingMessager.publish( 'user.delete',
'user.delete.info', JSON.stringify({ uuid: user.uuid }),
);
this._messager.publish(
'logging.user.delete.info',
JSON.stringify({ uuid: user.uuid }), JSON.stringify({ uuid: user.uuid }),
); );
return user; return user;
} catch (error) { } catch (error) {
this._loggingMessager.publish( this._messager.publish(
'user.delete.crit', 'logging.user.delete.crit',
JSON.stringify({ JSON.stringify({
command, command,
error, error,

View File

@ -1,6 +1,6 @@
import { NotFoundException } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common';
import { QueryHandler } from '@nestjs/cqrs'; import { QueryHandler } from '@nestjs/cqrs';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager'; import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository'; import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { FindUserByUuidQuery } from '../../queries/find-user-by-uuid.query'; import { FindUserByUuidQuery } from '../../queries/find-user-by-uuid.query';
import { User } from '../entities/user'; import { User } from '../entities/user';
@ -9,7 +9,7 @@ import { User } from '../entities/user';
export class FindUserByUuidUseCase { export class FindUserByUuidUseCase {
constructor( constructor(
private readonly _repository: UsersRepository, private readonly _repository: UsersRepository,
private readonly _loggingMessager: LoggingMessager, private readonly _messager: Messager,
) {} ) {}
async execute(findUserByUuid: FindUserByUuidQuery): Promise<User> { async execute(findUserByUuid: FindUserByUuidQuery): Promise<User> {
@ -18,8 +18,8 @@ export class FindUserByUuidUseCase {
if (!user) throw new NotFoundException(); if (!user) throw new NotFoundException();
return user; return user;
} catch (error) { } catch (error) {
this._loggingMessager.publish( this._messager.publish(
'user.read.warning', 'logging.user.read.warning',
JSON.stringify({ JSON.stringify({
query: findUserByUuid, query: findUserByUuid,
error, error,

View File

@ -1,8 +1,7 @@
import { Mapper } from '@automapper/core'; import { Mapper } from '@automapper/core';
import { InjectMapper } from '@automapper/nestjs'; import { InjectMapper } from '@automapper/nestjs';
import { CommandHandler } from '@nestjs/cqrs'; import { CommandHandler } from '@nestjs/cqrs';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager'; import { Messager } from '../../adapters/secondaries/messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository'; import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { UpdateUserCommand } from '../../commands/update-user.command'; import { UpdateUserCommand } from '../../commands/update-user.command';
import { UpdateUserRequest } from '../dtos/update-user.request'; import { UpdateUserRequest } from '../dtos/update-user.request';
@ -12,8 +11,7 @@ import { User } from '../entities/user';
export class UpdateUserUseCase { export class UpdateUserUseCase {
constructor( constructor(
private readonly _repository: UsersRepository, private readonly _repository: UsersRepository,
private readonly _userMessager: UserMessager, private readonly _messager: Messager,
private readonly _loggingMessager: LoggingMessager,
@InjectMapper() private readonly _mapper: Mapper, @InjectMapper() private readonly _mapper: Mapper,
) {} ) {}
@ -29,18 +27,18 @@ export class UpdateUserUseCase {
command.updateUserRequest.uuid, command.updateUserRequest.uuid,
entity, entity,
); );
this._userMessager.publish( this._messager.publish(
'update', 'user.update',
JSON.stringify(command.updateUserRequest), JSON.stringify(command.updateUserRequest),
); );
this._loggingMessager.publish( this._messager.publish(
'user.update.info', 'logging.user.update.info',
JSON.stringify(command.updateUserRequest), JSON.stringify(command.updateUserRequest),
); );
return user; return user;
} catch (error) { } catch (error) {
this._loggingMessager.publish( this._messager.publish(
'user.update.crit', 'logging.user.update.crit',
JSON.stringify({ JSON.stringify({
command, command,
error, error,

View File

@ -1,8 +1,7 @@
import { classes } from '@automapper/classes'; import { classes } from '@automapper/classes';
import { AutomapperModule } from '@automapper/nestjs'; import { AutomapperModule } from '@automapper/nestjs';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager'; import { Messager } from '../../adapters/secondaries/messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository'; import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { CreateUserCommand } from '../../commands/create-user.command'; import { CreateUserCommand } from '../../commands/create-user.command';
import { CreateUserRequest } from '../../domain/dtos/create-user.request'; import { CreateUserRequest } from '../../domain/dtos/create-user.request';
@ -50,11 +49,7 @@ describe('CreateUserUseCase', () => {
CreateUserUseCase, CreateUserUseCase,
UserProfile, UserProfile,
{ {
provide: UserMessager, provide: Messager,
useValue: mockMessager,
},
{
provide: LoggingMessager,
useValue: mockMessager, useValue: mockMessager,
}, },
], ],

View File

@ -1,6 +1,5 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager'; import { Messager } from '../../adapters/secondaries/messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository'; import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { DeleteUserCommand } from '../../commands/delete-user.command'; import { DeleteUserCommand } from '../../commands/delete-user.command';
import { DeleteUserUseCase } from '../../domain/usecases/delete-user.usecase'; import { DeleteUserUseCase } from '../../domain/usecases/delete-user.usecase';
@ -63,11 +62,7 @@ describe('DeleteUserUseCase', () => {
}, },
DeleteUserUseCase, DeleteUserUseCase,
{ {
provide: UserMessager, provide: Messager,
useValue: mockMessager,
},
{
provide: LoggingMessager,
useValue: mockMessager, useValue: mockMessager,
}, },
], ],

View File

@ -1,6 +1,6 @@
import { NotFoundException } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager'; import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository'; import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { FindUserByUuidRequest } from '../../domain/dtos/find-user-by-uuid.request'; import { FindUserByUuidRequest } from '../../domain/dtos/find-user-by-uuid.request';
import { FindUserByUuidUseCase } from '../../domain/usecases/find-user-by-uuid.usecase'; import { FindUserByUuidUseCase } from '../../domain/usecases/find-user-by-uuid.usecase';
@ -42,7 +42,7 @@ describe('FindUserByUuidUseCase', () => {
useValue: mockUserRepository, useValue: mockUserRepository,
}, },
{ {
provide: LoggingMessager, provide: Messager,
useValue: mockMessager, useValue: mockMessager,
}, },
FindUserByUuidUseCase, FindUserByUuidUseCase,

View File

@ -1,36 +1,47 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager'; import { Messager } from '../../adapters/secondaries/messager';
const mockAmqpConnection = { const mockAmqpConnection = {
publish: jest.fn().mockImplementation(), publish: jest.fn().mockImplementation(),
}; };
describe('LoggingMessager', () => { const mockConfigService = {
let loggingMessager: LoggingMessager; get: jest.fn().mockResolvedValue({
RMQ_EXCHANGE: 'mobicoop',
}),
};
describe('Messager', () => {
let messager: Messager;
beforeAll(async () => { beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
imports: [], imports: [],
providers: [ providers: [
LoggingMessager, Messager,
{ {
provide: AmqpConnection, provide: AmqpConnection,
useValue: mockAmqpConnection, useValue: mockAmqpConnection,
}, },
{
provide: ConfigService,
useValue: mockConfigService,
},
], ],
}).compile(); }).compile();
loggingMessager = module.get<LoggingMessager>(LoggingMessager); messager = module.get<Messager>(Messager);
}); });
it('should be defined', () => { it('should be defined', () => {
expect(LoggingMessager).toBeDefined(); expect(messager).toBeDefined();
}); });
it('should publish a message', async () => { it('should publish a message', async () => {
jest.spyOn(mockAmqpConnection, 'publish'); jest.spyOn(mockAmqpConnection, 'publish');
await loggingMessager.publish('user.create.info', 'my-test'); messager.publish('user.create.info', 'my-test');
expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1); expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1);
}); });
}); });

View File

@ -1,8 +1,7 @@
import { classes } from '@automapper/classes'; import { classes } from '@automapper/classes';
import { AutomapperModule } from '@automapper/nestjs'; import { AutomapperModule } from '@automapper/nestjs';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager'; import { Messager } from '../../adapters/secondaries/messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository'; import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { UpdateUserCommand } from '../../commands/update-user.command'; import { UpdateUserCommand } from '../../commands/update-user.command';
import { UpdateUserRequest } from '../../domain/dtos/update-user.request'; import { UpdateUserRequest } from '../../domain/dtos/update-user.request';
@ -55,11 +54,7 @@ describe('UpdateUserUseCase', () => {
UpdateUserUseCase, UpdateUserUseCase,
UserProfile, UserProfile,
{ {
provide: UserMessager, provide: Messager,
useValue: mockMessager,
},
{
provide: LoggingMessager,
useValue: mockMessager, useValue: mockMessager,
}, },
], ],

View File

@ -1,36 +0,0 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Test, TestingModule } from '@nestjs/testing';
import { UserMessager } from '../../adapters/secondaries/user.messager';
const mockAmqpConnection = {
publish: jest.fn().mockImplementation(),
};
describe('UserMessager', () => {
let userMessager: UserMessager;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [],
providers: [
UserMessager,
{
provide: AmqpConnection,
useValue: mockAmqpConnection,
},
],
}).compile();
userMessager = module.get<UserMessager>(UserMessager);
});
it('should be defined', () => {
expect(userMessager).toBeDefined();
});
it('should publish a message', async () => {
jest.spyOn(mockAmqpConnection, 'publish');
await userMessager.publish('user.create.info', 'my-test');
expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1);
});
});

View File

@ -6,8 +6,7 @@ import { CqrsModule } from '@nestjs/cqrs';
import { redisStore } from 'cache-manager-ioredis-yet'; import { redisStore } from 'cache-manager-ioredis-yet';
import { DatabaseModule } from '../database/database.module'; import { DatabaseModule } from '../database/database.module';
import { UsersController } from './adapters/primaries/users.controller'; import { UsersController } from './adapters/primaries/users.controller';
import { LoggingMessager } from './adapters/secondaries/logging.messager'; import { Messager } from './adapters/secondaries/messager';
import { UserMessager } from './adapters/secondaries/user.messager';
import { UsersRepository } from './adapters/secondaries/users.repository'; import { UsersRepository } from './adapters/secondaries/users.repository';
import { CreateUserUseCase } from './domain/usecases/create-user.usecase'; import { CreateUserUseCase } from './domain/usecases/create-user.usecase';
import { DeleteUserUseCase } from './domain/usecases/delete-user.usecase'; import { DeleteUserUseCase } from './domain/usecases/delete-user.usecase';
@ -25,11 +24,7 @@ import { UserProfile } from './mappers/user.profile';
useFactory: async (configService: ConfigService) => ({ useFactory: async (configService: ConfigService) => ({
exchanges: [ exchanges: [
{ {
name: 'user', name: configService.get<string>('RMQ_EXCHANGE'),
type: 'topic',
},
{
name: 'logging',
type: 'topic', type: 'topic',
}, },
], ],
@ -54,8 +49,7 @@ import { UserProfile } from './mappers/user.profile';
providers: [ providers: [
UserProfile, UserProfile,
UsersRepository, UsersRepository,
UserMessager, Messager,
LoggingMessager,
FindAllUsersUseCase, FindAllUsersUseCase,
FindUserByUuidUseCase, FindUserByUuidUseCase,
CreateUserUseCase, CreateUserUseCase,