Skip to content

Commit

Permalink
feat: add specification typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
4lessandrodev committed Mar 20, 2021
0 parents commit 1816dc7
Show file tree
Hide file tree
Showing 13 changed files with 7,774 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## SPECIFICATION PATTERN

> Reaproveitamento de código
> Regra de negócio multável ou dinâmica
> Muitas condicionais
O que vamos fazer?
Criar uma regra specification que vai identificar
se o usuário que vamos informar pode estar com
COVID-19, se precisa de UTI ou se precisa de atendimento médico.
Com base nos sintomas:

Mais comuns: (More Common)

- Febre
- Tosse seca
- cansaço

Menos comuns: (Less Common)

- Dores e desconfortos
- Dor de garganta
- Diarreia
- Conjuntivite
- Dor de cabeça
- Perda de paladar ou olfato
- Erupção cutânea na pele

Sintomas mais graves: (Serious)

- Dificuldade de respirar
- Dor no peito
- Perda de fala

User

> Sintomas (symptoms)
11 changes: 11 additions & 0 deletions jest.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"transform": {
"^.+\\.ts$": "ts-jest"
},
"testRegex": "\\.spec\\.ts$",
"moduleFileExtensions": [
"js",
"json",
"ts"
]
}
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "specification-covid",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"test": "jest"
},
"dependencies": {
"@types/jest": "^26.0.21",
"@types/node": "^14.14.35",
"jest": "^26.6.3",
"ts-jest": "^26.5.4",
"typescript": "^4.2.3"
}
}
3 changes: 3 additions & 0 deletions src/interfaces/entity.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export abstract class Entity<T> {
constructor(protected readonly props: T) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface ISpecification<T> {
isSatisfiedBy: (target: T) => boolean;
and: (other: ISpecification<T>) => ISpecification<T>;
or: (other: ISpecification<T>) => ISpecification<T>;
}
43 changes: 43 additions & 0 deletions src/specification/common/specification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ISpecification } from './interfaces/specification.interface';

export abstract class Specification<T> implements ISpecification<T> {
abstract isSatisfiedBy(target: T): boolean;
and(other: ISpecification<T>): ISpecification<T> {
return new AndSpecification<T>(this, other);
}
or(other: ISpecification<T>): ISpecification<T> {
return new OrSpecification<T>(this, other);
}
}

export class AndSpecification<T> extends Specification<T> {
constructor(
private readonly one: ISpecification<T>,
private readonly other: ISpecification<T>,
) {
super();
}

isSatisfiedBy(target: T): boolean {
return (
this.one.isSatisfiedBy(target) &&
this.other.isSatisfiedBy(target)
);
}
}

export class OrSpecification<T> extends Specification<T> {
constructor(
private readonly one: ISpecification<T>,
private readonly other: ISpecification<T>,
) {
super();
}

isSatisfiedBy(target: T): boolean {
return (
this.one.isSatisfiedBy(target) ||
this.other.isSatisfiedBy(target)
);
}
}
42 changes: 42 additions & 0 deletions src/user/user.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Entity } from '../interfaces/entity.interface';

export enum typeSymptoms {
'FEBRE',
'TOSSE_SECA',
'CANSACO',
'DORES',
'DOR_DE_GARGANTA',
'DIARREIRA',
'CONJUNTIVITE',
'DOR_DE_CABECA',
'PERDA_DO_PALADAR',
'ERUPCAO_CULTANE',
'DIFICULDADE_DE_RESPIRAR',
'DOR_NO_PEITO',
'PERDA_DE_FALA',
}

export type symptom = keyof typeof typeSymptoms;

export interface UserProps {
name: string;
symptoms?: symptom[];
}

export class User extends Entity<UserProps> {
private constructor(props: UserProps) {
super(props);
}

get name(): string {
return this.props.name;
}

get symptoms(): symptom[] {
return this.props.symptoms;
}

public static create(props: UserProps): User {
return new User(props);
}
}
145 changes: 145 additions & 0 deletions src/user/user.specification.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { User } from './user.entity';
import {
UserHasAllCommonSymptomsSpecification as CommonSymptom,
UserNeedMedical,
UserNeedsUTI,
} from './user.specification';
import { UserHasLessCommonSymptomsSpecification as LessCommonSymptom } from './user.specification';
import { UserHasSomeSeriousSymptomsSpecification as SeriousSymptom } from './user.specification';

describe('user.specification', () => {
it('user should has fever', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['FEBRE', 'CANSACO', 'TOSSE_SECA'],
});

const result = new CommonSymptom().isSatisfiedBy(Peter);
expect(result).toBe(true);
});

it('user should has not fever', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['DOR_NO_PEITO'],
});

const result = new CommonSymptom().isSatisfiedBy(Peter);
expect(result).toBe(false);
});

it('user should has some less common symptom', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['DIARREIRA'],
});

const result = new LessCommonSymptom().isSatisfiedBy(Peter);
expect(result).toBe(true);
});

it('user should has not less common symptom', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['FEBRE'],
});

const result = new LessCommonSymptom().isSatisfiedBy(Peter);
expect(result).toBe(false);
});

it('user should has some serious symptom', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['DIFICULDADE_DE_RESPIRAR'],
});

const result = new SeriousSymptom().isSatisfiedBy(Peter);
expect(result).toBe(true);
});

