Voltar aos projetos
Logotipo de Portman

Portman

Extensão do VSCode para gerenciar portas em qualquer sistema operacional a partir de uma interface gráfica integrada.

+2k downloadsVSCodeMarketplace

Durante o desenvolvimento de software, um obstáculo operacional recorrente é a colisão na alocação de portas de rede (conhecida em ambientes Node.js como o erro EADDRINUSE). Isso ocorre quando um processo anterior, seja em execução intencional ou órfão (zumbi), mantém um socket em escuta aberto em uma interface de rede e porta específicas, impedindo que uma nova instância se vincule a esse recurso.

A resolução manual desse estado exige que o desenvolvedor interaja diretamente com o sistema operacional por meio de uma interface de linha de comando (CLI) para:

  1. Sondar: Executar utilitários de análise de rede (por exemplo, lsof, netstat, ss).
  2. Filtrar: Processar a saída padrão (stdout) para isolar o processo vinculado à porta em conflito.
  3. Identificar: Extrair o Identificador de Processo (PID).
  4. Encerrar: Enviar um sinal do sistema (como SIGKILL) ao PID identificado.

A Solução Operacional

O Portman automatiza e abstrai esse fluxo de diagnóstico e intervenção, integrando-o diretamente ao Ambiente de Desenvolvimento Integrado (IDE).

O fluxo de trabalho funcional se resume a:

  1. Extração do Estado do Sistema: A extensão interage de forma transparente com as APIs e os binários nativos do sistema operacional hospedeiro para obter o estado atual das conexões de rede.
  2. Transformação e Mapeamento: As informações brutas extraídas do SO são normalizadas em modelos de domínio estruturados.
  3. Apresentação em Tempo Real: Os modelos normalizados são renderizados em uma estrutura de árvore interativa dentro da interface gráfica do VS Code.
  4. Execução de Ações: Por meio de comandos integrados, a ferramenta permite ao usuário selecionar um nó da árvore e enviar o sinal de encerramento ao processo subjacente de forma unificada, independentemente do sistema operacional utilizado.

Análise Técnica: Arquitetura e Modelos

Para garantir que o ciclo de vida do software suporte um crescimento sustentável, testabilidade isolada e adaptabilidade transversal entre múltiplos sistemas operacionais (Linux, macOS, Windows), projetei a estrutura do projeto sob os princípios de Clean Architecture e Domain-Driven Design (DDD).

O código está dividido conceitualmente em quatro camadas concêntricas, nas quais a dependência flui estritamente em direção ao centro (o Domínio).

graph TD
    subgraph ext["Camada Externa (Infraestrutura e Apresentação)"]
        VS[VS Code Extension API]
        CLI[Binários do SO: netstat, lsof, ss]
    end

    subgraph pres["Camada de Apresentação"]
        PTD[ProcessTreeDataProvider]
        Cmds[KillProcessCommand]
    end

    subgraph app["Camada de Aplicação"]
        SP[UseCase: SearchProcesses]
        KP[UseCase: KillProcess]
    end

    subgraph dom["Camada de Domínio"]
        Model[Aggregate Root: Process]
        RepoInt[Interface: ProcessRepository]
    end

    VS --> PTD
    VS --> Cmds
    PTD --> SP
    Cmds --> KP
    SP --> RepoInt
    KP --> RepoInt
    CLI -.->|Implementação| RepoInt

    classDef domain fill:#14b8a6,fill-opacity:0.3,stroke:#0d9488,stroke-width:2px;
    classDef app fill:#14b8a6,fill-opacity:0.12,stroke:#0d9488,stroke-width:2px;
    classDef pres fill:#737373,fill-opacity:0.14,stroke:#737373,stroke-width:2px;
    classDef external fill:#737373,fill-opacity:0.06,stroke:#737373,stroke-width:1px,stroke-dasharray:4 4;

    class Model,RepoInt domain;
    class SP,KP app;
    class PTD,Cmds pres;
    class VS,CLI external;

1. O Núcleo do Domínio

O modelo de domínio está isolado de qualquer dependência externa, framework ou detalhe do sistema operacional. Seu objetivo principal é modelar a abstração de um processo de rede.

O Agregado Process

A entidade central é Process, que atua como Aggregate Root. É composta por múltiplos Value Objects que garantem a integridade dos dados no momento de sua instanciação:

  • ProcessId: Identificador único (PID).
  • ProcessProgram: Nome do executável ou daemon.
  • Protocol: Protocolo de transporte (TCP, UDP).
  • Address: Composição de AddressHost (IP local/remoto ou nome do host) e AddressPort (porta numérica).
  • ProcessStatus: Estado atual da conexão (por exemplo, LISTEN, ESTABLISHED).
