@devsvipcommerce/vipcommerce-lib-ddd
v1.7.5
Published
Lib VIPCOMMERCE DDD para projetos NodeJs
Downloads
111
Keywords
Readme
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!`),
)