Skip to content

Commit

Permalink
Add collaborator domain #172
Browse files Browse the repository at this point in the history
  • Loading branch information
mapeveri committed Dec 16, 2024
1 parent cbe5bbe commit 63ab151
Show file tree
Hide file tree
Showing 20 changed files with 287 additions and 86 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ RABBITMQ_DEFAULT_USER=app
RABBITMQ_DEFAULT_PASS=rabbit_app
RABBITMQ_EVENTS_QUEUE='events_queue'
RABBITMQ_HOST="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672"
SERVER_URL=http://localhost:4000
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ RABBITMQ_DEFAULT_USER=app
RABBITMQ_DEFAULT_PASS=rabbit_app
RABBITMQ_EVENTS_QUEUE='events_queue'
RABBITMQ_HOST="amqp://app:rabbit_app@localhost:5672"
SERVER_URL=http://localhost:4000
6 changes: 6 additions & 0 deletions src/languages/_dependencyInjection/repositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { COUNTRY_REPOSITORY } from '@src/languages/domain/country/countryReposit
import { TERM_REPOSITORY } from '@src/languages/domain/term/termRepository';
import MikroOrmCountryRepository from '../infrastructure/persistence/mikroOrm/repositories/mikroOrmCountryRepository';
import MikroOrmTermRepository from '../infrastructure/persistence/mikroOrm/repositories/mikroOrmTermRepository';
import { COLLABORATOR_REPOSITORY } from '@src/languages/domain/collaborator/collaboratorRepository';
import { TranslatingCollaboratorRepository } from '@src/languages/infrastructure/persistence/http/TranslatingCollaboratorRepository';