classDiagram
    class Process {
        +ProcessId id
        +ProcessProgram program
        +Protocol protocol
        +Address local
        +Address remote
        +ProcessStatus status
        +String icon
        +String label
        +String description
        +String tooltip
    }

    class Address {
        +AddressHost host
        +AddressPort port
    }

    class ProcessRepository {
        <<Interface>>
        +search() Promise~Process[]~
        +kill(process: Process) Promise~void~
    }

    Process "1" *-- "2" Address : local/remote
    ProcessRepository --> Process : gerencia

Além disso, o domínio define o contrato ProcessRepository, que estabelece as operações fundamentais de busca e encerramento exigidas pelo sistema, sem ditar como elas são implementadas.

2. Camada de Aplicação

A camada de aplicação orquestra as regras de negócio usando os contratos do domínio. Está estruturada em torno de Casos de Uso específicos:

  • SearchProcesses: Emite uma Query ao repositório para obter o array de objetos Process.
  • KillProcess: Emite um Command ao repositório para encerrar um objeto Process específico.

Esses casos de uso não têm estado e dependem da injeção do repositório concreto em tempo de execução.

3. Camada de Infraestrutura

Aqui residem as implementações técnicas da interface ProcessRepository. A injeção de dependências é gerenciada no ponto de entrada da aplicação (extension.ts), avaliando a variável de ambiente process.platform para instanciar o adaptador correto.

  • LinuxProcessRepository: Prioriza a extração via netstat. Conta com um mecanismo de fallback gradual para o binário ss caso o primeiro não esteja disponível. Além disso, avalia a configuração portman.linux.asRootUser para elevar privilégios usando pkexec ou sudo, se necessário.
  • MacProcessRepository: Interage nativamente com lsof -i -P -n.
  • WindowsProcessRepository: Invoca implementações baseadas em PowerShell (Get-NetTCPConnection) ou comandos legados do MS-DOS (netstat -a -b -n -o).

Cada repositório delega a análise da saída padrão a classes transformadoras específicas (por exemplo, MacNetstatProcessTransformer), que analisam as strings de texto bruto, validam a estrutura e mapeiam os dados para o objeto de domínio Process.

4. Camada de Apresentação

Esta camada isola a lógica de negócio da extensa API do Visual Studio Code. O componente principal é o ProcessTreeDataProvider, que implementa a interface TreeDataProvider do VS Code. Sua responsabilidade é assinar os eventos da IDE, consumir o caso de uso SearchProcesses e transformar o array de entidades Process em instâncias de ProcessTreeItem (ou ProcessQuickPickItem) para a renderização final da árvore visual na Interface do Usuário. Também gerencia a captura e a apresentação de erros operacionais por meio das caixas de diálogo integradas do editor.


Stack Tecnológico

O conjunto de ferramentas escolhido para este projeto reflete a necessidade de construir um artefato empacotado otimizado, fortemente tipado e estreitamente acoplado a um ambiente de execução Node.js (Electron).

Tecnologia / FerramentaPapel ArquiteturalPropósito Funcional
TypeScript (v5.x)Linguagem PrincipalFornece análise estática e tipagem forte, essenciais para modelar as entidades da camada de Domínio e garantir os contratos entre camadas.
VS Code Extension APIAmbiente de ExecuçãoFornece a interface programática (vscode.*) para a renderização nativa da UI, o registro de comandos e o acesso ao painel lateral.
Webpack (v5.x)BundlerCombina, transpila e minifica o código-fonte em um arquivo extension.js otimizado, reduzindo a latência de ativação do plugin.

Conclusão

A adoção estrita da Clean Architecture em um projeto de extensão de IDE —um ambiente fundamentalmente acoplado à camada de visualização— resultou em uma separação rigorosa de responsabilidades. O núcleo lógico para resolver as dependências de rede é completamente agnóstico tanto ao sistema operacional subjacente quanto ao framework de apresentação do VS Code. Essa abstração garante que as integrações nativas possam ser modificadas, otimizadas ou substituídas (por exemplo, migrando de comandos CLI para invocações de kernel em C++) sem produzir efeitos colaterais nas regras de negócio ou na lógica de apresentação do fluxo de encerramento de portas.

Arraste para mover o diagrama. Use + e − para dar zoom, as setas para movê-lo e 0 para redefinir a visualização.

Vamos trabalhar juntos

Engenheiro de Software e desenvolvedor Senior Full-Stack, trabalho remotamente a partir de Santa Cruz, Bolívia. Aberto a novas oportunidades.