Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 16, 2023 11:30 am GMT

Implementando o padro Specification no Node.js com TypeScript

O padro Specification um padro de projeto de software que permite definir regras de negcio em um formato legvel e reutilizvel. Ele particularmente til em aplicaes que precisam implementar regras de negcio complexas que envolvem mltiplas entidades. Neste artigo, vamos ver como implementar o padro Specification em uma aplicao Node.js com TypeScript.

Definindo a entidade

Para exemplificar o uso do padro Specification, vamos criar uma entidade simples chamada Transaction. Ela possui trs propriedades: amount (valor da transao), date (data da transao) e description (descrio da transao).

Crie um arquivo chamado transaction.ts na pasta src/entities com o seguinte contedo:

export class Transaction {  constructor(    public readonly amount: number,    public readonly date: Date,    public readonly description: string,  ) {}}

Esta classe representa uma transao financeira com os seguintes atributos:

amount: o valor da transao
date: a data da transao
description: uma descrio da transao

Criando as regras de negcio

Agora que temos a entidade de transao, vamos criar as regras de negcio para verificar se uma transao vlida ou no. Crie um arquivo chamado specification.ts na pasta src/specifications com o seguinte contedo:

export interface Specification<T> {  isSatisfiedBy(candidate: T): boolean;  and(other: Specification<T>): Specification<T>;  or(other: Specification<T>): Specification<T>;  not(): Specification<T>;}export class CompositeSpecification<T> implements Specification<T> {  public isSatisfiedBy(candidate: T): boolean {    throw new Error("Not implemented");  }  public and(other: Specification<T>): Specification<T> {    return new AndSpecification(this, other);  }  public or(other: Specification<T>): Specification<T> {    return new OrSpecification(this, other);  }  public not(): Specification<T> {    return new NotSpecification(this);  }}class AndSpecification<T> extends CompositeSpecification<T> {  constructor(    private readonly left: Specification<T>,    private readonly right: Specification<T>  ) {    super();  }  public isSatisfiedBy(candidate: T): boolean {    return (      this.left.isSatisfiedBy(candidate) && this.right.isSatisfiedBy(candidate)    );  }}export class OrSpecification<T> extends CompositeSpecification<T> {  constructor(    private readonly left: Specification<T>,    private readonly right: Specification<T>  ) {    super();  }  public isSatisfiedBy(candidate: T): boolean {    return (      this.left.isSatisfiedBy(candidate) || this.right.isSatisfiedBy(candidate)    );  }}export class NotSpecification<T> extends CompositeSpecification<T> {  constructor(private readonly specification: Specification<T>) {    super();  }  public isSatisfiedBy(candidate: T): boolean {    return !this.specification.isSatisfiedBy(candidate);  }}

Este arquivo define uma interface Specification que representa uma regra de negcio e uma classe CompositeSpecification que implementa a lgica de combinao de regras de negcio.

As classes AndSpecification, OrSpecification e NotSpecification so subclasses de CompositeSpecification que implementam as operaes lgicas AND, OR e NOT, respectivamente.

Crie um arquivo chamado transaction.ts na pasta src/specifications com o seguinte contedo:

import { Transaction } from "../entities/transaction";import { CompositeSpecification } from "./specification";export class TransactionSpecification extends CompositeSpecification<Transaction> {  public isSatisfiedBy(candidate: Transaction): boolean {    return TransactionSpecification.amountIsGreaterThan(50)      .and(TransactionSpecification.dateIsGreaterThan(new Date(2022, 0, 1)))      .and(TransactionSpecification.descriptionContains("supermercado"))      .isSatisfiedBy(candidate);  }  public static amountIsGreaterThan(value: number): TransactionSpecification {    return new (class extends TransactionSpecification {      public isSatisfiedBy(candidate: Transaction): boolean {        return candidate.amount > value;      }    })();  }  public static amountIsLessThan(value: number): TransactionSpecification {    return new (class extends TransactionSpecification {      public isSatisfiedBy(candidate: Transaction): boolean {        return candidate.amount < value;      }    })();  }  public static dateIsGreaterThan(value: Date): TransactionSpecification {    return new (class extends TransactionSpecification {      public isSatisfiedBy(candidate: Transaction): boolean {        return candidate.date > value;      }    })();  }  public static dateIsLessThan(value: Date): TransactionSpecification {    return new (class extends TransactionSpecification {      public isSatisfiedBy(candidate: Transaction): boolean {        return candidate.date < value;      }    })();  }  public static descriptionContains(value: string): TransactionSpecification {    return new (class extends TransactionSpecification {      public isSatisfiedBy(candidate: Transaction): boolean {        return candidate.description.includes(value);      }    })();  }}

A classe TransactionSpecification uma subclasse de CompositeSpecification que define as regras de negcio especficas para a entidade Transaction.

As funes estticas amountIsGreaterThan, amountIsLessThan, dateIsGreaterThan, dateIsLessThan e descriptionContains retornam instncias de TransactionSpecification que representam cada uma das regras de negcio. O mtodo isSatisfiedBy contem a composio da regra. a classe esta com as subsclasses publica a modo que ao criar uma outra regra pode ser apenas ajustada e extendida para compor.

Utilizando as regras de negcio

Agora que temos as regras de negcio definidas, podemos utiliz-las para validar uma transao. Crie um arquivo chamado transaction.ts na pasta src/validators com o seguinte contedo:

import { Transaction } from './Transaction';import { TransactionSpecification, Specification } from './TransactionSpecification';export class TransactionValidator {  constructor(private readonly specification: Specification<Transaction>) {}  public validate(transaction: Transaction): boolean {    return this.specification.isSatisfiedBy(transaction);  }}// testando o validatorconst transaction = new Transaction(100, new Date(), 'Compra no supermercado');const validator = new TransactionValidator()console.log(validator.validate(transaction)); // true

Este arquivo define a classe TransactionValidator, que recebe uma instncia de Specification<Transaction> no construtor e possui um mtodo validate que recebe uma transao e retorna um valor booleano indicando se a transao vlida ou no.

No exemplo acima, criamos uma transao com valor 100, data new Date() e descrio 'Compra no supermercado'. Em seguida, criamos uma instncia de TransactionValidator as regra de negcio verifica se o valor da transao maior que 50, se a data da transao maior que 01/01/2022 e se a descrio da transao contm a palavra 'supermercado'. Finalmente, chamamos o mtodo validate passando a transao criada e exibimos o resultado no console.

Concluso

Neste artigo, vimos como implementar o padro Specification em uma aplicao Node.js com TypeScript. Criamos uma entidade Transaction, definimos regras de negcio para validar uma transao utilizando a classe TransactionSpecification e criamos um validador de transaes utilizando a classe TransactionValidator.

O padro Specification pode ser aplicado em diversos contextos e uma boa opo quando precisamos implementar regras de negcio complexas que envolvem mltiplas entidades. Com ele, podemos tornar o cdigo mais modular, fcil de manter e extensvel.

Segue exemplo no github: https://github.com/jhonesgoncalves/example-specification-ts


Original Link: https://dev.to/jhonesgoncalves/implementando-o-padrao-specification-no-nodejs-com-typescript-15m0

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To