it('user should has not serious symptom', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['DIARREIRA'],
});

const result = new SeriousSymptom().isSatisfiedBy(Peter);
expect(result).toBe(false);
});

it('user should needs UTI', () => {
const Peter = User.create({
name: 'Peter',
symptoms: [
'FEBRE',
'TOSSE_SECA',
'CANSACO',
'DIFICULDADE_DE_RESPIRAR',
],
});

const result = new UserNeedsUTI().isSatisfiedBy(Peter);
expect(result).toBe(true);
});

it('user should not needs UTI', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['FEBRE', 'TOSSE_SECA', 'CANSACO'],
});

const result = new UserNeedsUTI().isSatisfiedBy(Peter);
expect(result).toBe(false);
});

it('user should not needs UTI', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['TOSSE_SECA', 'CANSACO', 'DIFICULDADE_DE_RESPIRAR'],
});

const result = new UserNeedsUTI().isSatisfiedBy(Peter);
expect(result).toBe(false);
});

it('user should needs medical ', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['FEBRE', 'TOSSE_SECA', 'CANSACO'],
});

const result = new UserNeedMedical().isSatisfiedBy(Peter);
expect(result).toBe(true);
});

it('user should needs medical ', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['FEBRE', 'TOSSE_SECA', 'CANSACO', 'PERDA_DE_FALA'],
});

const result = new UserNeedMedical().isSatisfiedBy(Peter);
expect(result).toBe(true);
});

it('user should needs medical ', () => {
const Peter = User.create({
name: 'Peter',
symptoms: ['PERDA_DE_FALA'],
});

const result = new UserNeedMedical().isSatisfiedBy(Peter);
expect(result).toBe(true);
});

it('user should not needs medical ', () => {
const Peter = User.create({
name: 'Peter',
symptoms: [],
});

const result = new UserNeedMedical().isSatisfiedBy(Peter);
expect(result).toBe(false);
});
});
68 changes: 68 additions & 0 deletions src/user/user.specification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Specification } from '../specification/common/specification';
import { symptom, User } from './user.entity';

export class UserHasAllCommonSymptomsSpecification extends Specification<User> {
private readonly SYMPTOM_CRITERIAL_FEVER: symptom = 'FEBRE';
private readonly SYMPTOM_CRITERIAL_TOSSE_SECA: symptom = 'TOSSE_SECA';
private readonly SYMPTOM_CRITERIAL_CANSACO: symptom = 'CANSACO';

isSatisfiedBy(user: User): boolean {
const symptoms = user.symptoms;
return (
symptoms.includes(this.SYMPTOM_CRITERIAL_FEVER) &&
symptoms.includes(this.SYMPTOM_CRITERIAL_TOSSE_SECA) &&
symptoms.includes(this.SYMPTOM_CRITERIAL_CANSACO)
);
}
}

export class UserHasLessCommonSymptomsSpecification extends Specification<User> {
private readonly SYMPTOM_CRITERIAL: symptom[] = [
'DORES',
'DOR_DE_GARGANTA',
'DIARREIRA',
'CONJUNTIVITE',
'DOR_DE_CABECA',
'PERDA_DO_PALADAR',
'ERUPCAO_CULTANE',
];

isSatisfiedBy(user: User): boolean {
const symptoms = user.symptoms;
return symptoms
.map((symptom) => this.SYMPTOM_CRITERIAL.includes(symptom))
.includes(true);
}
}

export class UserHasSomeSeriousSymptomsSpecification extends Specification<User> {
private readonly SYMPTOM_CRITERIAL: symptom[] = [
'DIFICULDADE_DE_RESPIRAR',
'DOR_NO_PEITO',
'PERDA_DE_FALA',
];

isSatisfiedBy(user: User): boolean {
const symptoms = user.symptoms;
return symptoms
.map((symptom) => this.SYMPTOM_CRITERIAL.includes(symptom))
.includes(true);
}
}

export class UserNeedsUTI extends Specification<User> {
isSatisfiedBy(user: User): boolean {
return new UserHasAllCommonSymptomsSpecification()
.and(new UserHasSomeSeriousSymptomsSpecification())
.isSatisfiedBy(user);
}
}

export class UserNeedMedical extends Specification<User> {
isSatisfiedBy(user: User): boolean {
return new UserHasAllCommonSymptomsSpecification()
.or(new UserHasSomeSeriousSymptomsSpecification())
.or(new UserHasLessCommonSymptomsSpecification())
.isSatisfiedBy(user);
}
}
15 changes: 15 additions & 0 deletions tasks.todo
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Passos

✔ Iniciar projeto com yarn @done(21-03-20 11:30)
✔ Instalar dependências @done(21-03-20 11:33)
- Typescript
- Jest
- ts-jest
- @types/jest
- @tyeps/node
✔ Criar nossa classe abstrata entity @done(21-03-20 14:00)
✔ Criar interface de usuário @done(21-03-20 14:00)
✔ Criar classe usuário @done(21-03-20 14:00)
✔ Criar classe specification @done(21-03-20 15:32)
✔ Criar classe de sintomar specification @done(21-03-20 15:32)
✔ Criar testes @done(21-03-20 15:32)
Loading

0 comments on commit 1816dc7

Please sign in to comment.