@devsvipcommerce/vipcommerce-lib-ddd v1.7.5
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!`),
)
7 months ago
7 months ago
7 months ago
7 months ago
11 months ago
7 months ago
7 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago