1.7.5 • Published 7 months ago

@devsvipcommerce/vipcommerce-lib-ddd v1.7.5

Weekly downloads
-
License
MIT
Repository
bitbucket
Last release
7 months ago

Vipcommerce Lib DDD NodeJs

Para instâncias dos tipos Entity, AggregateRoot ou ValueObject deve ser implementado um construtor privado, dessa forma garantimos que ao instânciar o objeto vamos passar por validações pertinentes.

Entity

Para utilizar uma entidade siga o exemplo abaixo:

export interface ExampleProps {
  name: string;
}

export class Example extends Entity<ExampleProps> {
  private constructor(props: ExampleProps, id?: UniqueEntityID) {
    super(props, id);
  }

  public static create(props: ExampleProps, id?: UniqueEntityID): Either<IGuardFieldResult[], Example> {
    //é sugerido a implementação de validações e comportamentos do domínio, ou seja functions para adicionar comportamentos antes da instância
    const ex = new Example(props, id);
    return right(ex);
  }
}

AggregateRoot

Para utilizar um aggregate root siga o exemplo abaixo:

export interface ExampleProps {
  name: string;
}

export class Example extends AggregateRoot<ExampleProps> {
  private constructor(props: ExampleProps, id?: UniqueEntityID) {
    super(props, id);
  }

  public static create(props: ExampleProps, id?: UniqueEntityID): Either<IGuardFieldResult[], Example> {
    //é sugerido a implementação de validações e comportamentos do domínio, ou seja functions para adicionar comportamentos antes da instância
    const ex = new Example(props, id);
    return right(ex);
  }
}

Value Object

Value objects devem ser imutáveis, exemplo de implementação abaixo:

export interface ExampleProps {
  name: string;
}

export class Example extends ValueObject<ExampleProps> {
  private constructor(props: ExampleProps) {
    super(props);
  }

  public static create(props: ExampleProps): Either<IGuardFieldResult[], Example> {
    //é sugerido a implementação de validações e comportamentos do domínio, ou seja functions para adicionar comportamentos antes da instância
    const ex = new Example(props);
    return right(ex);
  }
}

Casos de uso

Os casos de uso são parecidos com services, porém com uma implementação e responsabilidade para apenas um caso de regra da aplicação.

Para implementar um caso de uso siga o exemplo abaixo:

export namespace ExampleErrors {
  export class CamposInvalidos extends UseCaseError {
    constructor(errors: Array<IGuardFieldResult>) {
      super(errors);
    }
  }

  export class ErroAoCriar extends UseCaseError {
    constructor() {
      super('Sua Mensagem de erro aqui');
    }
  }
}

export class ExampleRequest {
  @ApiProperty({ example: '123' })
  id?: string;

  @ApiProperty({ example: 'biscoito' })
  name: string;
}


type Response = Either<
  | ExampleErrors.CamposInvalidos
  | ExampleErrors.ErroAoCriar
  void
>;

// Caso precise de uma resposta de retorno adicione como segundo parametro do type Response, em lugar do void no Response acima

@Injectable()
export class ExampleUseCase
  implements UseCase<ExampleRequest, Response>
{
  private readonly logger = new Logger(
    ExampleUseCase.name,
  );
  constructor(
    private readonly exampleRepository: ExampleRepository,
  ) {}

  async execute({
    id,
    name,
  }: ExampleRequest): Promise<Response> {

    const exampleOuError = Example.create(
      {
        name,
      },
      new UniqueEntityID(id),
    );
// Você também pode não querer passar um id, e neste ele será gerado ao fazer o create

    if (exampleOuError.isLeft()) {
      return left(
        new ExampleErrors.CamposInvalidos(
          exampleOuError.value,
        ),
      );
    }

    try {
      await this.exampleRepository.save(
        exampleOuError.value,
      );
      return right(null);
    } catch (error) {
      this.logger.error(error);
      return left(new ExampleErrors.ErroAoCriar());
    }
  }
}

Controller

No controller abaixo estamos verificando se ocorreu algum erro e então adicionamos o erro ao log e verificamos qual tipo de erro foi instanciado

@Controller({
  version: '1',
  path: 'exemplos',
})
export class ExemploController {
  private readonly logger = new Logger(ExemploController.name);
  constructor(private readonly exemploUseCase: ExemploUseCase) {}

  @Post()
  async criarExemplo(@Body() request: ExemploRequest) {
    const result = await this.exemploUseCase.execute(request);
    if (result.isLeft()) {
      const error = result.value;
      this.logger.error(JSON.stringify(error));
      switch (error.constructor) {
        case ExemploErrors.CamposInvalidos:
          throw new BadRequestException(error.errors);
        case ExemploErrors.ErroAoCriar:
          throw new InternalServerErrorException();
      }
    }
  }
}

AppErrorHandler

AppErrorHandler é um módulo que fornece um interceptor e um decorator para mapeamento dos erros da aplicação para um erro HTTP representativo.

Ao utilizar o decorator @MapAppErrorToHttpException no método do Controller, não é necessário o tratamento com switch/case da resposta do useCase.

Para utilizar importe o módulo AppErrorHandlerModule

....
import { AppErrorHandlerModule } from '@devsvipcommerce/vipcommerce-lib-ddd';
....
@Module({
  ....
  imports: [
    ....,
    AppErrorHandlerModule
  ],
  ....
})
export class ApiModule {}

Para realizar o mapeamento dos erros utilize o decorator @MapAppErrorToHttpException

@Controller({
  version: '1',
  path: 'exemplos',
})
export class ExemploController {
  constructor(private readonly exemploUseCase: ExemploUseCase) {}

  @Post()
  @MapAppErrorToHttpException(ExemploErrors.CamposInvalidos,BadRequestException)
  @MapAppErrorToHttpException(ExemploErrors.ErroAoCriar, InternalServerErrorException)
  async criarExemplo(@Body() request: ExemploRequest) {
    return this.exemploUseCase.execute(request);
  }
}

Ainda é possível utilizar uma factory para construção de um erro personalizado.

@MapAppErrorToHttpException(
    ProdutoErrors.ProdutoNaoExiste,
    NotFoundException,
    (appErro:ProdutoErrors.ProdutoNaoExiste) => new NotFoundException(`O produto ${appErro.sku} não existe!`),
  )
1.7.3

7 months ago

1.7.2

7 months ago

1.7.1

7 months ago

1.7.0

7 months ago

1.6.0

11 months ago

1.7.5

7 months ago

1.7.4

7 months ago

1.5.0

1 year ago

1.4.0

1 year ago

1.1.3

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.9

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.1.2

1 year ago

1.0.3

1 year ago

1.0.11

1 year ago

1.0.10

1 year ago

1.0.15

1 year ago

1.0.14

1 year ago

1.0.13

1 year ago

1.0.12

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago