From 27114c79b0ed44a7774463957073759607ca68d3 Mon Sep 17 00:00:00 2001 From: sbriat Date: Fri, 30 Jun 2023 16:36:17 +0200 Subject: [PATCH] move libs to dedicated package --- package-lock.json | 16 + package.json | 1 + src/libs/api/api-error.response.ts | 19 -- src/libs/api/id.response.dto.ts | 7 - src/libs/api/paginated.response.base.ts | 8 - src/libs/api/response.base.ts | 23 -- src/libs/db/prisma-repository.base.ts | 66 ---- src/libs/ddd/aggregate-root.base.ts | 35 --- src/libs/ddd/command.base.ts | 52 ---- src/libs/ddd/domain-event.base.ts | 52 ---- src/libs/ddd/entity.base.ts | 150 --------- src/libs/ddd/index.ts | 7 - src/libs/ddd/mapper.interface.ts | 12 - src/libs/ddd/query.base.ts | 31 -- src/libs/ddd/repository.port.ts | 40 --- src/libs/ddd/value-object.base.ts | 71 ----- src/libs/exceptions/exception.base.ts | 63 ---- src/libs/exceptions/exception.codes.ts | 16 - src/libs/exceptions/exceptions.ts | 99 ------ src/libs/exceptions/index.ts | 4 - .../exceptions/rpc-exception.codes.enum.ts | 19 -- src/libs/guard.ts | 55 ---- src/libs/ports/logger.port.ts | 6 - src/libs/ports/message-publisher.port.ts | 3 - src/libs/ports/prisma-repository.port.ts | 11 - .../unit/db/prisma-repository.base.spec.ts | 287 ------------------ .../unit/ddd/aggregate-root.base.spec.ts | 76 ----- src/libs/tests/unit/ddd/command.base.spec.ts | 47 --- .../tests/unit/ddd/domain-event.base.spec.ts | 42 --- src/libs/tests/unit/ddd/entity.base.spec.ts | 209 ------------- src/libs/tests/unit/ddd/query.base.spec.ts | 40 --- .../tests/unit/ddd/value-object.base.spec.ts | 42 --- src/libs/tests/unit/guard.spec.ts | 65 ---- .../utils/rpc-validation-pipe.usecase.spec.ts | 20 -- src/libs/types/index.ts | 6 - src/libs/types/object-literal.type.ts | 6 - .../utils/convert-props-to-object.util.ts | 47 --- src/libs/utils/index.ts | 1 - src/libs/utils/pipes/rpc.validation-pipe.ts | 15 - src/modules/ad/ad.mapper.ts | 2 +- src/modules/ad/ad.module.ts | 2 +- .../commands/create-ad/create-ad.command.ts | 2 +- .../commands/create-ad/create-ad.service.ts | 3 +- ...when-ad-is-created.domain-event-handler.ts | 2 +- ...when-ad-is-created.domain-event-handler.ts | 2 +- .../application/ports/ad.repository.port.ts | 2 +- .../find-ad-by-id/find-ad-by-id.query.ts | 2 +- src/modules/ad/core/domain/ad.entity.ts | 2 +- src/modules/ad/core/domain/ad.errors.ts | 2 +- .../domain/events/ad-created.domain-events.ts | 2 +- .../value-objects/address.value-object.ts | 2 +- .../value-objects/coordinates.value-object.ts | 2 +- .../margin-durations.value-object.ts | 2 +- .../value-objects/schedule.value-object.ts | 2 +- .../value-objects/waypoint.value-object.ts | 2 +- .../ad/infrastructure/ad.repository.ts | 4 +- .../ad/infrastructure/message-publisher.ts | 2 +- .../ad/infrastructure}/prisma.service.ts | 0 .../dtos/ad.paginated.response.dto.ts | 2 +- .../ad/interface/dtos/ad.response.dto.ts | 2 +- .../create-ad.grpc.controller.ts | 8 +- .../find-ad-by-id.grpc.controller.ts | 6 +- .../tests/integration/ad.repository.spec.ts | 2 +- .../tests/unit/core/create-ad.service.spec.ts | 4 +- .../unit/infrastructure/ad.repository.spec.ts | 2 +- .../create-ad.grpc.controller.spec.ts | 4 +- .../find-ad-by-id.grpc.controller.spec.ts | 4 +- .../repositories.health-indicator.usecase.ts | 2 +- .../infrastructure/message-publisher.ts | 2 +- ...ositories.health-indicator.usecase.spec.ts | 2 +- 70 files changed, 56 insertions(+), 1792 deletions(-) delete mode 100644 src/libs/api/api-error.response.ts delete mode 100644 src/libs/api/id.response.dto.ts delete mode 100644 src/libs/api/paginated.response.base.ts delete mode 100644 src/libs/api/response.base.ts delete mode 100644 src/libs/db/prisma-repository.base.ts delete mode 100644 src/libs/ddd/aggregate-root.base.ts delete mode 100644 src/libs/ddd/command.base.ts delete mode 100644 src/libs/ddd/domain-event.base.ts delete mode 100644 src/libs/ddd/entity.base.ts delete mode 100644 src/libs/ddd/index.ts delete mode 100644 src/libs/ddd/mapper.interface.ts delete mode 100644 src/libs/ddd/query.base.ts delete mode 100644 src/libs/ddd/repository.port.ts delete mode 100644 src/libs/ddd/value-object.base.ts delete mode 100644 src/libs/exceptions/exception.base.ts delete mode 100644 src/libs/exceptions/exception.codes.ts delete mode 100644 src/libs/exceptions/exceptions.ts delete mode 100644 src/libs/exceptions/index.ts delete mode 100644 src/libs/exceptions/rpc-exception.codes.enum.ts delete mode 100644 src/libs/guard.ts delete mode 100644 src/libs/ports/logger.port.ts delete mode 100644 src/libs/ports/message-publisher.port.ts delete mode 100644 src/libs/ports/prisma-repository.port.ts delete mode 100644 src/libs/tests/unit/db/prisma-repository.base.spec.ts delete mode 100644 src/libs/tests/unit/ddd/aggregate-root.base.spec.ts delete mode 100644 src/libs/tests/unit/ddd/command.base.spec.ts delete mode 100644 src/libs/tests/unit/ddd/domain-event.base.spec.ts delete mode 100644 src/libs/tests/unit/ddd/entity.base.spec.ts delete mode 100644 src/libs/tests/unit/ddd/query.base.spec.ts delete mode 100644 src/libs/tests/unit/ddd/value-object.base.spec.ts delete mode 100644 src/libs/tests/unit/guard.spec.ts delete mode 100644 src/libs/tests/unit/utils/rpc-validation-pipe.usecase.spec.ts delete mode 100644 src/libs/types/index.ts delete mode 100644 src/libs/types/object-literal.type.ts delete mode 100644 src/libs/utils/convert-props-to-object.util.ts delete mode 100644 src/libs/utils/index.ts delete mode 100644 src/libs/utils/pipes/rpc.validation-pipe.ts rename src/{libs/db => modules/ad/infrastructure}/prisma.service.ts (100%) diff --git a/package-lock.json b/package-lock.json index 645bdc0..ae2781e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@grpc/proto-loader": "^0.7.6", "@liaoliaots/nestjs-redis": "^9.0.5", "@mobicoop/configuration-module": "^1.2.0", + "@mobicoop/ddd-library": "^0.0.1", "@mobicoop/message-broker-module": "^1.2.0", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.3.1", @@ -1415,6 +1416,21 @@ "@nestjs/common": "^9.4.2" } }, + "node_modules/@mobicoop/ddd-library": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mobicoop/ddd-library/-/ddd-library-0.0.1.tgz", + "integrity": "sha512-T6g3pgodrMOZ1yrtNWylgu6EjRz3HPgcD9UoK0cJCvfiq9WjTH9TOZ6wKh9vIijiO+a5KJkZiKHbbjzuJvdwCg==", + "dependencies": { + "@nestjs/event-emitter": "^1.4.2", + "@nestjs/microservices": "^9.4.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "@nestjs/common": "^9.4.2" + } + }, "node_modules/@mobicoop/message-broker-module": { "version": "1.2.0", "license": "AGPL", diff --git a/package.json b/package.json index c8bc9a0..dcc7dde 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@grpc/proto-loader": "^0.7.6", "@liaoliaots/nestjs-redis": "^9.0.5", "@mobicoop/configuration-module": "^1.2.0", + "@mobicoop/ddd-library": "^0.0.1", "@mobicoop/message-broker-module": "^1.2.0", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.3.1", diff --git a/src/libs/api/api-error.response.ts b/src/libs/api/api-error.response.ts deleted file mode 100644 index db47f4c..0000000 --- a/src/libs/api/api-error.response.ts +++ /dev/null @@ -1,19 +0,0 @@ -export class ApiErrorResponse { - readonly statusCode: number; - - readonly message: string; - - readonly error: string; - - readonly correlationId: string; - - readonly subErrors?: string[]; - - constructor(body: ApiErrorResponse) { - this.statusCode = body.statusCode; - this.message = body.message; - this.error = body.error; - this.correlationId = body.correlationId; - this.subErrors = body.subErrors; - } -} diff --git a/src/libs/api/id.response.dto.ts b/src/libs/api/id.response.dto.ts deleted file mode 100644 index aa995e7..0000000 --- a/src/libs/api/id.response.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class IdResponse { - constructor(id: string) { - this.id = id; - } - - readonly id: string; -} diff --git a/src/libs/api/paginated.response.base.ts b/src/libs/api/paginated.response.base.ts deleted file mode 100644 index f11d849..0000000 --- a/src/libs/api/paginated.response.base.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Paginated } from '../ddd'; - -export abstract class PaginatedResponseDto extends Paginated { - readonly total: number; - readonly perPage: number; - readonly page: number; - abstract readonly data: readonly T[]; -} diff --git a/src/libs/api/response.base.ts b/src/libs/api/response.base.ts deleted file mode 100644 index 58dc0e6..0000000 --- a/src/libs/api/response.base.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { IdResponse } from './id.response.dto'; - -export interface BaseResponseProps { - id: string; - createdAt: Date; - updatedAt: Date; -} - -/** - * Most of our response objects will have properties like - * id, createdAt and updatedAt so we can move them to a - * separate class and extend it to avoid duplication. - */ -export class ResponseBase extends IdResponse { - constructor(props: BaseResponseProps) { - super(props.id); - this.createdAt = new Date(props.createdAt).toISOString(); - this.updatedAt = new Date(props.updatedAt).toISOString(); - } - - readonly createdAt: string; - readonly updatedAt: string; -} diff --git a/src/libs/db/prisma-repository.base.ts b/src/libs/db/prisma-repository.base.ts deleted file mode 100644 index cfb1ed7..0000000 --- a/src/libs/db/prisma-repository.base.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { EventEmitter2 } from '@nestjs/event-emitter'; -import { AggregateRoot, Mapper, RepositoryPort } from '../ddd'; -import { ObjectLiteral } from '../types'; -import { LoggerPort } from '../ports/logger.port'; -import { - PrismaRawRepositoryPort, - PrismaRepositoryPort, -} from '../ports/prisma-repository.port'; -import { Prisma } from '@prisma/client'; -import { - ConflictException, - DatabaseErrorException, - NotFoundException, -} from '@libs/exceptions'; - -export abstract class PrismaRepositoryBase< - Aggregate extends AggregateRoot, - DbReadModel extends ObjectLiteral, - DbWriteModel extends ObjectLiteral, -> implements RepositoryPort -{ - protected constructor( - protected readonly prisma: PrismaRepositoryPort | any, - protected readonly prismaRaw: PrismaRawRepositoryPort, - protected readonly mapper: Mapper, - protected readonly eventEmitter: EventEmitter2, - protected readonly logger: LoggerPort, - ) {} - - async findOneById(id: string, include?: any): Promise { - const entity = await this.prisma.findUnique({ - where: { uuid: id }, - include, - }); - if (entity) return this.mapper.toDomain(entity); - throw new NotFoundException('Record not found'); - } - - async insert(entity: Aggregate): Promise { - try { - await this.prisma.create({ - data: this.mapper.toPersistence(entity), - }); - entity.publishEvents(this.logger, this.eventEmitter); - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - if (e.message.includes('Already exists')) { - throw new ConflictException('Record already exists', e); - } - } - throw e; - } - } - - async healthCheck(): Promise { - try { - await this.prismaRaw.$queryRaw`SELECT 1`; - return true; - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseErrorException(e.message); - } - throw new DatabaseErrorException(); - } - } -} diff --git a/src/libs/ddd/aggregate-root.base.ts b/src/libs/ddd/aggregate-root.base.ts deleted file mode 100644 index a887396..0000000 --- a/src/libs/ddd/aggregate-root.base.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Entity } from './entity.base'; -import { EventEmitter2 } from '@nestjs/event-emitter'; -import { LoggerPort } from '@libs/ports/logger.port'; -import { DomainEvent } from './domain-event.base'; - -export abstract class AggregateRoot extends Entity { - private _domainEvents: DomainEvent[] = []; - - get domainEvents(): DomainEvent[] { - return this._domainEvents; - } - - protected addEvent(domainEvent: DomainEvent): void { - this._domainEvents.push(domainEvent); - } - - public clearEvents(): void { - this._domainEvents = []; - } - - public async publishEvents( - logger: LoggerPort, - eventEmitter: EventEmitter2, - ): Promise { - await Promise.all( - this.domainEvents.map(async (event) => { - logger.debug( - `"${event.constructor.name}" event published for aggregate ${this.constructor.name} : ${this.id}`, - ); - return eventEmitter.emitAsync(event.constructor.name, event); - }), - ); - this.clearEvents(); - } -} diff --git a/src/libs/ddd/command.base.ts b/src/libs/ddd/command.base.ts deleted file mode 100644 index 046db48..0000000 --- a/src/libs/ddd/command.base.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { v4 } from 'uuid'; -import { ArgumentNotProvidedException } from '../exceptions'; -import { Guard } from '../guard'; - -export type CommandProps = Omit & Partial; - -type CommandMetadata = { - /** ID for correlation purposes (for commands that - * arrive from other microservices,logs correlation, etc). */ - readonly correlationId: string; - - /** - * Causation id to reconstruct execution order if needed - */ - readonly causationId?: string; - - /** - * ID of a user who invoker the command. Can be useful for - * logging and tracking execution of commands and events - */ - readonly userId?: string; - - /** - * Time when the command occurred. Mostly for tracing purposes - */ - readonly timestamp: number; -}; - -export class Command { - /** - * Command id, in case if we want to save it - * for auditing purposes and create a correlation/causation chain - */ - readonly id: string; - - readonly metadata: CommandMetadata; - - constructor(props: CommandProps) { - if (Guard.isEmpty(props)) { - throw new ArgumentNotProvidedException( - 'Command props should not be empty', - ); - } - this.id = props.id || v4(); - this.metadata = { - correlationId: props?.metadata?.correlationId, - causationId: props?.metadata?.causationId, - timestamp: props?.metadata?.timestamp || Date.now(), - userId: props?.metadata?.userId, - }; - } -} diff --git a/src/libs/ddd/domain-event.base.ts b/src/libs/ddd/domain-event.base.ts deleted file mode 100644 index c905dca..0000000 --- a/src/libs/ddd/domain-event.base.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ArgumentNotProvidedException } from '../exceptions'; -import { Guard } from '../guard'; -import { v4 } from 'uuid'; - -type DomainEventMetadata = { - /** Timestamp when this domain event occurred */ - readonly timestamp: number; - - /** ID for correlation purposes (for Integration Events,logs correlation, etc). - */ - readonly correlationId: string; - - /** - * Causation id used to reconstruct execution order if needed - */ - readonly causationId?: string; - - /** - * User ID for debugging and logging purposes - */ - readonly userId?: string; -}; - -export type DomainEventProps = Omit & { - aggregateId: string; - metadata?: DomainEventMetadata; -}; - -export abstract class DomainEvent { - public readonly id: string; - - /** Aggregate ID where domain event occurred */ - public readonly aggregateId: string; - - public readonly metadata: DomainEventMetadata; - - constructor(props: DomainEventProps) { - if (Guard.isEmpty(props)) { - throw new ArgumentNotProvidedException( - 'DomainEvent props should not be empty', - ); - } - this.id = v4(); - this.aggregateId = props.aggregateId; - this.metadata = { - correlationId: props?.metadata?.correlationId, - causationId: props?.metadata?.causationId, - timestamp: props?.metadata?.timestamp || Date.now(), - userId: props?.metadata?.userId, - }; - } -} diff --git a/src/libs/ddd/entity.base.ts b/src/libs/ddd/entity.base.ts deleted file mode 100644 index b946f43..0000000 --- a/src/libs/ddd/entity.base.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { - ArgumentNotProvidedException, - ArgumentInvalidException, - ArgumentOutOfRangeException, -} from '../exceptions'; -import { Guard } from '../guard'; -import { convertPropsToObject } from '../utils'; - -export type AggregateID = string; - -export interface BaseEntityProps { - id: AggregateID; - createdAt: Date; - updatedAt: Date; -} - -export interface CreateEntityProps { - id: AggregateID; - props: T; - createdAt?: Date; - updatedAt?: Date; -} - -export abstract class Entity { - constructor({ - id, - createdAt, - updatedAt, - props, - }: CreateEntityProps) { - this.setId(id); - this.validateProps(props); - const now = new Date(); - this._createdAt = createdAt || now; - this._updatedAt = updatedAt || now; - this.props = props; - this.validate(); - } - - protected readonly props: EntityProps; - - /** - * ID is set in the concrete entity implementation to support - * different ID types depending on your needs. - * For example it could be a UUID for aggregate root, - * and shortid / nanoid for child entities. - */ - protected abstract _id: AggregateID; - - private readonly _createdAt: Date; - - private _updatedAt: Date; - - get id(): AggregateID { - return this._id; - } - - private setId(id: AggregateID): void { - this._id = id; - } - - get createdAt(): Date { - return this._createdAt; - } - - get updatedAt(): Date { - return this._updatedAt; - } - - static isEntity(entity: unknown): entity is Entity { - return entity instanceof Entity; - } - - /** - * Checks if two entities are the same Entity by comparing ID field. - * @param object Entity - */ - public equals(object?: Entity): boolean { - if (object === null || object === undefined) { - return false; - } - - if (this === object) { - return true; - } - - if (!Entity.isEntity(object)) { - return false; - } - - return this.id ? this.id === object.id : false; - } - - /** - * Returns entity properties. - * @return {*} {Props & EntityProps} - * @memberof Entity - */ - public getProps(): EntityProps & BaseEntityProps { - const propsCopy = { - id: this._id, - createdAt: this._createdAt, - updatedAt: this._updatedAt, - ...this.props, - }; - return Object.freeze(propsCopy); - } - - /** - * Convert an Entity and all sub-entities/Value Objects it - * contains to a plain object with primitive types. Can be - * useful when logging an entity during testing/debugging - */ - public toObject(): unknown { - const plainProps = convertPropsToObject(this.props); - - const result = { - id: this._id, - createdAt: this._createdAt, - updatedAt: this._updatedAt, - ...plainProps, - }; - return Object.freeze(result); - } - - /** - * There are certain rules that always have to be true (invariants) - * for each entity. Validate method is called every time before - * saving an entity to the database to make sure those rules are respected. - */ - public abstract validate(): void; - - private validateProps(props: EntityProps): void { - const MAX_PROPS = 50; - - if (Guard.isEmpty(props)) { - throw new ArgumentNotProvidedException( - 'Entity props should not be empty', - ); - } - if (typeof props !== 'object') { - throw new ArgumentInvalidException('Entity props should be an object'); - } - if (Object.keys(props as any).length > MAX_PROPS) { - throw new ArgumentOutOfRangeException( - `Entity props should not have more than ${MAX_PROPS} properties`, - ); - } - } -} diff --git a/src/libs/ddd/index.ts b/src/libs/ddd/index.ts deleted file mode 100644 index e0e4a3a..0000000 --- a/src/libs/ddd/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from './aggregate-root.base'; -export * from './command.base'; -export * from './domain-event.base'; -export * from './entity.base'; -export * from './mapper.interface'; -export * from './repository.port'; -export * from './value-object.base'; diff --git a/src/libs/ddd/mapper.interface.ts b/src/libs/ddd/mapper.interface.ts deleted file mode 100644 index 8e9e3a4..0000000 --- a/src/libs/ddd/mapper.interface.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Entity } from './entity.base'; - -export interface Mapper< - DomainEntity extends Entity, - DbReadRecord, - DbWriteRecord, - Response = any, -> { - toPersistence(entity: DomainEntity): DbWriteRecord; - toDomain(record: DbReadRecord): DomainEntity; - toResponse(entity: DomainEntity): Response; -} diff --git a/src/libs/ddd/query.base.ts b/src/libs/ddd/query.base.ts deleted file mode 100644 index 7aba63c..0000000 --- a/src/libs/ddd/query.base.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { OrderBy, PaginatedQueryParams } from './repository.port'; - -/** - * Base class for regular queries - */ -export abstract class QueryBase {} - -/** - * Base class for paginated queries - */ -export abstract class PaginatedQueryBase extends QueryBase { - perPage: number; - offset: number; - orderBy: OrderBy; - page: number; - - constructor(props: PaginatedParams) { - super(); - this.perPage = props.perPage || 10; - this.offset = props.page ? props.page * this.perPage : 0; - this.page = props.page || 0; - this.orderBy = props.orderBy || { field: true, param: 'desc' }; - } -} - -// Paginated query parameters -export type PaginatedParams = Omit< - T, - 'perPage' | 'offset' | 'orderBy' | 'page' -> & - Partial>; diff --git a/src/libs/ddd/repository.port.ts b/src/libs/ddd/repository.port.ts deleted file mode 100644 index 66d8450..0000000 --- a/src/libs/ddd/repository.port.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* Most of repositories will probably need generic - save/find/delete operations, so it's easier - to have some shared interfaces. - More specific queries should be defined - in a respective repository. -*/ - -export class Paginated { - readonly total: number; - readonly perPage: number; - readonly page: number; - readonly data: readonly T[]; - - constructor(props: Paginated) { - this.total = props.total; - this.perPage = props.perPage; - this.page = props.page; - this.data = props.data; - } -} - -export type OrderBy = { field: string | true; param: 'asc' | 'desc' }; - -export type PaginatedQueryParams = { - perPage: number; - page: number; - offset: number; - orderBy: OrderBy; -}; - -export interface RepositoryPort { - insert(entity: Entity | Entity[]): Promise; - findOneById(id: string, include?: any): Promise; - healthCheck(): Promise; - // findAll(): Promise; - // findAllPaginated(params: PaginatedQueryParams): Promise>; - // delete(entity: Entity): Promise; - - // transaction(handler: () => Promise): Promise; -} diff --git a/src/libs/ddd/value-object.base.ts b/src/libs/ddd/value-object.base.ts deleted file mode 100644 index 348d7a9..0000000 --- a/src/libs/ddd/value-object.base.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { ArgumentNotProvidedException } from '../exceptions'; -import { Guard } from '../guard'; -import { convertPropsToObject } from '../utils'; - -/** - * Domain Primitive is an object that contains only a single value - */ -export type Primitives = string | number | boolean; -export interface DomainPrimitive { - value: T; -} - -type ValueObjectProps = T extends Primitives | Date ? DomainPrimitive : T; - -export abstract class ValueObject { - protected readonly props: ValueObjectProps; - - constructor(props: ValueObjectProps) { - this.checkIfEmpty(props); - this.validate(props); - this.props = props; - } - - protected abstract validate(props: ValueObjectProps): void; - - static isValueObject(obj: unknown): obj is ValueObject { - return obj instanceof ValueObject; - } - - /** - * Check if two Value Objects are equal. Checks structural equality. - * @param vo ValueObject - */ - public equals(vo?: ValueObject): boolean { - if (vo === null || vo === undefined) { - return false; - } - return JSON.stringify(this) === JSON.stringify(vo); - } - - /** - * Unpack a value object to get its raw properties - */ - public unpack(): T { - if (this.isDomainPrimitive(this.props)) { - return this.props.value; - } - - const propsCopy = convertPropsToObject(this.props); - - return Object.freeze(propsCopy); - } - - private checkIfEmpty(props: ValueObjectProps): void { - if ( - Guard.isEmpty(props) || - (this.isDomainPrimitive(props) && Guard.isEmpty(props.value)) - ) { - throw new ArgumentNotProvidedException('Property cannot be empty'); - } - } - - private isDomainPrimitive( - obj: unknown, - ): obj is DomainPrimitive { - if (Object.prototype.hasOwnProperty.call(obj, 'value')) { - return true; - } - return false; - } -} diff --git a/src/libs/exceptions/exception.base.ts b/src/libs/exceptions/exception.base.ts deleted file mode 100644 index 4c9ebbc..0000000 --- a/src/libs/exceptions/exception.base.ts +++ /dev/null @@ -1,63 +0,0 @@ -export interface SerializedException { - message: string; - code: string; - correlationId: string; - stack?: string; - cause?: string; - metadata?: unknown; - /** - * ^ Consider adding optional `metadata` object to - * exceptions (if language doesn't support anything - * similar by default) and pass some useful technical - * information about the exception when throwing. - * This will make debugging easier. - */ -} - -/** - * Base class for custom exceptions. - * - * @abstract - * @class ExceptionBase - * @extends {Error} - */ -export abstract class ExceptionBase extends Error { - abstract code: string; - - public readonly correlationId: string; - - /** - * @param {string} message - * @param {ObjectLiteral} [metadata={}] - * **BE CAREFUL** not to include sensitive info in 'metadata' - * to prevent leaks since all exception's data will end up - * in application's log files. Only include non-sensitive - * info that may help with debugging. - */ - constructor( - readonly message: string, - readonly cause?: Error, - readonly metadata?: unknown, - ) { - super(message); - Error.captureStackTrace(this, this.constructor); - } - - /** - * By default in NodeJS Error objects are not - * serialized properly when sending plain objects - * to external processes. This method is a workaround. - * Keep in mind not to return a stack trace to user when in production. - * https://iaincollins.medium.com/error-handling-in-javascript-a6172ccdf9af - */ - toJSON(): SerializedException { - return { - message: this.message, - code: this.code, - stack: this.stack, - correlationId: this.correlationId, - cause: JSON.stringify(this.cause), - metadata: this.metadata, - }; - } -} diff --git a/src/libs/exceptions/exception.codes.ts b/src/libs/exceptions/exception.codes.ts deleted file mode 100644 index 5ca541b..0000000 --- a/src/libs/exceptions/exception.codes.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Adding a `code` string with a custom status code for every - * exception is a good practice, since when that exception - * is transferred to another process `instanceof` check - * cannot be performed anymore so a `code` string is used instead. - * code constants can be stored in a separate file so they - * can be shared and reused on a receiving side (code sharing is - * useful when developing fullstack apps or microservices) - */ -export const ARGUMENT_INVALID = 'GENERIC.ARGUMENT_INVALID'; -export const ARGUMENT_OUT_OF_RANGE = 'GENERIC.ARGUMENT_OUT_OF_RANGE'; -export const ARGUMENT_NOT_PROVIDED = 'GENERIC.ARGUMENT_NOT_PROVIDED'; -export const NOT_FOUND = 'GENERIC.NOT_FOUND'; -export const CONFLICT = 'GENERIC.CONFLICT'; -export const INTERNAL_SERVER_ERROR = 'GENERIC.INTERNAL_SERVER_ERROR'; -export const DATABASE_ERROR = 'GENERIC.DATABASE_ERROR'; diff --git a/src/libs/exceptions/exceptions.ts b/src/libs/exceptions/exceptions.ts deleted file mode 100644 index ceda019..0000000 --- a/src/libs/exceptions/exceptions.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { - ARGUMENT_INVALID, - ARGUMENT_NOT_PROVIDED, - ARGUMENT_OUT_OF_RANGE, - CONFLICT, - DATABASE_ERROR, - INTERNAL_SERVER_ERROR, - NOT_FOUND, -} from '.'; -import { ExceptionBase } from './exception.base'; - -/** - * Used to indicate that an incorrect argument was provided to a method/function/class constructor - * - * @class ArgumentInvalidException - * @extends {ExceptionBase} - */ -export class ArgumentInvalidException extends ExceptionBase { - readonly code = ARGUMENT_INVALID; -} - -/** - * Used to indicate that an argument was not provided (is empty object/array, null of undefined). - * - * @class ArgumentNotProvidedException - * @extends {ExceptionBase} - */ -export class ArgumentNotProvidedException extends ExceptionBase { - readonly code = ARGUMENT_NOT_PROVIDED; -} - -/** - * Used to indicate that an argument is out of allowed range - * (for example: incorrect string/array length, number not in allowed min/max range etc) - * - * @class ArgumentOutOfRangeException - * @extends {ExceptionBase} - */ -export class ArgumentOutOfRangeException extends ExceptionBase { - readonly code = ARGUMENT_OUT_OF_RANGE; -} - -/** - * Used to indicate conflicting entities (usually in the database) - * - * @class ConflictException - * @extends {ExceptionBase} - */ -export class ConflictException extends ExceptionBase { - readonly code = CONFLICT; -} - -/** - * Used to indicate that entity is not found - * - * @class NotFoundException - * @extends {ExceptionBase} - */ -export class NotFoundException extends ExceptionBase { - static readonly message = 'Not found'; - - constructor(message = NotFoundException.message) { - super(message); - } - - readonly code = NOT_FOUND; -} - -/** - * Used to indicate an internal server error that does not fall under all other errors - * - * @class InternalServerErrorException - * @extends {ExceptionBase} - */ -export class InternalServerErrorException extends ExceptionBase { - static readonly message = 'Internal server error'; - - constructor(message = InternalServerErrorException.message) { - super(message); - } - - readonly code = INTERNAL_SERVER_ERROR; -} - -/** - * Used to indicate a database error - * - * @class DatabaseErrorException - * @extends {ExceptionBase} - */ -export class DatabaseErrorException extends ExceptionBase { - static readonly message = 'Database error'; - - constructor(message = DatabaseErrorException.message) { - super(message); - } - - readonly code = DATABASE_ERROR; -} diff --git a/src/libs/exceptions/index.ts b/src/libs/exceptions/index.ts deleted file mode 100644 index 4445442..0000000 --- a/src/libs/exceptions/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './exception.base'; -export * from './exception.codes'; -export * from './exceptions'; -export * from './rpc-exception.codes.enum'; diff --git a/src/libs/exceptions/rpc-exception.codes.enum.ts b/src/libs/exceptions/rpc-exception.codes.enum.ts deleted file mode 100644 index 034391c..0000000 --- a/src/libs/exceptions/rpc-exception.codes.enum.ts +++ /dev/null @@ -1,19 +0,0 @@ -export enum RpcExceptionCode { - OK = 0, - CANCELLED = 1, - UNKNOWN = 2, - INVALID_ARGUMENT = 3, - DEADLINE_EXCEEDED = 4, - NOT_FOUND = 5, - ALREADY_EXISTS = 6, - PERMISSION_DENIED = 7, - RESOURCE_EXHAUSTED = 8, - FAILED_PRECONDITION = 9, - ABORTED = 10, - OUT_OF_RANGE = 11, - UNIMPLEMENTED = 12, - INTERNAL = 13, - UNAVAILABLE = 14, - DATA_LOSS = 15, - UNAUTHENTICATED = 16, -} diff --git a/src/libs/guard.ts b/src/libs/guard.ts deleted file mode 100644 index 31b7a2b..0000000 --- a/src/libs/guard.ts +++ /dev/null @@ -1,55 +0,0 @@ -export class Guard { - /** - * Checks if value is empty. Accepts strings, numbers, booleans, objects and arrays. - */ - static isEmpty(value: unknown): boolean { - if (typeof value === 'number' || typeof value === 'boolean') { - return false; - } - if (typeof value === 'undefined' || value === null) { - return true; - } - if (value instanceof Date) { - return false; - } - if (value instanceof Object && !Object.keys(value).length) { - return true; - } - if (Array.isArray(value)) { - if (value.length === 0) { - return true; - } - if (value.every((item) => Guard.isEmpty(item))) { - return true; - } - } - if (value === '') { - return true; - } - - return false; - } - - /** - * Checks length range of a provided number/string/array - */ - static lengthIsBetween( - value: number | string | Array, - min: number, - max: number, - ): boolean { - if (Guard.isEmpty(value)) { - throw new Error( - 'Cannot check length of a value. Provided value is empty', - ); - } - const valueLength = - typeof value === 'number' - ? Number(value).toString().length - : value.length; - if (valueLength >= min && valueLength <= max) { - return true; - } - return false; - } -} diff --git a/src/libs/ports/logger.port.ts b/src/libs/ports/logger.port.ts deleted file mode 100644 index 43bf996..0000000 --- a/src/libs/ports/logger.port.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface LoggerPort { - log(message: string, ...meta: unknown[]): void; - error(message: string, trace?: unknown, ...meta: unknown[]): void; - warn(message: string, ...meta: unknown[]): void; - debug(message: string, ...meta: unknown[]): void; -} diff --git a/src/libs/ports/message-publisher.port.ts b/src/libs/ports/message-publisher.port.ts deleted file mode 100644 index fd35c90..0000000 --- a/src/libs/ports/message-publisher.port.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface MessagePublisherPort { - publish(routingKey: string, message: string): void; -} diff --git a/src/libs/ports/prisma-repository.port.ts b/src/libs/ports/prisma-repository.port.ts deleted file mode 100644 index 79b86fe..0000000 --- a/src/libs/ports/prisma-repository.port.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface PrismaRepositoryPort { - findUnique(options: any): Promise; - create(entity: any): Promise; -} - -export interface PrismaRawRepositoryPort { - $queryRaw( - query: TemplateStringsArray, - ...values: any[] - ): Promise; -} diff --git a/src/libs/tests/unit/db/prisma-repository.base.spec.ts b/src/libs/tests/unit/db/prisma-repository.base.spec.ts deleted file mode 100644 index e3cc272..0000000 --- a/src/libs/tests/unit/db/prisma-repository.base.spec.ts +++ /dev/null @@ -1,287 +0,0 @@ -import { ResponseBase } from '@libs/api/response.base'; -import { PrismaRepositoryBase } from '@libs/db/prisma-repository.base'; -import { PrismaService } from '@libs/db/prisma.service'; -import { AggregateID, AggregateRoot, Mapper, RepositoryPort } from '@libs/ddd'; -import { - ConflictException, - DatabaseErrorException, - NotFoundException, -} from '@libs/exceptions'; -import { Injectable, Logger } from '@nestjs/common'; -import { EventEmitter2 } from '@nestjs/event-emitter'; -import { Test, TestingModule } from '@nestjs/testing'; -import { Prisma } from '@prisma/client'; -import { v4 } from 'uuid'; - -interface FakeProps { - name: string; -} - -interface CreateFakeProps { - name: string; -} - -class FakeEntity extends AggregateRoot { - protected readonly _id: AggregateID; - - static create = (create: CreateFakeProps): FakeEntity => { - const id = v4(); - const props: FakeProps = { ...create }; - const fake = new FakeEntity({ id, props }); - return fake; - }; - - validate(): void { - // not implemented - } -} - -type FakeModel = { - uuid: string; - name: string; - createdAt: Date; - updatedAt: Date; -}; - -type FakeRepositoryPort = RepositoryPort; - -class FakeResponseDto extends ResponseBase { - name: string; -} - -const fakeRecord: FakeModel = { - uuid: 'd567ea3b-4981-43c9-9449-a409b5fa9fed', - name: 'fakeName', - createdAt: new Date('2023-06-28T16:02:00Z'), - updatedAt: new Date('2023-06-28T16:02:00Z'), -}; - -let recordId = 2; -const recordUuid = 'uuid-'; -const recordName = 'fakeName-'; - -const createRandomRecord = (): FakeModel => { - const fakeRecord: FakeModel = { - uuid: `${recordUuid}${recordId}`, - name: `${recordName}${recordId}`, - createdAt: new Date('2023-06-30T08:00:00Z'), - updatedAt: new Date('2023-06-30T08:00:00Z'), - }; - - recordId++; - - return fakeRecord; -}; - -const fakeRecords: FakeModel[] = []; -Array.from({ length: 10 }).forEach(() => { - fakeRecords.push(createRandomRecord()); -}); - -@Injectable() -class FakeMapper - implements Mapper -{ - toPersistence = (entity: FakeEntity): FakeModel => { - const copy = entity.getProps(); - const record: FakeModel = { - uuid: copy.id, - name: copy.name, - createdAt: copy.createdAt, - updatedAt: copy.updatedAt, - }; - return record; - }; - - toDomain = (record: FakeModel): FakeEntity => { - const entity = new FakeEntity({ - id: record.uuid, - createdAt: new Date(record.createdAt), - updatedAt: new Date(record.updatedAt), - props: { - name: record.name, - }, - }); - return entity; - }; - - toResponse = (entity: FakeEntity): FakeResponseDto => { - const props = entity.getProps(); - const response = new FakeResponseDto(entity); - response.name = props.name; - return response; - }; -} - -@Injectable() -class FakePrismaService extends PrismaService { - fake: any; -} - -const mockPrismaService = { - $queryRaw: jest - .fn() - .mockImplementationOnce(() => { - return true; - }) - .mockImplementation(() => { - throw new Prisma.PrismaClientKnownRequestError('Database unavailable', { - code: 'code', - clientVersion: 'version', - }); - }) - .mockImplementationOnce(() => { - throw new Error(); - }), - fake: { - create: jest - .fn() - .mockResolvedValueOnce(fakeRecord) - .mockImplementationOnce(() => { - throw new Prisma.PrismaClientKnownRequestError('Already exists', { - code: 'code', - clientVersion: 'version', - }); - }) - .mockImplementationOnce(() => { - throw new Error('An unknown error'); - }), - - findUnique: jest.fn().mockImplementation(async (params?: any) => { - let record: FakeModel; - - if (params?.where?.uuid) { - record = fakeRecords.find( - (record) => record.uuid === params?.where?.uuid, - ); - } - - if (!record && params?.where?.uuid == 'uuid-triggering-error') { - throw new Prisma.PrismaClientKnownRequestError('unknown request', { - code: 'code', - clientVersion: 'version', - }); - } - - return record; - }), - }, -}; - -@Injectable() -class FakeRepository - extends PrismaRepositoryBase - implements FakeRepositoryPort -{ - constructor( - prisma: FakePrismaService, - mapper: FakeMapper, - eventEmitter: EventEmitter2, - ) { - super( - prisma.fake, - prisma, - mapper, - eventEmitter, - new Logger(FakeRepository.name), - ); - } -} - -describe('PrismaRepositoryBase', () => { - let fakeRepository: FakeRepository; - let prisma: FakePrismaService; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - EventEmitter2, - FakeRepository, - FakeMapper, - { - provide: FakePrismaService, - useValue: mockPrismaService, - }, - ], - }).compile(); - - fakeRepository = module.get(FakeRepository); - prisma = module.get(FakePrismaService); - }); - - it('should be defined', () => { - expect(fakeRepository).toBeDefined(); - expect(prisma).toBeDefined(); - }); - - describe('insert', () => { - it('should create a record', async () => { - jest.spyOn(prisma.fake, 'create'); - - await fakeRepository.insert( - FakeEntity.create({ - name: 'someFakeName', - }), - ); - expect(prisma.fake.create).toHaveBeenCalledTimes(1); - }); - - it('should throw a ConflictException if record already exists', async () => { - await expect( - fakeRepository.insert( - FakeEntity.create({ - name: 'someFakeName', - }), - ), - ).rejects.toBeInstanceOf(ConflictException); - }); - - it('should throw an Error if an error occurs', async () => { - await expect( - fakeRepository.insert( - FakeEntity.create({ - name: 'someFakeName', - }), - ), - ).rejects.toBeInstanceOf(Error); - }); - }); - - describe('findOneById', () => { - it('should find a record by its id', async () => { - const record = await fakeRepository.findOneById('uuid-3'); - expect(record.getProps().name).toBe('fakeName-3'); - }); - - it('should throw an Error for client error', async () => { - await expect( - fakeRepository.findOneById('uuid-triggering-error'), - ).rejects.toBeInstanceOf(Error); - }); - - it('should throw a NotFoundException if id is not found', async () => { - await expect( - fakeRepository.findOneById('wrong-id'), - ).rejects.toBeInstanceOf(NotFoundException); - }); - }); - - describe('healthCheck', () => { - it('should return a healthy result', async () => { - const res = await fakeRepository.healthCheck(); - expect(res).toBeTruthy(); - }); - - it('should throw an exception if database is not available', async () => { - await expect(fakeRepository.healthCheck()).rejects.toBeInstanceOf( - DatabaseErrorException, - ); - }); - - it('should throw a DatabaseErrorException if an error occurs', async () => { - await expect(fakeRepository.healthCheck()).rejects.toBeInstanceOf( - DatabaseErrorException, - ); - }); - }); -}); diff --git a/src/libs/tests/unit/ddd/aggregate-root.base.spec.ts b/src/libs/tests/unit/ddd/aggregate-root.base.spec.ts deleted file mode 100644 index 1b10ec6..0000000 --- a/src/libs/tests/unit/ddd/aggregate-root.base.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { - AggregateID, - AggregateRoot, - DomainEvent, - DomainEventProps, -} from '@libs/ddd'; -import { EventEmitter2 } from '@nestjs/event-emitter'; -import { v4 } from 'uuid'; - -interface FakeProps { - name: string; -} - -interface CreateFakeProps { - name: string; -} - -class FakeRecordCreatedDomainEvent extends DomainEvent { - readonly name: string; - - constructor(props: DomainEventProps) { - super(props); - this.name = props.name; - } -} - -class FakeEntity extends AggregateRoot { - protected readonly _id: AggregateID; - - static create = (create: CreateFakeProps): FakeEntity => { - const id = v4(); - const props: FakeProps = { ...create }; - const fake = new FakeEntity({ id, props }); - fake.addEvent( - new FakeRecordCreatedDomainEvent({ - aggregateId: id, - name: props.name, - }), - ); - return fake; - }; - - validate(): void { - // not implemented - } -} - -const mockLogger = { - debug: jest.fn(), - log: jest.fn(), - error: jest.fn(), - warn: jest.fn(), -}; - -describe('AggregateRoot Base', () => { - it('should define an aggregate root based object instance', () => { - const fakeInstance = FakeEntity.create({ - name: 'someFakeName', - }); - expect(fakeInstance).toBeDefined(); - expect(fakeInstance.domainEvents.length).toBe(1); - }); - - it('should publish domain events', async () => { - jest.spyOn(mockLogger, 'debug'); - const eventEmitter = new EventEmitter2(); - jest.spyOn(eventEmitter, 'emitAsync'); - const fakeInstance = FakeEntity.create({ - name: 'someFakeName', - }); - await fakeInstance.publishEvents(mockLogger, eventEmitter); - expect(mockLogger.debug).toHaveBeenCalledTimes(1); - expect(eventEmitter.emitAsync).toHaveBeenCalledTimes(1); - expect(fakeInstance.domainEvents.length).toBe(0); - }); -}); diff --git a/src/libs/tests/unit/ddd/command.base.spec.ts b/src/libs/tests/unit/ddd/command.base.spec.ts deleted file mode 100644 index 5ea8f42..0000000 --- a/src/libs/tests/unit/ddd/command.base.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Command, CommandProps } from '@libs/ddd'; -import { ArgumentNotProvidedException } from '@libs/exceptions'; - -class FakeCommand extends Command { - readonly name: string; - - constructor(props: CommandProps) { - super(props); - this.name = props.name; - } -} - -class BadFakeCommand extends Command { - constructor(props: CommandProps) { - super(props); - } -} - -describe('Command Base', () => { - it('should define a command based object instance', () => { - const fakeCommand = new FakeCommand({ name: 'fakeName' }); - expect(fakeCommand).toBeDefined(); - expect(fakeCommand.id.length).toBe(36); - }); - - it('should define a command based object instance with a provided id', () => { - const fakeCommand = new FakeCommand({ id: 'some-id', name: 'fakeName' }); - expect(fakeCommand.id).toBe('some-id'); - }); - - it('should define a command based object instance with metadata', () => { - const fakeCommand = new FakeCommand({ - name: 'fakeName', - metadata: { - correlationId: 'some-correlation-id', - causationId: 'some-causation-id', - userId: 'some-user-id', - timestamp: new Date('2023-06-28T05:00:00Z').getTime(), - }, - }); - expect(fakeCommand.metadata.timestamp).toBe(1687928400000); - }); - - it('should throw an exception if props are empty', () => { - expect(() => new BadFakeCommand({})).toThrow(ArgumentNotProvidedException); - }); -}); diff --git a/src/libs/tests/unit/ddd/domain-event.base.spec.ts b/src/libs/tests/unit/ddd/domain-event.base.spec.ts deleted file mode 100644 index d4751b2..0000000 --- a/src/libs/tests/unit/ddd/domain-event.base.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { DomainEvent, DomainEventProps } from '@libs/ddd'; -import { ArgumentNotProvidedException } from '@libs/exceptions'; - -class FakeDomainEvent extends DomainEvent { - readonly name: string; - - constructor(props: DomainEventProps) { - super(props); - this.name = props.name; - } -} - -describe('DomainEvent Base', () => { - it('should define a domain event based object instance', () => { - const fakeDomainEvent = new FakeDomainEvent({ - aggregateId: 'some-id', - name: 'some-name', - }); - expect(fakeDomainEvent).toBeDefined(); - expect(fakeDomainEvent.id.length).toBe(36); - }); - - it('should define a domain event based object instance with metadata', () => { - const fakeDomainEvent = new FakeDomainEvent({ - aggregateId: 'some-id', - name: 'some-name', - metadata: { - correlationId: 'some-correlation-id', - causationId: 'some-causation-id', - userId: 'some-user-id', - timestamp: new Date('2023-06-28T05:00:00Z').getTime(), - }, - }); - expect(fakeDomainEvent.metadata.timestamp).toBe(1687928400000); - }); - it('should throw an exception if props are empty', () => { - const emptyProps: DomainEventProps = undefined; - expect(() => new FakeDomainEvent(emptyProps)).toThrow( - ArgumentNotProvidedException, - ); - }); -}); diff --git a/src/libs/tests/unit/ddd/entity.base.spec.ts b/src/libs/tests/unit/ddd/entity.base.spec.ts deleted file mode 100644 index 38f90f2..0000000 --- a/src/libs/tests/unit/ddd/entity.base.spec.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { Entity } from '@libs/ddd'; -import { ArgumentOutOfRangeException } from '@libs/exceptions'; - -interface FakeProps { - name: string; -} - -class FakeEntity extends Entity { - protected _id: string; - - validate(): void { - // not implemented - } -} - -describe('Entity Base', () => { - it('should define an entity based object instance', () => { - const fakeInstance = new FakeEntity({ - id: 'some-id', - props: { - name: 'some-name', - }, - }); - expect(fakeInstance).toBeDefined(); - expect(fakeInstance.id).toBe('some-id'); - expect(fakeInstance.createdAt).toBeInstanceOf(Date); - expect(fakeInstance.updatedAt).toBeInstanceOf(Date); - expect(FakeEntity.isEntity(fakeInstance)).toBeTruthy(); - }); - - it('should define an entity with given created and updated dates', () => { - const fakeInstance = new FakeEntity({ - id: 'some-id', - createdAt: new Date('2023-06-28T05:00:00Z'), - updatedAt: new Date('2023-06-28T06:00:00Z'), - props: { - name: 'some-name', - }, - }); - expect(fakeInstance.createdAt.getUTCHours()).toBe(5); - expect(fakeInstance.updatedAt.getUTCHours()).toBe(6); - }); - - it('should compare entities', () => { - const fakeInstance = new FakeEntity({ - id: 'some-id', - props: { - name: 'some-name', - }, - }); - const fakeInstanceClone = new FakeEntity({ - id: 'some-id', - props: { - name: 'some-name', - }, - }); - const fakeInstanceNotReallyClone = new FakeEntity({ - id: 'some-slightly-different-id', - props: { - name: 'some-name', - }, - }); - const undefinedFakeInstance: FakeEntity = undefined; - expect(fakeInstance.equals(undefinedFakeInstance)).toBeFalsy(); - expect(fakeInstance.equals(fakeInstance)).toBeTruthy(); - expect(fakeInstance.equals(fakeInstanceClone)).toBeTruthy(); - expect(fakeInstance.equals(fakeInstanceNotReallyClone)).toBeFalsy(); - }); - - it('should convert entity to plain object', () => { - const fakeInstance = new FakeEntity({ - id: 'some-id', - createdAt: new Date('2023-06-28T05:00:00Z'), - updatedAt: new Date('2023-06-28T06:00:00Z'), - props: { - name: 'some-name', - }, - }); - expect(fakeInstance.toObject()).toEqual({ - id: 'some-id', - createdAt: new Date('2023-06-28T05:00:00.000Z'), - updatedAt: new Date('2023-06-28T06:00:00.000Z'), - name: 'some-name', - }); - }); - - it('should throw an exception if props number is too high', () => { - interface BigFakeProps { - prop1: string; - prop2: string; - prop3: string; - prop4: string; - prop5: string; - prop6: string; - prop7: string; - prop8: string; - prop9: string; - prop10: string; - prop11: string; - prop12: string; - prop13: string; - prop14: string; - prop15: string; - prop16: string; - prop17: string; - prop18: string; - prop19: string; - prop20: string; - prop21: string; - prop22: string; - prop23: string; - prop24: string; - prop25: string; - prop26: string; - prop27: string; - prop28: string; - prop29: string; - prop30: string; - prop31: string; - prop32: string; - prop33: string; - prop34: string; - prop35: string; - prop36: string; - prop37: string; - prop38: string; - prop39: string; - prop40: string; - prop41: string; - prop42: string; - prop43: string; - prop44: string; - prop45: string; - prop46: string; - prop47: string; - prop48: string; - prop49: string; - prop50: string; - prop51: string; - } - - class BigFakeEntity extends Entity { - protected _id: string; - - validate(): void { - // not implemented - } - } - expect( - () => - new BigFakeEntity({ - id: 'some-id', - props: { - prop1: 'some-name', - prop2: 'some-name', - prop3: 'some-name', - prop4: 'some-name', - prop5: 'some-name', - prop6: 'some-name', - prop7: 'some-name', - prop8: 'some-name', - prop9: 'some-name', - prop10: 'some-name', - prop11: 'some-name', - prop12: 'some-name', - prop13: 'some-name', - prop14: 'some-name', - prop15: 'some-name', - prop16: 'some-name', - prop17: 'some-name', - prop18: 'some-name', - prop19: 'some-name', - prop20: 'some-name', - prop21: 'some-name', - prop22: 'some-name', - prop23: 'some-name', - prop24: 'some-name', - prop25: 'some-name', - prop26: 'some-name', - prop27: 'some-name', - prop28: 'some-name', - prop29: 'some-name', - prop30: 'some-name', - prop31: 'some-name', - prop32: 'some-name', - prop33: 'some-name', - prop34: 'some-name', - prop35: 'some-name', - prop36: 'some-name', - prop37: 'some-name', - prop38: 'some-name', - prop39: 'some-name', - prop40: 'some-name', - prop41: 'some-name', - prop42: 'some-name', - prop43: 'some-name', - prop44: 'some-name', - prop45: 'some-name', - prop46: 'some-name', - prop47: 'some-name', - prop48: 'some-name', - prop49: 'some-name', - prop50: 'some-name', - prop51: 'some-name', - }, - }), - ).toThrow(ArgumentOutOfRangeException); - }); -}); diff --git a/src/libs/tests/unit/ddd/query.base.spec.ts b/src/libs/tests/unit/ddd/query.base.spec.ts deleted file mode 100644 index 9d93d8f..0000000 --- a/src/libs/tests/unit/ddd/query.base.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - PaginatedParams, - PaginatedQueryBase, - QueryBase, -} from '@libs/ddd/query.base'; - -class FakeQuery extends QueryBase { - readonly id: string; - - constructor(id: string) { - super(); - this.id = id; - } -} - -describe('Query Base', () => { - it('should define a query based object instance', () => { - const fakeQuery = new FakeQuery('some-id'); - expect(fakeQuery).toBeDefined(); - }); -}); - -class FakePaginatedQuery extends PaginatedQueryBase { - readonly id: string; - - constructor(props: PaginatedParams) { - super(props); - this.id = props.id; - } -} - -describe('Paginated Query Base', () => { - it('should define a paginated query based object instance', () => { - const fakePaginatedQuery = new FakePaginatedQuery({ - id: 'some-id', - page: 1, - }); - expect(fakePaginatedQuery).toBeDefined(); - }); -}); diff --git a/src/libs/tests/unit/ddd/value-object.base.spec.ts b/src/libs/tests/unit/ddd/value-object.base.spec.ts deleted file mode 100644 index 2b99005..0000000 --- a/src/libs/tests/unit/ddd/value-object.base.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ValueObject } from '@libs/ddd'; - -interface FakeProps { - name: string; -} - -class FakeValueObject extends ValueObject { - get name(): string { - return this.props.name; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected validate(props: FakeProps): void { - return; - } -} - -describe('Value Object Base', () => { - it('should create a base value object', () => { - const fakeValueObject = new FakeValueObject({ name: 'fakeName' }); - expect(fakeValueObject).toBeDefined(); - expect(ValueObject.isValueObject(fakeValueObject)).toBeTruthy(); - }); - - it('should compare value objects', () => { - const fakeValueObject = new FakeValueObject({ name: 'fakeName' }); - const fakeValueObjectClone = new FakeValueObject({ name: 'fakeName' }); - const undefinedFakeValueObject: FakeValueObject = undefined; - const nullFakeValueObject: FakeValueObject = null; - expect(fakeValueObject.equals(undefinedFakeValueObject)).toBeFalsy(); - expect(fakeValueObject.equals(nullFakeValueObject)).toBeFalsy(); - expect(fakeValueObject.equals(fakeValueObject)).toBeTruthy(); - expect(fakeValueObject.equals(fakeValueObjectClone)).toBeTruthy(); - }); - - it('should unpack value object props', () => { - const fakeValueObject = new FakeValueObject({ name: 'fakeName' }); - expect(fakeValueObject.unpack()).toEqual({ - name: 'fakeName', - }); - }); -}); diff --git a/src/libs/tests/unit/guard.spec.ts b/src/libs/tests/unit/guard.spec.ts deleted file mode 100644 index 8a4b543..0000000 --- a/src/libs/tests/unit/guard.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Guard } from '@libs/guard'; - -describe('Guard', () => { - describe('isEmpty', () => { - it('should return false for a number', () => { - expect(Guard.isEmpty(1)).toBeFalsy(); - }); - it('should return false for a falsy boolean', () => { - expect(Guard.isEmpty(false)).toBeFalsy(); - }); - it('should return false for a truthy boolean', () => { - expect(Guard.isEmpty(true)).toBeFalsy(); - }); - it('should return true for undefined', () => { - expect(Guard.isEmpty(undefined)).toBeTruthy(); - }); - it('should return true for null', () => { - expect(Guard.isEmpty(null)).toBeTruthy(); - }); - it('should return false for a Date', () => { - expect(Guard.isEmpty(new Date('2023-06-28'))).toBeFalsy(); - }); - it('should return false for an object with keys', () => { - expect(Guard.isEmpty({ key: 'value' })).toBeFalsy(); - }); - it('should return true for an object without keys', () => { - expect(Guard.isEmpty({})).toBeTruthy(); - }); - it('should return true for an array without values', () => { - expect(Guard.isEmpty([])).toBeTruthy(); - }); - it('should return true for an array with only empty values', () => { - expect(Guard.isEmpty([null, undefined])).toBeTruthy(); - }); - it('should return false for an array with some empty values', () => { - expect(Guard.isEmpty([1, null, undefined])).toBeFalsy(); - }); - it('should return true for an empty string', () => { - expect(Guard.isEmpty('')).toBeTruthy(); - }); - }); - describe('lengthIsBetween', () => { - it('should return true for a string in the range', () => { - expect(Guard.lengthIsBetween('test', 0, 4)).toBeTruthy(); - }); - it('should return true for a number in the range', () => { - expect(Guard.lengthIsBetween(2, 0, 4)).toBeTruthy(); - }); - it('should return true for an array with number of elements in the range', () => { - expect(Guard.lengthIsBetween([1, 2, 3], 0, 4)).toBeTruthy(); - }); - it('should return false for a string not in the range', () => { - expect(Guard.lengthIsBetween('test', 5, 9)).toBeFalsy(); - }); - it('should return false for a number not in the range', () => { - expect(Guard.lengthIsBetween(2, 3, 6)).toBeFalsy(); - }); - it('should return false for an array with number of elements not in the range', () => { - expect(Guard.lengthIsBetween([1, 2, 3], 10, 12)).toBeFalsy(); - }); - it('should throw an exception if value is empty', () => { - expect(() => Guard.lengthIsBetween(undefined, 0, 4)).toThrow(); - }); - }); -}); diff --git a/src/libs/tests/unit/utils/rpc-validation-pipe.usecase.spec.ts b/src/libs/tests/unit/utils/rpc-validation-pipe.usecase.spec.ts deleted file mode 100644 index fffdad4..0000000 --- a/src/libs/tests/unit/utils/rpc-validation-pipe.usecase.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ArgumentMetadata } from '@nestjs/common'; -import { FindAdByIdRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/find-ad-by-id.request.dto'; -import { RpcValidationPipe } from '@libs/utils/pipes/rpc.validation-pipe'; - -describe('RpcValidationPipe', () => { - it('should not validate request', async () => { - const target: RpcValidationPipe = new RpcValidationPipe({ - whitelist: true, - forbidUnknownValues: false, - }); - const metadata: ArgumentMetadata = { - type: 'body', - metatype: FindAdByIdRequestDto, - data: '', - }; - await target.transform({}, metadata).catch((err) => { - expect(err.message).toEqual('Rpc Exception'); - }); - }); -}); diff --git a/src/libs/types/index.ts b/src/libs/types/index.ts deleted file mode 100644 index da248df..0000000 --- a/src/libs/types/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** Consider creating a bunch of shared custom utility - * types for different situations. - * Alternatively you can use a library like - * https://github.com/andnp/SimplyTyped - */ -export * from './object-literal.type'; diff --git a/src/libs/types/object-literal.type.ts b/src/libs/types/object-literal.type.ts deleted file mode 100644 index c8d50ec..0000000 --- a/src/libs/types/object-literal.type.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Interface of the simple literal object with any string keys. - */ -export interface ObjectLiteral { - [key: string]: unknown; -} diff --git a/src/libs/utils/convert-props-to-object.util.ts b/src/libs/utils/convert-props-to-object.util.ts deleted file mode 100644 index 4605fc7..0000000 --- a/src/libs/utils/convert-props-to-object.util.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types */ -import { Entity } from '../ddd/entity.base'; -import { ValueObject } from '../ddd/value-object.base'; - -function isEntity(obj: unknown): obj is Entity { - /** - * 'instanceof Entity' causes error here for some reason. - * Probably creates some circular dependency. This is a workaround - * until I find a solution :) - */ - return ( - Object.prototype.hasOwnProperty.call(obj, 'toObject') && - Object.prototype.hasOwnProperty.call(obj, 'id') && - ValueObject.isValueObject((obj as Entity).id) - ); -} - -function convertToPlainObject(item: any): any { - if (ValueObject.isValueObject(item)) { - return item.unpack(); - } - if (isEntity(item)) { - return item.toObject(); - } - return item; -} - -/** - * Converts Entity/Value Objects props to a plain object. - * Useful for testing and debugging. - * @param props - */ -export function convertPropsToObject(props: any): any { - const propsCopy = structuredClone(props); - - // eslint-disable-next-line guard-for-in - for (const prop in propsCopy) { - if (Array.isArray(propsCopy[prop])) { - propsCopy[prop] = (propsCopy[prop] as Array).map((item) => { - return convertToPlainObject(item); - }); - } - propsCopy[prop] = convertToPlainObject(propsCopy[prop]); - } - - return propsCopy; -} diff --git a/src/libs/utils/index.ts b/src/libs/utils/index.ts deleted file mode 100644 index 546588f..0000000 --- a/src/libs/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './convert-props-to-object.util'; diff --git a/src/libs/utils/pipes/rpc.validation-pipe.ts b/src/libs/utils/pipes/rpc.validation-pipe.ts deleted file mode 100644 index 75b1852..0000000 --- a/src/libs/utils/pipes/rpc.validation-pipe.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { RpcExceptionCode } from '@libs/exceptions/rpc-exception.codes.enum'; -import { Injectable, ValidationPipe } from '@nestjs/common'; -import { RpcException } from '@nestjs/microservices'; - -@Injectable() -export class RpcValidationPipe extends ValidationPipe { - createExceptionFactory() { - return (validationErrors = []) => { - return new RpcException({ - code: RpcExceptionCode.INVALID_ARGUMENT, - message: this.flattenValidationErrors(validationErrors), - }); - }; - } -} diff --git a/src/modules/ad/ad.mapper.ts b/src/modules/ad/ad.mapper.ts index 66ee22f..5e2deb2 100644 --- a/src/modules/ad/ad.mapper.ts +++ b/src/modules/ad/ad.mapper.ts @@ -1,4 +1,4 @@ -import { Mapper } from '@libs/ddd'; +import { Mapper } from '@mobicoop/ddd-library'; import { AdResponseDto } from './interface/dtos/ad.response.dto'; import { Inject, Injectable } from '@nestjs/common'; import { AdEntity } from './core/domain/ad.entity'; diff --git a/src/modules/ad/ad.module.ts b/src/modules/ad/ad.module.ts index 163cdc9..7360608 100644 --- a/src/modules/ad/ad.module.ts +++ b/src/modules/ad/ad.module.ts @@ -18,12 +18,12 @@ import { MessagePublisher } from './infrastructure/message-publisher'; import { AdMapper } from './ad.mapper'; import { CreateAdService } from './core/application/commands/create-ad/create-ad.service'; import { TimezoneFinder } from './infrastructure/timezone-finder'; -import { PrismaService } from '@libs/db/prisma.service'; import { TimeConverter } from './infrastructure/time-converter'; import { FindAdByIdGrpcController } from './interface/grpc-controllers/find-ad-by-id.grpc.controller'; import { FindAdByIdQueryHandler } from './core/application/queries/find-ad-by-id/find-ad-by-id.query-handler'; import { PublishMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler'; import { PublishLogMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler'; +import { PrismaService } from './infrastructure/prisma.service'; const grpcControllers = [CreateAdGrpcController, FindAdByIdGrpcController]; diff --git a/src/modules/ad/core/application/commands/create-ad/create-ad.command.ts b/src/modules/ad/core/application/commands/create-ad/create-ad.command.ts index b68de1b..4c4dd67 100644 --- a/src/modules/ad/core/application/commands/create-ad/create-ad.command.ts +++ b/src/modules/ad/core/application/commands/create-ad/create-ad.command.ts @@ -1,8 +1,8 @@ -import { Command, CommandProps } from '@libs/ddd'; import { Schedule } from '../../types/schedule'; import { MarginDurations } from '../../types/margin-durations'; import { Waypoint } from '../../types/waypoint'; import { Frequency } from '@modules/ad/core/domain/ad.types'; +import { Command, CommandProps } from '@mobicoop/ddd-library'; export class CreateAdCommand extends Command { readonly userId: string; diff --git a/src/modules/ad/core/application/commands/create-ad/create-ad.service.ts b/src/modules/ad/core/application/commands/create-ad/create-ad.service.ts index cfee514..831bf2e 100644 --- a/src/modules/ad/core/application/commands/create-ad/create-ad.service.ts +++ b/src/modules/ad/core/application/commands/create-ad/create-ad.service.ts @@ -2,14 +2,13 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { CreateAdCommand } from './create-ad.command'; import { Inject } from '@nestjs/common'; import { AD_REPOSITORY, PARAMS_PROVIDER } from '@modules/ad/ad.di-tokens'; -import { AggregateID } from '@libs/ddd'; import { AdEntity } from '@modules/ad/core/domain/ad.entity'; -import { ConflictException } from '@libs/exceptions'; import { Waypoint } from '../../types/waypoint'; import { DefaultParams } from '../../ports/default-params.type'; import { AdRepositoryPort } from '../../ports/ad.repository.port'; import { DefaultParamsProviderPort } from '../../ports/default-params-provider.port'; import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors'; +import { AggregateID, ConflictException } from '@mobicoop/ddd-library'; @CommandHandler(CreateAdCommand) export class CreateAdService implements ICommandHandler { diff --git a/src/modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler.ts b/src/modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler.ts index 0b14b4a..14b801c 100644 --- a/src/modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler.ts +++ b/src/modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler.ts @@ -1,8 +1,8 @@ -import { MessagePublisherPort } from '@libs/ports/message-publisher.port'; import { Inject, Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; import { MESSAGE_PUBLISHER } from '@src/app.constants'; import { AdCreatedDomainEvent } from '../../domain/events/ad-created.domain-events'; +import { MessagePublisherPort } from '@mobicoop/ddd-library'; @Injectable() export class PublishLogMessageWhenAdIsCreatedDomainEventHandler { diff --git a/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler.ts b/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler.ts index 647af0b..5b6c51d 100644 --- a/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler.ts +++ b/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler.ts @@ -1,8 +1,8 @@ -import { MessagePublisherPort } from '@libs/ports/message-publisher.port'; import { Inject, Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; import { MESSAGE_PUBLISHER } from '@src/app.constants'; import { AdCreatedDomainEvent } from '../../domain/events/ad-created.domain-events'; +import { MessagePublisherPort } from '@mobicoop/ddd-library'; @Injectable() export class PublishMessageWhenAdIsCreatedDomainEventHandler { diff --git a/src/modules/ad/core/application/ports/ad.repository.port.ts b/src/modules/ad/core/application/ports/ad.repository.port.ts index 43e00eb..5b9fe49 100644 --- a/src/modules/ad/core/application/ports/ad.repository.port.ts +++ b/src/modules/ad/core/application/ports/ad.repository.port.ts @@ -1,4 +1,4 @@ -import { RepositoryPort } from '@libs/ddd'; +import { RepositoryPort } from '@mobicoop/ddd-library'; import { AdEntity } from '../../domain/ad.entity'; export type AdRepositoryPort = RepositoryPort; diff --git a/src/modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query.ts b/src/modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query.ts index defce96..ec23431 100644 --- a/src/modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query.ts +++ b/src/modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query.ts @@ -1,4 +1,4 @@ -import { QueryBase } from '@libs/ddd/query.base'; +import { QueryBase } from '@mobicoop/ddd-library'; export class FindAdByIdQuery extends QueryBase { readonly id: string; diff --git a/src/modules/ad/core/domain/ad.entity.ts b/src/modules/ad/core/domain/ad.entity.ts index 3d682ab..660799f 100644 --- a/src/modules/ad/core/domain/ad.entity.ts +++ b/src/modules/ad/core/domain/ad.entity.ts @@ -1,4 +1,4 @@ -import { AggregateRoot, AggregateID } from '@libs/ddd'; +import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library'; import { v4 } from 'uuid'; import { AdCreatedDomainEvent } from './events/ad-created.domain-events'; import { AdProps, CreateAdProps, DefaultAdProps } from './ad.types'; diff --git a/src/modules/ad/core/domain/ad.errors.ts b/src/modules/ad/core/domain/ad.errors.ts index ec53993..7d14547 100644 --- a/src/modules/ad/core/domain/ad.errors.ts +++ b/src/modules/ad/core/domain/ad.errors.ts @@ -1,4 +1,4 @@ -import { ExceptionBase } from '@libs/exceptions'; +import { ExceptionBase } from '@mobicoop/ddd-library'; export class AdAlreadyExistsException extends ExceptionBase { static readonly message = 'Ad already exists'; diff --git a/src/modules/ad/core/domain/events/ad-created.domain-events.ts b/src/modules/ad/core/domain/events/ad-created.domain-events.ts index 812e960..9acf7f1 100644 --- a/src/modules/ad/core/domain/events/ad-created.domain-events.ts +++ b/src/modules/ad/core/domain/events/ad-created.domain-events.ts @@ -1,4 +1,4 @@ -import { DomainEvent, DomainEventProps } from '@libs/ddd'; +import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; export class AdCreatedDomainEvent extends DomainEvent { readonly userId: string; diff --git a/src/modules/ad/core/domain/value-objects/address.value-object.ts b/src/modules/ad/core/domain/value-objects/address.value-object.ts index 66a5da7..87659b6 100644 --- a/src/modules/ad/core/domain/value-objects/address.value-object.ts +++ b/src/modules/ad/core/domain/value-objects/address.value-object.ts @@ -1,4 +1,4 @@ -import { ValueObject } from '@libs/ddd'; +import { ValueObject } from '@mobicoop/ddd-library'; import { CoordinatesProps } from './coordinates.value-object'; /** Note: diff --git a/src/modules/ad/core/domain/value-objects/coordinates.value-object.ts b/src/modules/ad/core/domain/value-objects/coordinates.value-object.ts index 7451d8a..9fbb0ae 100644 --- a/src/modules/ad/core/domain/value-objects/coordinates.value-object.ts +++ b/src/modules/ad/core/domain/value-objects/coordinates.value-object.ts @@ -1,4 +1,4 @@ -import { ValueObject } from '@libs/ddd'; +import { ValueObject } from '@mobicoop/ddd-library'; /** Note: * Value Objects with multiple properties can contain diff --git a/src/modules/ad/core/domain/value-objects/margin-durations.value-object.ts b/src/modules/ad/core/domain/value-objects/margin-durations.value-object.ts index f69ccf8..9621389 100644 --- a/src/modules/ad/core/domain/value-objects/margin-durations.value-object.ts +++ b/src/modules/ad/core/domain/value-objects/margin-durations.value-object.ts @@ -1,4 +1,4 @@ -import { ValueObject } from '@libs/ddd'; +import { ValueObject } from '@mobicoop/ddd-library'; /** Note: * Value Objects with multiple properties can contain diff --git a/src/modules/ad/core/domain/value-objects/schedule.value-object.ts b/src/modules/ad/core/domain/value-objects/schedule.value-object.ts index da4696c..429f386 100644 --- a/src/modules/ad/core/domain/value-objects/schedule.value-object.ts +++ b/src/modules/ad/core/domain/value-objects/schedule.value-object.ts @@ -1,4 +1,4 @@ -import { ValueObject } from '@libs/ddd'; +import { ValueObject } from '@mobicoop/ddd-library'; /** Note: * Value Objects with multiple properties can contain diff --git a/src/modules/ad/core/domain/value-objects/waypoint.value-object.ts b/src/modules/ad/core/domain/value-objects/waypoint.value-object.ts index 1b8395d..8c1503b 100644 --- a/src/modules/ad/core/domain/value-objects/waypoint.value-object.ts +++ b/src/modules/ad/core/domain/value-objects/waypoint.value-object.ts @@ -1,4 +1,4 @@ -import { ValueObject } from '@libs/ddd'; +import { ValueObject } from '@mobicoop/ddd-library'; import { AddressProps } from './address.value-object'; /** Note: diff --git a/src/modules/ad/infrastructure/ad.repository.ts b/src/modules/ad/infrastructure/ad.repository.ts index bd89fba..5ccc174 100644 --- a/src/modules/ad/infrastructure/ad.repository.ts +++ b/src/modules/ad/infrastructure/ad.repository.ts @@ -1,10 +1,10 @@ import { Injectable, Logger } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { AdEntity } from '../core/domain/ad.entity'; -import { PrismaService } from '@libs/db/prisma.service'; import { AdMapper } from '../ad.mapper'; -import { PrismaRepositoryBase } from '@libs/db/prisma-repository.base'; import { AdRepositoryPort } from '../core/application/ports/ad.repository.port'; +import { PrismaRepositoryBase } from '@mobicoop/ddd-library'; +import { PrismaService } from './prisma.service'; export type AdBaseModel = { uuid: string; diff --git a/src/modules/ad/infrastructure/message-publisher.ts b/src/modules/ad/infrastructure/message-publisher.ts index 69122e5..c0b5bfd 100644 --- a/src/modules/ad/infrastructure/message-publisher.ts +++ b/src/modules/ad/infrastructure/message-publisher.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; import { MESSAGE_BROKER_PUBLISHER } from '@src/app.constants'; -import { MessagePublisherPort } from '@libs/ports/message-publisher.port'; +import { MessagePublisherPort } from '@mobicoop/ddd-library'; @Injectable() export class MessagePublisher implements MessagePublisherPort { diff --git a/src/libs/db/prisma.service.ts b/src/modules/ad/infrastructure/prisma.service.ts similarity index 100% rename from src/libs/db/prisma.service.ts rename to src/modules/ad/infrastructure/prisma.service.ts diff --git a/src/modules/ad/interface/dtos/ad.paginated.response.dto.ts b/src/modules/ad/interface/dtos/ad.paginated.response.dto.ts index e680096..634ece7 100644 --- a/src/modules/ad/interface/dtos/ad.paginated.response.dto.ts +++ b/src/modules/ad/interface/dtos/ad.paginated.response.dto.ts @@ -1,4 +1,4 @@ -import { PaginatedResponseDto } from '@libs/api/paginated.response.base'; +import { PaginatedResponseDto } from '@mobicoop/ddd-library'; import { AdResponseDto } from './ad.response.dto'; export class AdPaginatedResponseDto extends PaginatedResponseDto { diff --git a/src/modules/ad/interface/dtos/ad.response.dto.ts b/src/modules/ad/interface/dtos/ad.response.dto.ts index 6401451..dfeef37 100644 --- a/src/modules/ad/interface/dtos/ad.response.dto.ts +++ b/src/modules/ad/interface/dtos/ad.response.dto.ts @@ -1,4 +1,4 @@ -import { ResponseBase } from '@libs/api/response.base'; +import { ResponseBase } from '@mobicoop/ddd-library'; import { Frequency } from '@modules/ad/core/domain/ad.types'; export class AdResponseDto extends ResponseBase { diff --git a/src/modules/ad/interface/grpc-controllers/create-ad.grpc.controller.ts b/src/modules/ad/interface/grpc-controllers/create-ad.grpc.controller.ts index 215536f..cafb7f9 100644 --- a/src/modules/ad/interface/grpc-controllers/create-ad.grpc.controller.ts +++ b/src/modules/ad/interface/grpc-controllers/create-ad.grpc.controller.ts @@ -2,10 +2,10 @@ import { Controller, UsePipes } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { GrpcMethod, RpcException } from '@nestjs/microservices'; import { CreateAdRequestDto } from './dtos/create-ad.request.dto'; -import { AggregateID } from '@libs/ddd'; -import { IdResponse } from '@libs/api/id.response.dto'; -import { RpcExceptionCode } from '@libs/exceptions/rpc-exception.codes.enum'; -import { RpcValidationPipe } from '@libs/utils/pipes/rpc.validation-pipe'; +import { AggregateID } from '@mobicoop/ddd-library'; +import { IdResponse } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { RpcValidationPipe } from '@mobicoop/ddd-library'; import { CreateAdCommand } from '@modules/ad/core/application/commands/create-ad/create-ad.command'; import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors'; diff --git a/src/modules/ad/interface/grpc-controllers/find-ad-by-id.grpc.controller.ts b/src/modules/ad/interface/grpc-controllers/find-ad-by-id.grpc.controller.ts index 1862ea5..eb31d19 100644 --- a/src/modules/ad/interface/grpc-controllers/find-ad-by-id.grpc.controller.ts +++ b/src/modules/ad/interface/grpc-controllers/find-ad-by-id.grpc.controller.ts @@ -6,9 +6,9 @@ import { FindAdByIdQuery } from '@modules/ad/core/application/queries/find-ad-by import { AdResponseDto } from '../dtos/ad.response.dto'; import { AdEntity } from '@modules/ad/core/domain/ad.entity'; import { AdMapper } from '@modules/ad/ad.mapper'; -import { NotFoundException } from '@libs/exceptions'; -import { RpcExceptionCode } from '@libs/exceptions/rpc-exception.codes.enum'; -import { RpcValidationPipe } from '@libs/utils/pipes/rpc.validation-pipe'; +import { NotFoundException } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { RpcValidationPipe } from '@mobicoop/ddd-library'; @UsePipes( new RpcValidationPipe({ diff --git a/src/modules/ad/tests/integration/ad.repository.spec.ts b/src/modules/ad/tests/integration/ad.repository.spec.ts index ffd1da3..a5875e7 100644 --- a/src/modules/ad/tests/integration/ad.repository.spec.ts +++ b/src/modules/ad/tests/integration/ad.repository.spec.ts @@ -1,4 +1,3 @@ -import { PrismaService } from '@libs/db/prisma.service'; import { AD_REPOSITORY, PARAMS_PROVIDER, @@ -8,6 +7,7 @@ import { import { AdMapper } from '@modules/ad/ad.mapper'; import { AdRepository } from '@modules/ad/infrastructure/ad.repository'; import { DefaultParamsProvider } from '@modules/ad/infrastructure/default-params-provider'; +import { PrismaService } from '@modules/ad/infrastructure/prisma.service'; import { TimeConverter } from '@modules/ad/infrastructure/time-converter'; import { TimezoneFinder } from '@modules/ad/infrastructure/timezone-finder'; import { ConfigModule } from '@nestjs/config'; diff --git a/src/modules/ad/tests/unit/core/create-ad.service.spec.ts b/src/modules/ad/tests/unit/core/create-ad.service.spec.ts index a86fb1e..3e42dcd 100644 --- a/src/modules/ad/tests/unit/core/create-ad.service.spec.ts +++ b/src/modules/ad/tests/unit/core/create-ad.service.spec.ts @@ -2,9 +2,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AD_REPOSITORY, PARAMS_PROVIDER } from '@modules/ad/ad.di-tokens'; import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto'; import { CreateAdRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/create-ad.request.dto'; -import { AggregateID } from '@libs/ddd'; +import { AggregateID } from '@mobicoop/ddd-library'; import { AdEntity } from '@modules/ad/core/domain/ad.entity'; -import { ConflictException } from '@libs/exceptions'; +import { ConflictException } from '@mobicoop/ddd-library'; import { Frequency } from '@modules/ad/core/domain/ad.types'; import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port'; import { CreateAdService } from '@modules/ad/core/application/commands/create-ad/create-ad.service'; diff --git a/src/modules/ad/tests/unit/infrastructure/ad.repository.spec.ts b/src/modules/ad/tests/unit/infrastructure/ad.repository.spec.ts index fc1c48b..3f79692 100644 --- a/src/modules/ad/tests/unit/infrastructure/ad.repository.spec.ts +++ b/src/modules/ad/tests/unit/infrastructure/ad.repository.spec.ts @@ -1,4 +1,3 @@ -import { PrismaService } from '@libs/db/prisma.service'; import { PARAMS_PROVIDER, TIMEZONE_FINDER, @@ -9,6 +8,7 @@ import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/de import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port'; import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port'; import { AdRepository } from '@modules/ad/infrastructure/ad.repository'; +import { PrismaService } from '@modules/ad/infrastructure/prisma.service'; import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter'; import { Test, TestingModule } from '@nestjs/testing'; diff --git a/src/modules/ad/tests/unit/interface/create-ad.grpc.controller.spec.ts b/src/modules/ad/tests/unit/interface/create-ad.grpc.controller.spec.ts index f4f5ac4..3313aeb 100644 --- a/src/modules/ad/tests/unit/interface/create-ad.grpc.controller.spec.ts +++ b/src/modules/ad/tests/unit/interface/create-ad.grpc.controller.spec.ts @@ -1,5 +1,5 @@ -import { IdResponse } from '@libs/api/id.response.dto'; -import { RpcExceptionCode } from '@libs/exceptions/rpc-exception.codes.enum'; +import { IdResponse } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors'; import { Frequency } from '@modules/ad/core/domain/ad.types'; import { CreateAdGrpcController } from '@modules/ad/interface/grpc-controllers/create-ad.grpc.controller'; diff --git a/src/modules/ad/tests/unit/interface/find-ad-by-id.grpc.controller.spec.ts b/src/modules/ad/tests/unit/interface/find-ad-by-id.grpc.controller.spec.ts index 495e1eb..a2064d5 100644 --- a/src/modules/ad/tests/unit/interface/find-ad-by-id.grpc.controller.spec.ts +++ b/src/modules/ad/tests/unit/interface/find-ad-by-id.grpc.controller.spec.ts @@ -1,5 +1,5 @@ -import { NotFoundException } from '@libs/exceptions'; -import { RpcExceptionCode } from '@libs/exceptions/rpc-exception.codes.enum'; +import { NotFoundException } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; import { AdMapper } from '@modules/ad/ad.mapper'; import { Frequency } from '@modules/ad/core/domain/ad.types'; import { FindAdByIdGrpcController } from '@modules/ad/interface/grpc-controllers/find-ad-by-id.grpc.controller'; diff --git a/src/modules/health/core/aplication/usecases/repositories.health-indicator.usecase.ts b/src/modules/health/core/aplication/usecases/repositories.health-indicator.usecase.ts index b842f9d..34aa245 100644 --- a/src/modules/health/core/aplication/usecases/repositories.health-indicator.usecase.ts +++ b/src/modules/health/core/aplication/usecases/repositories.health-indicator.usecase.ts @@ -10,7 +10,7 @@ import { AD_REPOSITORY } from '@modules/health/health.di-tokens'; import { AdRepositoryPort } from '@modules/ad/core/application/ports/ad.repository.port'; import { MESSAGE_PUBLISHER } from '@src/app.constants'; import { LOGGING_AD_HEALTH_CRIT } from '@modules/health/health.constants'; -import { MessagePublisherPort } from '@libs/ports/message-publisher.port'; +import { MessagePublisherPort } from '@mobicoop/ddd-library'; @Injectable() export class RepositoriesHealthIndicatorUseCase extends HealthIndicator { diff --git a/src/modules/health/infrastructure/message-publisher.ts b/src/modules/health/infrastructure/message-publisher.ts index 036743b..0177bc3 100644 --- a/src/modules/health/infrastructure/message-publisher.ts +++ b/src/modules/health/infrastructure/message-publisher.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MESSAGE_BROKER_PUBLISHER } from '../../../app.constants'; import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; -import { MessagePublisherPort } from '@libs/ports/message-publisher.port'; +import { MessagePublisherPort } from '@mobicoop/ddd-library'; @Injectable() export class MessagePublisher implements MessagePublisherPort { diff --git a/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts b/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts index c0d5d78..314afc6 100644 --- a/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts +++ b/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts @@ -3,7 +3,7 @@ import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus'; import { RepositoriesHealthIndicatorUseCase } from '../../core/aplication/usecases/repositories.health-indicator.usecase'; import { AD_REPOSITORY } from '@modules/health/health.di-tokens'; import { MESSAGE_PUBLISHER } from '@src/app.constants'; -import { DatabaseErrorException } from '@libs/exceptions'; +import { DatabaseErrorException } from '@mobicoop/ddd-library'; const mockAdRepository = { healthCheck: jest