Implementando Clean Architecture com TypeScript

RMAG news

Clean Architecture é uma filosofia de design de software que visa criar sistemas fáceis de manter, testar e entender. Ela enfatiza a separação de responsabilidades, garantindo que cada parte do sistema tenha uma única responsabilidade. Neste artigo, exploraremos como implementar Clean Architecture usando TypeScript.

Índice

Introdução à Clean Architecture
Princípios Fundamentais
Configurando o Projeto
Estrutura de Pastas
Entidades
Casos de Uso
Interfaces
Frameworks e Drivers
Juntando Tudo
Conclusão

Introdução à Clean Architecture

Clean Architecture, introduzida por Robert C. Martin (Uncle Bob), proporciona uma separação clara entre as diferentes partes de um sistema de software. A ideia principal é manter a lógica de negócios central independente de fatores externos, como bancos de dados, UI ou frameworks.

Princípios Fundamentais

Independência: A lógica de negócios deve ser independente de UI, banco de dados ou sistemas externos.

Testabilidade: O sistema deve ser fácil de testar.

Separação de Responsabilidades: Diferentes partes do sistema devem ter responsabilidades distintas.

Manutenibilidade: O sistema deve ser fácil de manter e evoluir.

Configurando o Projeto

Primeiro, vamos configurar um projeto TypeScript. Você pode usar npm ou yarn para inicializar um novo projeto.

mkdir clean-architecture-ts
cd clean-architecture-ts
npm init -y
npm install typescript ts-node @types/node –save-dev

Crie um arquivo tsconfig.json para configurar o TypeScript.

{
“compilerOptions”: {
“target”: “ES6”,
“module”: “commonjs”,
“outDir”: “./dist”,
“rootDir”: “./src”,
“strict”: true,
“esModuleInterop”: true
}
}

Estrutura de Pastas

Um projeto com clean architecture geralmente tem a seguinte estrutura de pastas:

src/
├── entities/
├── usecases/
├── interfaces/
├── frameworks/
└── main.ts

Entidades

Entidades representam a lógica de negócios central. Elas são a parte mais importante do sistema e devem ser independentes de fatores externos.

// src/entities/user.entity.ts
export class User {
constructor(id: string, public email: string, public password:string) {}

static create(email: string, password: string) {
const userId = uuid()
return new User(userId, email, password)
}
}

Casos de Uso

Casos de uso contêm as regras de negócios específicas da aplicação. Eles orquestram a interação entre entidades e interfaces.

// src/usecases/create-user.usecase.ts
import { User } from ../entities/user.entity;
import { UsersRepository } from ../interfaces/users.repository

interface CreateUserRequest {
email: string;
password: string;
}

export class CreateUserUseCase {
constructor(private userRepository: UserRepository) {}

async execute(request: CreateUserRequest): Promise<void> {
const user = User.create(request.email, request.password)
await this.userRepository.save(user);
}
}

Interfaces

Interfaces são os contratos entre os casos de uso e o mundo externo. Elas podem incluir repositórios, serviços ou qualquer sistema externo.

// src/interfaces/users.repository.ts
import { User } from ../entities/user.entity;

export interface UserRepository {
save(user: User): Promise<void>;
}

Frameworks e Drivers

Frameworks e drivers contêm os detalhes de implementação das interfaces. Eles interagem com sistemas externos, como bancos de dados ou APIs.

// src/frameworks/in-memory-users.repository.ts
import { User } from ../entities/User;
import { UserRepository } from ../interfaces/users.repository;

export class InMemoryUsersRepository implements UserRepository {
private users: User[] = [];

async save(user: User): Promise<void> {
this.users.push(user);
}
}

Juntando Tudo

Finalmente, vamos criar um ponto de entrada para conectar tudo.

// src/main.ts
import { CreateUser } from ./usecases/create-user.usecase;
import { InMemoryUserRepository } from ./frameworks/in-memory-users.repository;

const userRepository = new InMemoryUserRepository();
const createUser = new CreateUserUseCase(userRepository);

createUser.execute({ email: john.doe@example.com, password: 123456 })
.then(() => console.log(User created successfully))
.catch(err => console.error(Failed to create user, err));

Compile e execute o projeto:

tsc
node dist/main.js

Conclusão

Seguindo os princípios da Clean Architecture, podemos criar um sistema que é manutenível, testável e adaptável a mudanças. TypeScript fornece tipagem forte e recursos modernos de JavaScript que ajudam a impor esses princípios. Com uma clara separação de responsabilidades, nosso código se torna mais fácil de entender e evoluir ao longo do tempo.

Please follow and like us:
Pin Share