export const repositories = [
{
Expand All @@ -12,4 +14,8 @@ export const repositories = [
provide: TERM_REPOSITORY,
useClass: MikroOrmTermRepository,
},
{
provide: COLLABORATOR_REPOSITORY,
useClass: TranslatingCollaboratorRepository,
},
];
5 changes: 5 additions & 0 deletions src/languages/_dependencyInjection/services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { HttpCollaboratorAdapter } from '@src/languages/infrastructure/persistence/http/httpCollaboratorAdapter';

export const services = [
HttpCollaboratorAdapter
];
28 changes: 15 additions & 13 deletions src/languages/application/term/command/addLikeTermCommandHandler.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
import { CommandHandler, ICommandHandler } from '@src/shared/domain/bus/commandBus/commandHandler';
import AddLikeTermCommand from '@src/languages/application/term/command/addLikeTermCommand';
import TermId from '@src/languages/domain/term/termId';
import UserId from '@src/account/domain/user/userId';
import { Inject } from '@src/shared/domain/injector/inject.decorator';
import TermRepository, { TERM_REPOSITORY } from '@src/languages/domain/term/termRepository';
import Term from '@src/languages/domain/term/term';
import TermDoesNotExistsException from '@src/languages/domain/term/termDoesNotExistsException';
import UserRepository, { USER_REPOSITORY } from '@src/account/domain/user/userRepository';
import UserDoesNotExistsException from '@src/account/domain/user/userDoesNotExistsException';
import User from '@src/account/domain/user/user';
import { ASYNC_EVENT_BUS, EventBus } from '@src/shared/domain/bus/eventBus/eventBus';
import CollaboratorRepository, {
COLLABORATOR_REPOSITORY,
} from '@src/languages/domain/collaborator/collaboratorRepository';
import CollaboratorId from '@src/languages/domain/collaborator/collaboratorId';
import Collaborator from '@src/languages/domain/collaborator/collaborator';
import CollaboratorDoesNotExistsException from '@src/languages/domain/collaborator/collaboratorDoesNotExistsException';

@CommandHandler(AddLikeTermCommand)
export default class AddLikeTermCommandHandler implements ICommandHandler<AddLikeTermCommand> {
constructor(
@Inject(TERM_REPOSITORY) private readonly termRepository: TermRepository,
@Inject(USER_REPOSITORY) private readonly userRepository: UserRepository,
@Inject(COLLABORATOR_REPOSITORY) private readonly collaboratorRepository: CollaboratorRepository,
@Inject(ASYNC_EVENT_BUS) private readonly eventBus: EventBus,
) {}

async execute(command: AddLikeTermCommand): Promise<void> {
const termId = TermId.of(command.termId);
const userId = UserId.of(command.userId);
const collaboratorId = CollaboratorId.of(command.userId);

const term = await this.getTerm(termId);
const user = await this.getUser(userId);
const collaborator = await this.getCollaborator(collaboratorId);

term.addLike(userId, user.getName(), user.getPhoto());
term.addLike(collaboratorId, collaborator.getName(), collaborator.getPhoto());

this.termRepository.save(term);

Expand All @@ -42,12 +44,12 @@ export default class AddLikeTermCommandHandler implements ICommandHandler<AddLik
return term;
}

private async getUser(userId: UserId): Promise<User> {
const user = await this.userRepository.findById(userId);
if (!user) {
throw new UserDoesNotExistsException(userId.toString());
private async getCollaborator(collaboratorId: CollaboratorId): Promise<Collaborator> {
const collaborator = await this.collaboratorRepository.findById(collaboratorId);
if (!collaborator) {
throw new CollaboratorDoesNotExistsException(collaboratorId.toString());
}

return user;
return collaborator;
}
}
28 changes: 15 additions & 13 deletions src/languages/application/term/command/dislikeTermCommandHandler.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
import { CommandHandler, ICommandHandler } from '@src/shared/domain/bus/commandBus/commandHandler';
import TermId from '@src/languages/domain/term/termId';
import UserId from '@src/account/domain/user/userId';
import DislikeTermCommand from '@src/languages/application/term/command/dislikeTermCommand';
import Term from '@src/languages/domain/term/term';
import TermDoesNotExistsException from '@src/languages/domain/term/termDoesNotExistsException';
import User from '@src/account/domain/user/user';
import UserDoesNotExistsException from '@src/account/domain/user/userDoesNotExistsException';
import { Inject } from '@src/shared/domain/injector/inject.decorator';
import TermRepository, { TERM_REPOSITORY } from '@src/languages/domain/term/termRepository';
import UserRepository, { USER_REPOSITORY } from '@src/account/domain/user/userRepository';
import { ASYNC_EVENT_BUS, EventBus } from '@src/shared/domain/bus/eventBus/eventBus';
import CollaboratorRepository, {
COLLABORATOR_REPOSITORY,
} from '@src/languages/domain/collaborator/collaboratorRepository';
import Collaborator from '@src/languages/domain/collaborator/collaborator';
import CollaboratorId from '@src/languages/domain/collaborator/collaboratorId';
import CollaboratorDoesNotExistsException from '@src/languages/domain/collaborator/collaboratorDoesNotExistsException';

@CommandHandler(DislikeTermCommand)
export default class DislikeTermCommandHandler implements ICommandHandler<DislikeTermCommand> {
constructor(
@Inject(TERM_REPOSITORY) private readonly termRepository: TermRepository,
@Inject(USER_REPOSITORY) private readonly userRepository: UserRepository,
@Inject(COLLABORATOR_REPOSITORY) private readonly collaboratorRepository: CollaboratorRepository,
@Inject(ASYNC_EVENT_BUS) private readonly eventBus: EventBus,
) {}

async execute(command: DislikeTermCommand): Promise<void> {
const termId = TermId.of(command.termId);
const userId = UserId.of(command.userId);
const collaboratorId = CollaboratorId.of(command.userId);

const term = await this.getTerm(termId);
const user = await this.getUser(userId);
const collaborator = await this.getCollaborator(collaboratorId);

term.dislike(userId, user.getName(), user.getPhoto());
term.dislike(collaboratorId, collaborator.getName(), collaborator.getPhoto());

this.termRepository.save(term);

Expand All @@ -42,12 +44,12 @@ export default class DislikeTermCommandHandler implements ICommandHandler<Dislik
return term;
}

private async getUser(userId: UserId): Promise<User> {
const user = await this.userRepository.findById(userId);
if (!user) {
throw new UserDoesNotExistsException(userId.toString());
private async getCollaborator(collaboratorId: CollaboratorId): Promise<Collaborator> {
const collaborator = await this.collaboratorRepository.findById(collaboratorId);
if (!collaborator) {
throw new CollaboratorDoesNotExistsException(collaboratorId.toString());
}

return user;
return collaborator;
}
}
36 changes: 36 additions & 0 deletions src/languages/domain/collaborator/collaborator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { AggregateRoot } from '@src/shared/domain/aggregate/aggregateRoot';
import CollaboratorId from '@src/languages/domain/collaborator/collaboratorId';

export type CollaboratorPrimitives = {
id: string;
name: string;
photo: string;
interests: string[];
};

export default class Collaborator extends AggregateRoot {
constructor(private id: CollaboratorId, private name: string, private photo: string, private interests: string[]) {
super();
}

public getName(): string {
return this.name;
}

public getPhoto(): string {
return this.photo;
}

public getInterests(): string[] {
return this.interests;
}

toPrimitives(): CollaboratorPrimitives {
return {
id: this.id.toString(),
name: this.name,
photo: this.photo,
interests: this.interests,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import NotFoundException from '@src/shared/domain/exceptions/notFoundException';

export default class CollaboratorDoesNotExistsException extends NotFoundException {
constructor(collaboratorId: string) {
super(`Collaborator ${collaboratorId} does not exists`, 'collaborator_does_not_exists');
}
}
11 changes: 11 additions & 0 deletions src/languages/domain/collaborator/collaboratorId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Uuid } from '@src/shared/domain/valueObjects/uuid';

export default class CollaboratorId extends Uuid {
static of(value: string): CollaboratorId {
return super.of(value) as CollaboratorId;
}

static fromPrimitives(value: string): CollaboratorId {
return super.fromPrimitives(value) as CollaboratorId;
}
}
10 changes: 10 additions & 0 deletions src/languages/domain/collaborator/collaboratorRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Collaborator from '@src/languages/domain/collaborator/collaborator';
import CollaboratorId from '@src/languages/domain/collaborator/collaboratorId';

interface CollaboratorRepository {
findById(id: CollaboratorId): Promise<Collaborator | null>;
}

export default CollaboratorRepository;

export const COLLABORATOR_REPOSITORY = Symbol('CollaboratorRepository');
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import CollaboratorRepository from '@src/languages/domain/collaborator/collaboratorRepository';
import Collaborator from '@src/languages/domain/collaborator/collaborator';
import CollaboratorId from '@src/languages/domain/collaborator/collaboratorId';
import { Injectable } from '@nestjs/common';
import { HttpCollaboratorAdapter } from '@src/languages/infrastructure/persistence/http/httpCollaboratorAdapter';

@Injectable()
export class TranslatingCollaboratorRepository implements CollaboratorRepository {
constructor(private readonly httpCollaboratorAdapter: HttpCollaboratorAdapter) {}

async findById(id: CollaboratorId): Promise<Collaborator | null> {
return this.httpCollaboratorAdapter.toCollaborator(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Collaborator from '@src/languages/domain/collaborator/collaborator';
import { AxiosResponse } from 'axios';

export class CollaboratorTranslator {
static toCollaborator(collaboratorResponse: AxiosResponse): Collaborator {
const collaborator = collaboratorResponse.data;
return new Collaborator(collaborator.id, collaborator.name, collaborator.photo, collaborator.interests);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import CollaboratorId from '@src/languages/domain/collaborator/collaboratorId';
import Collaborator from '@src/languages/domain/collaborator/collaborator';
import { CollaboratorTranslator } from '@src/languages/infrastructure/persistence/http/collaboratorTranslator';

@Injectable()
export class HttpCollaboratorAdapter {
URL = '/users/{id}';
constructor(private readonly httpService: HttpService) {}

async toCollaborator(id: CollaboratorId): Promise<Collaborator | null> {
const { data } = await firstValueFrom(this.httpService.get(this.URL.replace('{id}', id.toString)));

return CollaboratorTranslator.toCollaborator(data);
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import FindSuggestionsTermReadModel from '@src/languages/application/term/query/findSuggestionsTermReadModel';
import UserId from '@src/account/domain/user/userId';
import { TermView } from '@src/languages/application/term/query/termView';
import UserRepository, { USER_REPOSITORY } from '@src/account/domain/user/userRepository';
import { Inject } from '@src/shared/domain/injector/inject.decorator';
import { Document } from 'mongodb';
import MongoConnection, { MONGO_CLIENT } from '@src/shared/infrastructure/persistence/mongo/mongoConnection';
import User from '@src/account/domain/user/user';
import UserDoesNotExistsException from '@src/account/domain/user/userDoesNotExistsException';
import CollaboratorRepository, {
COLLABORATOR_REPOSITORY,
} from '@src/languages/domain/collaborator/collaboratorRepository';
import Collaborator from '@src/languages/domain/collaborator/collaborator';
import CollaboratorDoesNotExistsException from '@src/languages/domain/collaborator/collaboratorDoesNotExistsException';
import CollaboratorId from '@src/languages/domain/collaborator/collaboratorId';

export default class MongoFindSuggestionsTermReadModel implements FindSuggestionsTermReadModel {
constructor(
@Inject(MONGO_CLIENT) private readonly mongo: MongoConnection,
@Inject(USER_REPOSITORY) private readonly userRepository: UserRepository,
@Inject(COLLABORATOR_REPOSITORY) private readonly collaboratorRepository: CollaboratorRepository,
) {}

async find(userId: UserId): Promise<TermView[]> {
const user = await this.getUser(userId);
const collaborator = await this.getCollaborator(userId);

const result = await this.mongo.db
.collection('terms')
.find({
$or: [{ hashtags: user.getInterests() }],
$or: [{ hashtags: collaborator.getInterests() }],
})
.project({ _id: 0 })
.sort(['createdAt', -1])
Expand All @@ -43,12 +46,12 @@ export default class MongoFindSuggestionsTermReadModel implements FindSuggestion
});
}

async getUser(userId: UserId): Promise<User> {
const user = await this.userRepository.findById(userId);
if (null === user) {
throw new UserDoesNotExistsException(userId.toString());
async getCollaborator(collaboratorId: CollaboratorId): Promise<Collaborator> {
const collaborator = await this.collaboratorRepository.findById(collaboratorId);
if (null === collaborator) {
throw new CollaboratorDoesNotExistsException(collaboratorId.toString());
}

return user;
return collaborator;
}
}
19 changes: 17 additions & 2 deletions src/languages/language.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,26 @@ import { readModels } from '@src/languages/_dependencyInjection/readModels';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { entitySchemas } from '@src/languages/_dependencyInjection/entitySchemas';
import mikroOrmConfiguration from './infrastructure/persistence/mikroOrm/config';
import { HttpModule } from '@nestjs/axios';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { services } from '@src/languages/_dependencyInjection/services';

@Module({
imports: [MikroOrmModule.forRoot(mikroOrmConfiguration), MikroOrmModule.forFeature(entitySchemas)],
imports: [
MikroOrmModule.forRoot(mikroOrmConfiguration),
MikroOrmModule.forFeature(entitySchemas),
ConfigModule.forRoot(),
HttpModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
baseURL: configService.get('SERVER_URL'),
timeout: 5000,
}),
}),
],
exports: [],
controllers: [...controllers],
providers: [...commands, ...queries, ...events, ...projections, ...repositories, ...readModels],
providers: [...commands, ...queries, ...events, ...projections, ...services, ...repositories, ...readModels],
})
export class LanguageModule {}
Loading

0 comments on commit 63ab151

Please sign in to comment.