Pular para o conteúdo
Análise de Dados

Guia Prático de Design de Sistemas para Iniciantes

Admin5 min de leitura
Guia Prático de Design de Sistemas para Iniciantes

Guia Prático de Design de Sistemas para Iniciantes

Objetivo: oferecer uma visão clara e didática sobre como conceber a estrutura de um software, apresentando conceitos, diagramas e trechos de código que podem ser copiados e executados imediatamente.

Tecnologia e Inovação

Introdução

Desenvolver um sistema sem um plano sólido é como montar um quebra‑cabeça às cegas: as peças podem até se encaixar, mas o resultado costuma ser instável e difícil de manter. O design de sistemas (ou design de software) fornece a base necessária para que a aplicação cresça de forma organizada, facilitando a leitura, a evolução e a correção de bugs.

Este guia foi pensado para quem ainda está dando os primeiros passos na construção de aplicações mais complexas. Ao final da leitura, você será capaz de:

  • Identificar as principais camadas de um sistema típico.
  • Escolher e aplicar padrões de projeto simples.
  • Produzir diagramas básicos usando a notação UML.
  • Implementar um pequeno projeto CRUD (Create, Read, Update, Delete) em Python e JavaScript, seguindo as boas práticas apresentadas.
  • Vamos começar?


    1. Estrutura em Camadas: o alicerce do seu sistema

    A maioria dos projetos bem‑sucedidos adota um modelo em camadas, que separa responsabilidades e reduz o acoplamento entre partes diferentes da aplicação. A seguir, descrevemos as três camadas mais comuns:

    CamadaResponsabilidade principalTecnologias típicas
    ApresentaçãoInterage com o usuário (UI, API pública).HTML/CSS, React, Vue, Flask (templates)
    DomínioContém as regras de negócio, validações e orquestrações.Classes de serviço, objetos de valor
    PersistênciaGerencia o acesso a bancos de dados ou outros repositórios de dados.ORMs (SQLAlchemy, TypeORM), drivers de banco

    Por que separar?

    • Manutenibilidade: Alterações em uma camada raramente afetam as demais.
    • Testabilidade: Cada camada pode ser testada isoladamente com unit tests.
    • Escalabilidade: É mais fácil substituir, por exemplo, o banco de dados sem tocar na lógica de negócio.

    Diagrama de componentes simplificado

    graph TD
    

    UI[Camada de Apresentação] -->|chama| Service[Camada de Domínio] Service -->|acessa| Repo[Camada de Persistência] Repo --> DB[(Banco de Dados)]

    Observação: O diagrama acima usa a sintaxe Mermaid, suportada por diversas ferramentas de documentação (GitBook, MkDocs, etc.). Ele ilustra o fluxo de chamadas entre as camadas.


    2. Padrões de Projeto Essenciais

    Padrões de projeto são soluções reutilizáveis para problemas recorrentes. Para iniciantes, três padrões são particularmente úteis:

    2.1. Factory (Fábrica)

    Problema: Você precisa criar objetos de diferentes tipos sem expor a lógica de construção ao cliente.

    Solução: Centralizar a criação em uma classe Factory.

    # factory.py
    

    from abc import ABC, abstractmethod

    class Notificacao(ABC): @abstractmethod def enviar(self, mensagem: str) -> None: pass

    class Email(Notificacao): def enviar(self, mensagem: str) -> None: print(f"Enviando e‑mail: {mensagem}")

    class SMS(Notificacao): def enviar(self, mensagem: str) -> None: print(f"Enviando SMS: {mensagem}")

    class NotificacaoFactory: @staticmethod def criar(tipo: str) -> Notificacao: if tipo == "email": return Email() elif tipo == "sms": return SMS() else: raise ValueError(f"Tipo desconhecido: {tipo}")

    Uso

    if __name__ == "__main__": canal = NotificacaoFactory.criar("email") canal.enviar("Bem‑vindo ao nosso serviço!")

    2.2. Repository (Repositório)

    Problema: O código de acesso a dados se espalha por toda a aplicação, dificultando a manutenção.

    Solução: Encapsular todas as operações de leitura/escrita em um repositório.

    // repository.js (Node.js)
    

    class UsuarioRepository { constructor(db) { this.db = db; // objeto de conexão (ex.: pg, mysql) }

    async buscarPorId(id) { const result = await this.db.query('SELECT FROM usuarios WHERE id = $1', [id]); return result.rows[0]; }

    async criar(usuario) { const { nome, email } = usuario; const result = await this.db.query( 'INSERT INTO usuarios (nome, email) VALUES ($1, $2) RETURNING ', [nome, email] ); return result.rows[0]; }

    // Outros métodos: atualizar, remover... }

    module.exports = UsuarioRepository;

    2.3. Singleton (Único)

    Problema: Você precisa de uma única instância de um recurso (ex.: logger, pool de conexão).

    Solução: Garantir que a classe possua apenas uma instância global.

    # logger.py
    

    class Logger: _instancia = None

    def __new__(cls): if cls._instancia is None: cls._instancia = super(Logger, cls).__new__(cls) cls._instancia._configurar() return cls._instancia

    def _configurar(self): self.prefixo = "[MeuApp]"

    def info(self, mensagem): print(f"{self.prefixo} INFO: {mensagem}")

    Uso

    log = Logger() log.info("Aplicação iniciada") outro_log = Logger() print(log is outro_log) # True


    3. Diagramas UML Básicos

    UML (Unified Modeling Language) oferece notações padronizadas para representar visualmente a estrutura e o comportamento do sistema. Para iniciantes, três diagramas são suficientes:

    DiagramaO que representa
    Diagrama de ClassesEstrutura estática: classes, atributos, métodos e relacionamentos.
    Diagrama de SequênciaInteração temporal entre objetos (ex.: fluxo de um request).
    Diagrama de ComponentesOrganização de módulos, bibliotecas e dependências externas.

    3.1. Exemplo de Diagrama de Classes (texto)

    +-------------------+          +-------------------+
    

    | UsuarioService |<>------->| UsuarioRepository| +-------------------+ +-------------------+ | +criar(usuario) | | +criar(usuario) | | +obterPorId(id) | | +buscarPorId(id) | +-------------------+ +-------------------+

    +-------------------+ +-------------------+ | NotificacaoFactory|<>------>| Notificacao (interface) | +-------------------+ +-------------------+ | +criar(tipo) | | +enviar(mensagem) | +-------------------+ +-------------------+

    Dica: Ferramentas gratuitas como draw.io ou PlantUML permitem gerar esses diagramas a partir de texto.


    4. Exemplo Prático: Aplicação CRUD de Tarefas

    Vamos colocar tudo em prática construindo uma aplicação simples de gerenciamento de tarefas. O projeto será dividido nas três camadas descritas na seção 1 e usará os padrões Repository e Factory.

    4.1. Estrutura de pastas

    task-app/
    

    │ ├─ src/ │ ├─ presentation/ │ │ └─ api.py # camada de apresentação (Flask) │ ├─ domain/ │ │ ├─ services/ │ │ │ └─ task_service.py │ │ └─ models/ │ │ └─ task.py │ └─ persistence/ │ └─ task_repository.py │ ├─ requirements.txt └─ run.py

    4.2. Código da camada de domínio

    # src/domain/models/task.py
    

    from dataclasses import dataclass from datetime import datetime

    @dataclass class Task: id: int | None titulo: str concluida: bool = False criado_em: datetime = datetime.utcnow()

    ```python

    src/domain/services/task_service.py

    from ..models.task import Task from ...persistence.task_repository import TaskRepository

    class TaskService: def __init__(self, repo: TaskRepository): self.repo = repo

    def criar(self, titulo: str) -> Task: nova = Task(id=None, titulo=t

    Artigos relacionados