/projects/ulbra-atende

Todos os projetos

Ulbra Atende

Service desk de TI de uma universidade, no lugar do GLPI.

Design & implementação · Trabalho profissional

.NET 10PostgreSQL 17RabbitMQReact 19OpenIddictMCPOpenTelemetryDocker Swarm
Privado
Captura de tela de Ulbra Atende

Visão geral

O service desk de TI da ULBRA — um monólito modular em .NET 10 que substituiu o GLPI como canal único de entrada da TI da universidade, levando um pedido do chamado ao SLA à pesquisa de satisfação.

O que eu fiz

Autor principal, do zero — a arquitetura, o backend, o front-end e o deploy.

  • O monólito modular e as fronteiras entre seus contextos.
  • O motor de SLA, incluindo pausas que registram quem interrompeu a contagem e por quê.
  • O outbox transacional e o fan-out de notificação que ele alimenta.
  • O servidor de autorização OAuth e o servidor MCP atrás da sua tela de consentimento.
  • O front-end em React e o deploy em Docker Swarm.

O problema

A TI da ULBRA recebia demanda por GLPI, e-mail e mensagem direta ao mesmo tempo. Não havia SLA por time, nem rastro de aprovação, nem como saber se alguém ficou satisfeito com o resultado. O Ulbra Atende substitui o GLPI como canal único de entrada e torna cada uma dessas coisas mensurável — três meses depois, a mediana de fechamento é de cerca de uma hora e meia.

Em números

em ~3 meses de produção

~2,4 milchamados atendidos85% concluídos
200+usuáriosem ~30 times
~6 minmediana da 1ª respostaSLA medido por time
~5,0nota de satisfação400+ respostas, escala 1-5

Arquitetura

Um monólito modular em .NET 10: um único deploy, contextos delimitados separados — Core, Identity, Notifications e MCP — cada um em camadas Domain → Application → Infrastructure com seu próprio schema no Postgres. Eventos de integração passam pelo RabbitMQ através de um outbox transacional do EF. Anexos ficam em S3/MinIO, cache em Redis, tracing por OpenTelemetry; os testes de integração rodam contra Postgres, RabbitMQ e MinIO reais via Testcontainers.

  1. React 19 SPATanStack Router e Query sobre um design system em Tailwind.
  2. .NET 10 APIMonólito modular — quatro contextos delimitados num único deploy.
  3. PostgreSQL 17Um schema por módulo; migrations do EF Core aplicadas no startup.
  4. RabbitMQEventos de integração publicados por um outbox transacional do EF.
  5. Slack · Google Chat · e-mailFan-out de notificação consumindo esses eventos.

A vida de um chamado

O relógio do SLA é o fio que atravessa tudo. Ele começa pela política do time que recebe, para quando o chamado depende de alguém fora do time, e é contra ele que as metas de resposta e resolução são medidas. Um chamado também pode terminar cancelado, e trabalho que exige aval espera uma aprovação antes de começar.

  1. OpenO relógio começa contra a política de SLA do time que recebe, e a triagem faz o roteamento para um time e uma categoria.
  2. InProgressAlguém assume. A primeira resposta já foi medida a esta altura.
  3. PausedEsperando quem abriu ou um terceiro. O relógio para, e quem pausou e por quê fica registrado como uma entrada própria.
  4. CompletedO trabalho acabou e quem abriu é convidado a avaliar — que é de onde vem a nota de satisfação.

O que faz

  • SLA por time, com pausas que registram quem parou o relógio e por quê.
  • Templates de chamado multi-etapa, então um pedido recorrente já chega dividido em passos.
  • Fluxo de aprovação — trabalho que exige aval não começa sem ele.
  • Chamados pai/filho e dependências explícitas entre eles.
  • Notificações se espalham por Slack, Google Chat e e-mail, conforme a preferência de cada usuário.
  • Um dashboard cujos cards abrem exatamente a listagem que resumem.
  • Pesquisa de satisfação em todo chamado concluído.

Decisões de engenharia

  • Monólito modular, não microsserviços

    Um time, um deploy. A fronteira que importa é o módulo — garantida por referências de projeto e um schema por contexto — não a rede. Distribuir teria comprado uma independência de deploy que ninguém precisava, pagando em latência, falha parcial e dificuldade de depurar.

  • Outbox transacional para todo evento de integração

    A linha do evento é escrita na mesma transação da mudança de negócio. Uma notificação nunca dispara para um chamado que não commitou, e nunca some porque o broker estava fora naquele instante — o relay entrega assim que a transação fecha.

  • IDs fortemente tipados por source generator

    Cada entidade tem seu próprio struct de ID, escrito como ti_…, tm_…, us_…. Passar um ID de time onde se espera um de chamado para de compilar. Uma classe inteira de bug sai do runtime e vai para o build, e o ID diz o que é em log e em URL.

  • Servidor OAuth próprio, e um servidor MCP atrás dele

    O OpenIddict emite os tokens; o servidor MCP expõe ferramentas de leitura, escrita e consulta de chamados. A pessoa conecta o Claude ou o ChatGPT à própria conta por uma tela de consentimento e trabalha os chamados em linguagem natural — com exatamente as permissões que já tem na interface, e a mesma checagem de escopo em cada chamada de ferramenta.