diff --git a/README.md b/README.md index f1df69d..b68bb7a 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ Ports (for Driven Adapters) are interfaces that define contracts which must be i - Ports should be created to fit the Domain needs, not simply mimic the tools APIs. - Mock implementations can be passed to ports while testing. Mocking makes your tests faster and independent from the environment. -Example file: [event-emitter.port.ts](src/application/ports/event-emitter.port.ts) +Example file: [event-emitter.port.ts](src/core/ports/event-emitter.port.ts) --- @@ -338,7 +338,7 @@ Lets distinguish two types of protection from illegal states: at **compile time* Types give useful semantic information to a developer. Good code should be easy to use correctly, and hard to use incorrectly. Types system can be a good help for that. It can prevent some nasty errors at a compile time, so IDE will show type errors right away. -The simplest example may be using enums instead of constants, for example: [events.ts](src/application/events/events.ts). This file has enums of events that can occur in a program. Now, event emitter port [event-emitter.port.ts](src/application/ports/event-emitter.port.ts) uses that events type to prevent illegal types pass. If you try to pass anything that is not intended it will show type error. +The simplest example may be using enums instead of constants, for example: [events.ts](src/core/events/events.ts). This file has enums of events that can occur in a program. Now, event emitter port [event-emitter.port.ts](src/core/ports/event-emitter.port.ts) uses that events type to prevent illegal types pass. If you try to pass anything that is not intended it will show type error. More importantly, this approach can be used to make business logic safer. @@ -379,7 +379,7 @@ Validating will inform immediately when `Value Object` is created with corrupted To avoid repeating same validation code between different domain objects consider using [guards](https://medium.com/better-programming/refactoring-guard-clauses-2ceeaa1a9da). -Example file: [guard.ts](src/domain/guard.ts) +Example file: [guard.ts](src/core/guard.ts) **Keep in mind** that not all validations can be done in a single `Value Object`, it should validate only rules shared by all contexts. There are cases when validation may be different depending on a context, or one field may invole another field, or even a different entity. Handle those cases accordingly. @@ -599,8 +599,8 @@ By default, Error objects seralize to JSON with output like this: Consider serializing errors by creating a `toJSON()` method so it can be easily sent to other processes as a plain object. -- Exception abstract base class example: [exception.base.ts](/src/infrastructure/exceptions/exception.base.ts) -- Domain Validation Exception class example: [domain-validation.exception.ts](src/infrastructure/exceptions/domain-validation.exception.ts) +- Exception abstract base class example: [exception.base.ts](src/core/exceptions/exception.base.ts) +- Domain Validation Exception class example: [domain-validation.exception.ts](src/core/exceptions/domain-validation.exception.ts) Read more: [Better error handling in JavaScript](https://iaincollins.medium.com/error-handling-in-javascript-a6172ccdf9af) diff --git a/src/domain/.gitignore b/src/core/.gitignore similarity index 100% rename from src/domain/.gitignore rename to src/core/.gitignore diff --git a/src/domain/base-classes/entity.base.ts b/src/core/base-classes/entity.base.ts similarity index 100% rename from src/domain/base-classes/entity.base.ts rename to src/core/base-classes/entity.base.ts diff --git a/src/domain/base-classes/value-object.base.ts b/src/core/base-classes/value-object.base.ts similarity index 100% rename from src/domain/base-classes/value-object.base.ts rename to src/core/base-classes/value-object.base.ts diff --git a/src/application/events/events.ts b/src/core/events/events.ts similarity index 100% rename from src/application/events/events.ts rename to src/core/events/events.ts diff --git a/src/infrastructure/exceptions/argument-out-of-range.exception.ts b/src/core/exceptions/argument-out-of-range.exception.ts similarity index 100% rename from src/infrastructure/exceptions/argument-out-of-range.exception.ts rename to src/core/exceptions/argument-out-of-range.exception.ts diff --git a/src/infrastructure/exceptions/business-rule-validation.exception.ts b/src/core/exceptions/business-rule-validation.exception.ts similarity index 100% rename from src/infrastructure/exceptions/business-rule-validation.exception.ts rename to src/core/exceptions/business-rule-validation.exception.ts diff --git a/src/infrastructure/exceptions/conflict.exception.ts b/src/core/exceptions/conflict.exception.ts similarity index 100% rename from src/infrastructure/exceptions/conflict.exception.ts rename to src/core/exceptions/conflict.exception.ts diff --git a/src/infrastructure/exceptions/domain-validation.exception.ts b/src/core/exceptions/domain-validation.exception.ts similarity index 100% rename from src/infrastructure/exceptions/domain-validation.exception.ts rename to src/core/exceptions/domain-validation.exception.ts diff --git a/src/infrastructure/exceptions/exception.base.ts b/src/core/exceptions/exception.base.ts similarity index 100% rename from src/infrastructure/exceptions/exception.base.ts rename to src/core/exceptions/exception.base.ts diff --git a/src/infrastructure/exceptions/exception.types.ts b/src/core/exceptions/exception.types.ts similarity index 100% rename from src/infrastructure/exceptions/exception.types.ts rename to src/core/exceptions/exception.types.ts diff --git a/src/core/exceptions/index.ts b/src/core/exceptions/index.ts new file mode 100644 index 0000000..8a370a7 --- /dev/null +++ b/src/core/exceptions/index.ts @@ -0,0 +1,8 @@ +export * from './exception.base'; +export * from './argument-out-of-range.exception'; +export * from './business-rule-validation.exception'; +export * from './conflict.exception'; +export * from './domain-validation.exception'; +export * from './exception.types'; +export * from './input-validation.exception'; +export * from './not-found.exception'; diff --git a/src/infrastructure/exceptions/input-validation.exception.ts b/src/core/exceptions/input-validation.exception.ts similarity index 100% rename from src/infrastructure/exceptions/input-validation.exception.ts rename to src/core/exceptions/input-validation.exception.ts diff --git a/src/infrastructure/exceptions/not-found.exception.ts b/src/core/exceptions/not-found.exception.ts similarity index 100% rename from src/infrastructure/exceptions/not-found.exception.ts rename to src/core/exceptions/not-found.exception.ts diff --git a/src/domain/guard.ts b/src/core/guard.ts similarity index 100% rename from src/domain/guard.ts rename to src/core/guard.ts diff --git a/src/application/ports/event-emitter.port.ts b/src/core/ports/event-emitter.port.ts similarity index 100% rename from src/application/ports/event-emitter.port.ts rename to src/core/ports/event-emitter.port.ts diff --git a/src/application/ports/generic.ports.ts b/src/core/ports/generic.ports.ts similarity index 90% rename from src/application/ports/generic.ports.ts rename to src/core/ports/generic.ports.ts index baddfc4..19684c6 100644 --- a/src/application/ports/generic.ports.ts +++ b/src/core/ports/generic.ports.ts @@ -1,4 +1,4 @@ -import { ID } from 'src/domain/value-objects/id.value-object'; +import { ID } from 'src/core/value-objects/id.value-object'; /* Most of repos will probably need generic save/find/delete operations, so it's easier diff --git a/src/application/ports/logger.port.ts b/src/core/ports/logger.port.ts similarity index 100% rename from src/application/ports/logger.port.ts rename to src/core/ports/logger.port.ts diff --git a/src/domain/value-objects/date.value-object.ts b/src/core/value-objects/date.value-object.ts similarity index 79% rename from src/domain/value-objects/date.value-object.ts rename to src/core/value-objects/date.value-object.ts index 9f4c3c9..17beda3 100644 --- a/src/domain/value-objects/date.value-object.ts +++ b/src/core/value-objects/date.value-object.ts @@ -1,4 +1,4 @@ -import { DomainValidationException } from 'src/infrastructure/exceptions/domain-validation.exception'; +import { DomainValidationException } from '@exceptions'; import { ValueObject } from '../base-classes/value-object.base'; export class DateVO extends ValueObject { diff --git a/src/domain/value-objects/id.value-object.ts b/src/core/value-objects/id.value-object.ts similarity index 81% rename from src/domain/value-objects/id.value-object.ts rename to src/core/value-objects/id.value-object.ts index 1c6f9f1..5b7dda3 100644 --- a/src/domain/value-objects/id.value-object.ts +++ b/src/core/value-objects/id.value-object.ts @@ -1,4 +1,4 @@ -import { ArgumentOutOfRangeException } from 'src/infrastructure/exceptions/argument-out-of-range.exception'; +import { ArgumentOutOfRangeException } from '@exceptions'; import { ValueObject } from '../base-classes/value-object.base'; import { Guard } from '../guard'; diff --git a/src/infrastructure/adapters/event-emitter.adapter.ts b/src/infrastructure/adapters/event-emitter.adapter.ts index dd05e24..38b735f 100644 --- a/src/infrastructure/adapters/event-emitter.adapter.ts +++ b/src/infrastructure/adapters/event-emitter.adapter.ts @@ -1,5 +1,5 @@ import { NestEventEmitter } from 'nest-event'; -import { EventEmitterPort } from '../../application/ports/event-emitter.port'; +import { EventEmitterPort } from 'src/core/ports/event-emitter.port'; export class EventEmitterAdapter extends NestEventEmitter implements EventEmitterPort {} diff --git a/src/infrastructure/database/base-classes/orm-entity.base.ts b/src/infrastructure/database/base-classes/orm-entity.base.ts index 5b35bd7..a738ede 100644 --- a/src/infrastructure/database/base-classes/orm-entity.base.ts +++ b/src/infrastructure/database/base-classes/orm-entity.base.ts @@ -1,6 +1,6 @@ -import { EntityProps } from 'src/domain/base-classes/entity.base'; -import { DateVO } from 'src/domain/value-objects/date.value-object'; -import { ID } from 'src/domain/value-objects/id.value-object'; +import { EntityProps } from 'src/core/base-classes/entity.base'; +import { DateVO } from 'src/core/value-objects/date.value-object'; +import { ID } from 'src/core/value-objects/id.value-object'; import { CreateDateColumn, PrimaryGeneratedColumn, diff --git a/src/infrastructure/database/base-classes/repository.base.ts b/src/infrastructure/database/base-classes/repository.base.ts index 1bd00b0..4491be9 100644 --- a/src/infrastructure/database/base-classes/repository.base.ts +++ b/src/infrastructure/database/base-classes/repository.base.ts @@ -1,6 +1,6 @@ -import { RepositoryPort } from 'src/application/ports/generic.ports'; -import { ID } from 'src/domain/value-objects/id.value-object'; -import { NotFoundException } from 'src/infrastructure/exceptions/not-found.exception'; +import { RepositoryPort } from 'src/core/ports/generic.ports'; +import { ID } from 'src/core/value-objects/id.value-object'; +import { NotFoundException } from '@exceptions'; import { Repository } from 'typeorm'; import { OrmEntityBase } from './orm-entity.base'; diff --git a/src/infrastructure/interceptors/exception.interceptor.ts b/src/infrastructure/interceptors/exception.interceptor.ts index 2ddad10..993b13a 100644 --- a/src/infrastructure/interceptors/exception.interceptor.ts +++ b/src/infrastructure/interceptors/exception.interceptor.ts @@ -9,10 +9,12 @@ import { } from '@nestjs/common'; import { Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import { ConflictException } from '../exceptions/conflict.exception'; -import { ExceptionBase } from '../exceptions/exception.base'; -import { InputValidationException } from '../exceptions/input-validation.exception'; -import { NotFoundException } from '../exceptions/not-found.exception'; +import { + ExceptionBase, + ConflictException, + NotFoundException, + InputValidationException, +} from '@exceptions'; export class ExceptionInterceptor implements NestInterceptor { intercept( diff --git a/src/interface-adapters/base-classes/response.base.ts b/src/interface-adapters/base-classes/response.base.ts index 69017b1..c67e07e 100644 --- a/src/interface-adapters/base-classes/response.base.ts +++ b/src/interface-adapters/base-classes/response.base.ts @@ -1,4 +1,4 @@ -import { EntityProps } from 'src/domain/base-classes/entity.base'; +import { EntityProps } from 'src/core/base-classes/entity.base'; import { ApiProperty } from '@nestjs/swagger'; import { IdResponseDTO } from '../dtos/id.response.dto'; diff --git a/src/modules/user/database/user.repository.interface.ts b/src/modules/user/database/user.repository.interface.ts index 2b26e87..63a634d 100644 --- a/src/modules/user/database/user.repository.interface.ts +++ b/src/modules/user/database/user.repository.interface.ts @@ -1,4 +1,4 @@ -import { RepositoryPort } from 'src/application/ports/generic.ports'; +import { RepositoryPort } from 'src/core/ports/generic.ports'; import { UserEntity } from '../domain/entities/user.entity'; /* Repository port belongs to application's core, but since it usually diff --git a/src/modules/user/database/user.repository.ts b/src/modules/user/database/user.repository.ts index e125ceb..3cfcf5b 100644 --- a/src/modules/user/database/user.repository.ts +++ b/src/modules/user/database/user.repository.ts @@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Injectable } from '@nestjs/common'; import { UserEntity } from 'src/modules/user/domain/entities/user.entity'; -import { NotFoundException } from 'src/infrastructure/exceptions/not-found.exception'; +import { NotFoundException } from '@exceptions'; import { OrmEntityBase } from 'src/infrastructure/database/base-classes/orm-entity.base'; import { UserOrmEntity } from './user.orm-entity'; import { UserRepositoryPort } from './user.repository.interface'; diff --git a/src/modules/user/domain/entities/user.entity.ts b/src/modules/user/domain/entities/user.entity.ts index 38fc4b7..0aa003f 100644 --- a/src/modules/user/domain/entities/user.entity.ts +++ b/src/modules/user/domain/entities/user.entity.ts @@ -1,4 +1,4 @@ -import { Entity, EntityProps } from 'src/domain/base-classes/entity.base'; +import { Entity, EntityProps } from 'src/core/base-classes/entity.base'; import { Address } from '../value-objects/address.value-object'; import { Email } from '../value-objects/email.value-object'; diff --git a/src/modules/user/domain/value-objects/address.value-object.ts b/src/modules/user/domain/value-objects/address.value-object.ts index c66adad..8d9a0c5 100644 --- a/src/modules/user/domain/value-objects/address.value-object.ts +++ b/src/modules/user/domain/value-objects/address.value-object.ts @@ -1,7 +1,9 @@ -import { ValueObject } from 'src/domain/base-classes/value-object.base'; -import { Guard } from 'src/domain/guard'; -import { ArgumentOutOfRangeException } from 'src/infrastructure/exceptions/argument-out-of-range.exception'; -import { DomainValidationException } from 'src/infrastructure/exceptions/domain-validation.exception'; +import { ValueObject } from 'src/core/base-classes/value-object.base'; +import { Guard } from 'src/core/guard'; +import { + ArgumentOutOfRangeException, + DomainValidationException, +} from '@exceptions'; export interface AddressProps { country: string; diff --git a/src/modules/user/domain/value-objects/email.value-object.ts b/src/modules/user/domain/value-objects/email.value-object.ts index 3958c9e..10523df 100644 --- a/src/modules/user/domain/value-objects/email.value-object.ts +++ b/src/modules/user/domain/value-objects/email.value-object.ts @@ -1,5 +1,5 @@ -import { ValueObject } from 'src/domain/base-classes/value-object.base'; -import { DomainValidationException } from 'src/infrastructure/exceptions/domain-validation.exception'; +import { ValueObject } from 'src/core/base-classes/value-object.base'; +import { DomainValidationException } from '@exceptions'; export class Email extends ValueObject { constructor(value: string) { diff --git a/src/modules/user/use-cases/create-user/create-user.event.handler.ts b/src/modules/user/use-cases/create-user/create-user.event.handler.ts index fe77bb0..eb83758 100644 --- a/src/modules/user/use-cases/create-user/create-user.event.handler.ts +++ b/src/modules/user/use-cases/create-user/create-user.event.handler.ts @@ -1,5 +1,5 @@ import { On } from 'nest-event'; -import { UserEvents } from 'src/application/events/events'; +import { UserEvents } from 'src/core/events/events'; import { UserEntity } from '../../domain/entities/user.entity'; export class CreateUserEventHandler { diff --git a/src/modules/user/use-cases/create-user/create-user.service.ts b/src/modules/user/use-cases/create-user/create-user.service.ts index feb7288..34ad457 100644 --- a/src/modules/user/use-cases/create-user/create-user.service.ts +++ b/src/modules/user/use-cases/create-user/create-user.service.ts @@ -1,8 +1,8 @@ -import { ID } from 'src/domain/value-objects/id.value-object'; -import { EventEmitterPort } from 'src/application/ports/event-emitter.port'; -import { UserEvents } from 'src/application/events/events'; +import { ID } from 'src/core/value-objects/id.value-object'; +import { EventEmitterPort } from 'src/core/ports/event-emitter.port'; +import { UserEvents } from 'src/core/events/events'; import { UserRepositoryPort } from '@modules/user/database/user.repository.interface'; -import { ConflictException } from 'src/infrastructure/exceptions/conflict.exception'; +import { ConflictException } from '@exceptions'; import { CreateUserCommand } from './create-user.command'; import { UserEntity } from '../../domain/entities/user.entity'; diff --git a/src/modules/user/use-cases/remove-user/delete-user.service.ts b/src/modules/user/use-cases/remove-user/delete-user.service.ts index 62ea1d1..ab47fe0 100644 --- a/src/modules/user/use-cases/remove-user/delete-user.service.ts +++ b/src/modules/user/use-cases/remove-user/delete-user.service.ts @@ -1,5 +1,5 @@ -import { EventEmitterPort } from 'src/application/ports/event-emitter.port'; -import { UserEvents } from 'src/application/events/events'; +import { EventEmitterPort } from 'src/core/ports/event-emitter.port'; +import { UserEvents } from 'src/core/events/events'; import { UserRepositoryPort } from '@modules/user/database/user.repository.interface'; export class DeleteUserService { diff --git a/src/modules/user/use-cases/remove-user/user-deleted.event.handler.ts b/src/modules/user/use-cases/remove-user/user-deleted.event.handler.ts index 5967b6c..74c66a2 100644 --- a/src/modules/user/use-cases/remove-user/user-deleted.event.handler.ts +++ b/src/modules/user/use-cases/remove-user/user-deleted.event.handler.ts @@ -1,5 +1,5 @@ import { On } from 'nest-event'; -import { UserEvents } from 'src/application/events/events'; +import { UserEvents } from 'src/core/events/events'; import { UserEntity } from '../../domain/entities/user.entity'; export class UserDeletedEventHandler { diff --git a/tsconfig.json b/tsconfig.json index 135db60..feabbb4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,8 @@ "incremental": true, "paths": { "@modules/*": ["src/modules/*"], - "@config/*": ["src/infrastructure/configs/*"] + "@config/*": ["src/infrastructure/configs/*"], + "@exceptions": ["./src/core/exceptions"] } } }