DEV Community

Davi Orlandi
Davi Orlandi

Posted on

5

Implementando Clean Architecture com TypeScript

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

  1. Introdução à Clean Architecture
  2. Princípios Fundamentais
  3. Configurando o Projeto
  4. Estrutura de Pastas
  5. Entidades
  6. Casos de Uso
  7. Interfaces
  8. Frameworks e Drivers
  9. Juntando Tudo
  10. 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

  1. Independência: A lógica de negócios deve ser independente de UI, banco de dados ou sistemas externos.
  2. Testabilidade: O sistema deve ser fácil de testar.
  3. Separação de Responsabilidades: Diferentes partes do sistema devem ter responsabilidades distintas.
  4. 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
Enter fullscreen mode Exit fullscreen mode

Crie um arquivo tsconfig.json para configurar o TypeScript.

{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Estrutura de Pastas

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

src/
├── entities/
├── usecases/
├── interfaces/
├── frameworks/
└── main.ts
Enter fullscreen mode Exit fullscreen mode

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)
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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));
Enter fullscreen mode Exit fullscreen mode

Compile e execute o projeto:

tsc
node dist/main.js
Enter fullscreen mode Exit fullscreen mode

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.

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (2)

Collapse
 
cquintella profile image
𝖈𝖖𝖚𝖎𝖓𝖙𝖊𝖑𝖑𝖆

Gostei, tenho seguido a Clean Architecture, no entanto tenho uma dúvida se no type script uso DTO ou interface. O que vcs usam, classicamente a Interface seria mais pra comportamentos e não para dados, no typescript a comunidade parece usar para os dois. Qual seria melhor?

export type UserDTO = {
id: number;
name: string;
email: string;
password:string;
}

vs.

export interface UserInterface {
id:number;
name:string;
email:string;
password:string;
}

Collapse
 
marialuizaleitao profile image
Maria Leitão

Muito boa a explicação!!

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay