-
Notifications
You must be signed in to change notification settings - Fork 0
Rota Facil
O Rota Fácil é um sistema de gestão de rotas voltado para o transporte escolar municipal, desenvolvido para apoiar prefeituras na organização e no acompanhamento das rotas realizadas pelos ônibus que transportam estudantes. Seu objetivo é centralizar as informações relacionadas ao transporte escolar e facilitar a comunicação entre os diferentes participantes desse processo, incluindo gestores municipais, motoristas e alunos. Ao estruturar e organizar os dados relacionados às rotas, o sistema contribui para tornar a logística do transporte escolar mais clara, previsível e acompanhada por todos os envolvidos. O Rota Fácil atua como um sistema central de gerenciamento das rotas escolares. Ele organiza as informações relacionadas ao transporte estudantil e permite acompanhar o andamento das rotas realizadas pelos ônibus. Por meio dessa organização, o sistema possibilita que os motoristas tenham acesso às informações necessárias para realizar suas rotas de forma adequada, como as paradas previstas e os alunos associados a cada trajeto. Ao mesmo tempo, os estudantes podem acompanhar o andamento da rota responsável pelo seu transporte. Essa centralização das informações também permite que a equipe de gestão municipal tenha uma visão mais ampla do funcionamento do transporte escolar, podendo acompanhar o status das rotas, verificar se os trajetos foram iniciados e identificar possíveis atrasos ou irregularidades.
- Autenticação e controle de acesso por perfil (Prefeitura/ADMIN, Motorista/DRIVER, Aluno/STUDENT), com vínculo obrigatório de cada usuário a uma única prefeitura;
- Gerencia de (CRUD) de rotas, ônibus, motoristas e instituições — restrito ao perfil Prefeitura;
- Gestão de viagens: início, finalização e cancelamento (motorista, com justificativa obrigatória no cancelamento), inscrição e cancelamento de participação (aluno), telemetria em tempo real, controle de presença/falta de estudantes, feedback entre usuários e relatórios em PDF;
- Check-in de estudantes exclusivamente via QR Code e válido apenas para a viagem ativa;
- Notificações de eventos importantes, com destaque para o cancelamento de viagens, que notifica todos os usuários vinculados;
- Auditoria automática de toda alteração relevante de estado (criação/atualização/exclusão de usuários, ônibus, viagens etc.), com registros imutáveis;
- Perfil de usuário com visualização de dados como e-mail e foto, para usuários autorizados;
- Análise inteligente: geração de descrição/interpretação de rotas e mapas de calor de pontos de embarque via IA (OpenAI).
O sistema é concebido desde sua base como uma aplicação distribuída de microsserviços (pré-requisito do Projeto Integrador de Sistemas Distribuídos), com comunicação assíncrona via RabbitMQ, múltiplas linguagens (Java/Spring Boot para os serviços de domínio, Python/FastAPI para o serviço de inteligência) e execução containerizada via Docker/Docker Compose.
- Permitir diferentes níveis de acesso para usuário (Prefeitura, Motorista, Aluno possuem permissões distintas)
- Permitir o cadastro de usuários, informando dados pessoais e selecionando uma prefeitura.
- Permitir edição e exclusão de usuários, respeitando os níveis de acesso definidos
- Vincular usuários a uma prefeitura (vínculo obrigatório para acesso ao sistema)
- O sistema deve permitir o cadastro, consulta, atualização e remoção de prefeituras.
- Permitir que alunos visualizem viagens disponíveis da sua prefeitura, com horários, rotas e paradas
- Permitir que alunos se inscrevam em viagens
- Permitir que alunos cancelem sua participação em viagens
- Permitir check-in de alunos via QR Code
- CRUD de viagens (rota, motorista, horários) — motorista e aluno não têm acesso
- Permitir que motoristas visualizem as viagens do dia atribuídas a eles
- Permitir que motoristas iniciem viagens, registrando horário de início e gerando QR Code
- Enviar notificações sobre eventos importantes, incluindo cancelamento de viagens (todos os alunos vinculados notificados)
- CRUD de rotas
- CRUD de ônibus
- CRUD de instituições
- Registrar automaticamente logs de todas as alterações relevantes (criação/atualização/deleção); nenhum nível de usuário pode alterar ou remover esses registros
- Exibir perfil de usuário com dados importantes (e-mail, foto) — motorista e aluno sem acesso a perfis de terceiros
- Sistema acessível via navegador web (desktop e mobile)
- Disponibilidade mínima de 95% em horário de uso (inatividade não deve ultrapassar 5%)
- Armazenamento em banco relacional, com suporte a PostgreSQL
- Seguir boas práticas de usabilidade — interface clara e intuitiva, com cores e ícones consistentes
- Autenticação obrigatória via login e senha
- Restringir acesso por papéis (admin, motorista, aluno), com diferentes níveis de confiabilidade e autorização
- Registrar todas as ações sensíveis dos usuários em tabela de auditoria centralizada, melhorando a rastreabilidade
- Comunicação assíncrona entre serviços via mensageria (RabbitMQ)
- Execução em ambiente containerizado (Docker)
O Rota-Fácil foi desenvolvido seguindo uma arquitetura de microsserviços, na qual cada domínio do sistema (identidade, transporte, lugares, arquivos, auditoria, notificações e inteligência artificial) é isolado em um serviço independente, com banco de dados próprio, ciclo de deploy próprio e responsabilidade única e bem definida. A comunicação entre esses serviços é híbrida: síncrona via HTTP/REST para operações que exigem resposta imediata ao usuário, e assíncrona orientada a eventos via RabbitMQ para propagação de estado entre domínios e para processos que não bloqueiam a experiência do usuário final (auditoria, notificações, sincronização de réplicas de dados). Essa decisão foi guiada pela natureza do próprio domínio de negócio: o transporte escolar municipal envolve múltiplos atores com necessidades muito diferentes entre si (alunos, motoristas, prefeituras e administradores gerais), múltiplas integrações externas (Google OAuth2, OpenStreetMap, OpenAI, serviço de push notification) e requisitos de escalabilidade desiguais entre módulos — por exemplo, o processamento de telemetria de viagens (/transports/trips/process) é chamado com alta frequência pelos aplicativos dos motoristas, enquanto o cadastro de instituições ou prefeituras é uma operação esporádica. Uma arquitetura monolítica dificultaria escalar apenas os componentes que realmente precisam, além de acoplar times e ciclos de deploy que, na prática, evoluem em ritmos diferentes.
-
a) Separação de responsabilidades por domínio: Cada serviço tem uma única razão para mudar: o auth-service muda quando as regras de identidade mudam; o transport-service muda quando as regras operacionais de rotas e viagens mudam. Isso reduz o acoplamento entre times e minimiza o "efeito cascata" de bugs — uma falha no notification-service, por exemplo, não derruba o cadastro de motoristas ou o processamento de uma viagem em andamento.
-
b) Escalabilidade independente: Serviços com padrões de carga muito diferentes (como o transport-service, que recebe atualizações de geolocalização em tempo real, versus o audit-service, que processa eventos em segundo plano) podem ser escalados horizontalmente de forma independente, sem desperdiçar recursos replicando o sistema inteiro.
-
c) Resiliência e tolerância a falhas: Como a comunicação entre domínios ocorre majoritariamente por eventos assíncronos (RabbitMQ), a indisponibilidade temporária de um serviço não bloqueia diretamente o funcionamento dos demais. Por exemplo, se o notification-service estiver fora do ar, o cancelamento de uma viagem ainda é processado normalmente pelo transport-service; o e-mail de aviso é apenas entregue quando o consumidor voltar a processar a fila.
-
d) Evolução tecnológica independente por serviço: A arquitetura permite que cada serviço utilize a stack mais adequada ao seu problema — os serviços de domínio de negócio (auth, transport, places, file, audit, notification, gateway, eureka) são construídos em Java/Spring Boot, enquanto o intelligence-service, por lidar com processamento de dados, geração de gráficos e integração com modelos de IA, foi implementado em Python com FastAPI, aproveitando o ecossistema de bibliotecas de ciência de dados (Pandas, Plotly) sem forçar essa dependência sobre o restante do sistema.
-
e) Desacoplamento via eventos: Serviços como transport-service, file-service e audit-service não criam usuários, instituições ou pontos de embarque diretamente — eles mantêm cópias locais desses dados a partir de eventos publicados pelo auth-service e pelo places-service (fontes únicas de verdade). Isso evita chamadas síncronas em cadeia entre serviços para operações de leitura frequentes, reduzindo latência e pontos únicos de falha.
Esta rotina executa periodicamente VACUUM (ANALYZE) nos bancos PostgreSQL do Rota Facil. Ela complementa o autovacuum nativo do PostgreSQL; não o substitui e não deve ser usada como motivo para desativá-lo. O processo não utiliza VACUUM FULL. O FULL reescreve tabelas e exige bloqueios mais agressivos, por isso não faz parte da manutenção automática.
O script percorre os containers definidos no compose geral:
- auth-database
- places-database
- transport-database
- audit-database
- files-database
- notification-database
Para cada container, o script usa POSTGRES_USER e POSTGRES_DB já presentes no ambiente do próprio container. Senhas e conteúdo do .env não são copiados para o script ou para as unidades do systemd.
- scripts/maintenance/rota-facil-vacuum.sh: executa e monitora a manutenção de cada banco.
- deploy/systemd/rota-facil-vacuum.service: serviço oneshot responsável pela execução.
- deploy/systemd/rota-facil-vacuum.timer: agenda a execução mensal.
O timer inicia o serviço no primeiro domingo de cada mês, às 03:30 no fuso America/Fortaleza, com atraso aleatório de até 30 minutos. O fuso é explícito porque o host de produção pode operar em UTC. A opção Persistent=true faz o systemd recuperar uma execução perdida depois que a máquina voltar a funcionar.
O serviço reduz a prioridade de CPU e I/O e permite no máximo seis horas por execução. Os logs são enviados ao journal do systemd.
O script:
-
Confirma que docker e flock estão instalados.
-
Obtém um lock em /var/lock/rota-facil-vacuum.lock, impedindo execuções simultâneas.
-
Confirma que cada container existe e está em execução.
-
Executa vacuumdb --analyze --jobs=2 --verbose no banco configurado no container.
-
Continua nos demais bancos caso um deles falhe.
-
Retorna erro ao systemd se pelo menos um banco não for processado.
O paralelismo pode ser alterado por meio de ROTA_FACIL_VACUUM_JOBS. O valor padrão é 2 para evitar carga excessiva em produção.
Execute a partir da raiz do projeto, com um usuário que possua sudo:
sudo install -o root -g root -m 0750 \
scripts/maintenance/rota-facil-vacuum.sh \
/usr/local/sbin/rota-facil-vacuum.shsudo install -o root -g root -m 0644 \
deploy/systemd/rota-facil-vacuum.service \
/etc/systemd/system/rota-facil-vacuum.service
sudo install -o root -g root -m 0644 \
deploy/systemd/rota-facil-vacuum.timer \
/etc/systemd/system/rota-facil-vacuum.timer
sudo systemctl daemon-reload
sudo systemctl enable --now rota-facil-vacuum.timer
O serviço roda como root por padrão para acessar o socket do Docker e criar o arquivo de lock. Não adicione senhas aos arquivos do systemd.
Confira quando ocorrerá a próxima execução:
systemctl list-timers rota-facil-vacuum.timer
systemctl status rota-facil-vacuum.timerAntes de depender do agendamento, faça uma execução manual em horário de baixa utilização:
sudo systemctl start rota-facil-vacuum.service
sudo journalctl -u rota-facil-vacuum.service -fAo terminar, confirme o resultado:
sudo systemctl status rota-facil-vacuum.service
sudo journalctl -u rota-facil-vacuum.service --since todayO resultado esperado é status=0/SUCCESS. Se algum container estiver ausente, parado ou não tiver as variáveis necessárias, o serviço terminará como falha e indicará o container afetado no journal.
Para executar com apenas uma conexão por banco, adicione ao bloco [Service] da unidade instalada:
Environment=ROTA_FACIL_VACUUM_JOBS=1
Depois de qualquer alteração em uma unidade:
sudo systemctl daemon-reload
sudo systemctl restart rota-facil-vacuum.timerPara mudar a periodicidade, altere OnCalendar em rota-facil-vacuum.timer. A expressão atual representa o primeiro domingo de cada mês. A expressão pode ser validada antes da implantação:
systemd-analyze calendar 'Sun *-*-01..07 03:30:00 America/Fortaleza'Para desativar a rotina sem apagar os arquivos:
sudo systemctl disable --now rota-facil-vacuum.timer- Faça a primeira execução em uma janela de baixa demanda e acompanhe CPU, I/O, espaço em disco e duração.
- O VACUUM comum permite operações concorrentes, mas ainda consome recursos do banco e do host.
- Se o nome de um container mudar no compose de produção, atualize a lista DATABASE_CONTAINERS no script e reinstale-o.
- Se a aplicação deixar de usar containers para PostgreSQL, este script deverá ser adaptado para conectar diretamente aos servidores de banco.
- Necessidade de VACUUM FULL, REINDEX ou ajustes de autovacuum deve ser avaliada separadamente, com métricas e janela de manutenção.
Mapeamento do contrato assíncrono observado em 2026-07-13. Este documento registra exchanges, routing keys, produtores, consumidores e efeitos. O location-service não faz parte do escopo desta atualização.
| Exchange | Dono | Conteúdo |
|---|---|---|
| auth.events | auth-service | Usuários, autenticação e prefeituras. |
| places.events | places-service | Instituições e pontos de embarque. |
| transport.events | transport-service | Rotas, ônibus, viagens, feedback e contadores. |
| file.events | file-service | Ciclo de vida de arquivos. |
Os eventos cumprem dois papéis: integração operacional e auditoria. Muitos payloads carregam simultaneamente dados do domínio e campos userId, prefectureId, userEmail/email, role, actor*, actionTitle, actionType, resourceName e resourceId.
O audit-service consome um DTO genérico, AuditEventReceive.
- Se actorUserId, actorEmail e actorRole estiverem preenchidos, eles identificam o autor real da ação.
- Caso contrário, o mapper usa userId, userEmail/email e role.
- prefectureId determina o isolamento na consulta HTTP.
- actionTitle, actionType, resourceName e resourceId descrevem a ação e o recurso.
A API de auditoria nunca aceita a prefeitura como filtro externo; usa o x-prefecture-id autenticado.
| Routing key | Quando | Consumidores | Observações |
|---|---|---|---|
| user.created | Cadastro público, motorista por admin e conclusão Google. | Transport, audit, notification. | Cadastro por admin inclui actor*; cadastro público usa o usuário criado como ator. |
| user.updated | Atualização própria e troca de prefeitura. | Transport, audit. | Sincroniza a cópia local. |
| driver.admin.updated | Admin edita motorista. | Transport, audit. | Carrega motorista como recurso e admin como ator. |
| user.deleted | Usuário remove a própria conta. | Transport, file, audit, notification, gateway. | Limpeza, e-mail e invalidação do token. |
| user.email.changed | Alteração de e-mail. | Gateway, audit. | Invalida o token anterior. |
| user.deactivate | Desativação própria ou de motorista por admin. | Transport, audit. | A variante administrativa inclui actor*. |
| user.logout | Logout. | Gateway, audit. | Invalida o token e registra auditoria. |
| prefecture.created | Criação de prefeitura. | Audit. | Registra criação. |
| prefecture.updated | Atualização de prefeitura. | Audit. | Registra atualização. |
| prefecture.deleted | Exclusão de prefeitura. | File, audit. | Remove arquivos e registra auditoria. |
O fluxo atual de registerUserPrefecture cria usuário administrativo, mas não publica user.created.
| Routing key | Fila padrão | Efeito |
|---|---|---|
| user.feedback | auth.user.updated.queue | Atualiza score do avaliado. |
| trip.completed | auth.user.complete.trip.queue | Incrementa completedTrips. |
| user.trips.increased | auth.user.trips.increased.queue | Incrementa trips. |
| user.trips.decreased | auth.user.trips.decreased.queue | Decrementa trips. |
| Routing key | Quando | Consumidores |
|---|---|---|
| institution.created | Criação de instituição. | Transport, audit. |
| institution.updated | Atualização de instituição. | Transport, audit. |
| institution.deleted | Exclusão de instituição. | Transport, file, audit. |
| boarding.created | Criação de ponto de embarque. | Transport, audit. |
| boarding.updated | Atualização de ponto de embarque. | Transport, audit. |
| boarding.deleted | Exclusão de ponto de embarque. | Transport, file, audit. |
Não há consumidores RabbitMQ no places-service.
| Routing key | Fila padrão | Efeito |
|---|---|---|
| user.created | transport.user.created.queue | Cria cópia local. |
| user.updated | transport.user.updated.queue | Atualiza cópia local. |
| driver.admin.updated | transport.user.updated.queue | Atualiza motorista na mesma fila. |
| user.deleted | transport.user.deleted.queue | Remove ou ajusta cópia local. |
| user.deactivate | transport.user.deactivate.queue | Desativa cópia local. |
| Routing key | Efeito |
|---|---|
| institution.created/updated/deleted | Mantém a cópia operacional da instituição. |
| boarding.created/updated/deleted | Mantém a cópia operacional do ponto. |
As filas padrão são específicas por operação, sob os prefixos transport.institution..queue e transport.board.point..queue.
| Routing key | Quando | Consumidores | Observações |
|---|---|---|---|
| trip.created | Criação/agendamento de viagem. | Nenhum consumidor observado. | Payload operacional; não está ligado à auditoria. |
| trip.running | Motorista inicia a ida. | Audit, notification. | Registra auditoria e notifica passageiros. O início explícito da volta não republica este evento. |
| trip.cancelled | Motorista cancela viagem. | Audit, notification. | Inclui motivo, passageiros e dados de auditoria. |
| trip.deleted | Exclusão de viagem. | Audit. | Inclui dados de auditoria. |
| trip.completed | Finalização operacional. | Auth. | Atualiza viagens concluídas dos usuários. |
| user.trips.increased | Entrada de aluno ou início do motorista. | Auth. | Incrementa contador. |
| user.trips.decreased | Aluno sai antes do início. | Auth. | Após o início, a saída marca falta e não publica decremento. |
| user.feedback | Avaliação de usuário. | Auth, notification, audit. | Atualiza score, envia e-mail e registra auditoria. |
| route.created | Criação de rota. | Audit. | Dados de auditoria. |
| route.updated | Atualização de rota. | Audit. | Dados de auditoria. |
| route.deleted | Exclusão/soft delete de rota. | Audit. | Dados de auditoria. |
| bus.created | Cadastro de ônibus. | Audit. | Admin como ator. |
| bus.updated | Atualização de ônibus. | Audit. | Admin como ator. |
| bus.deleted | Soft delete de ônibus. | File, audit. | Remove arquivos e registra auditoria. |
| Routing key | Quando | Consumidor |
|---|---|---|
| file.created | Upload/criação. | Audit. |
| file.updated | Substituição. | Audit. |
| file.deleted | Remoção. | Audit. |
| Exchange | Routing key | Fila padrão | Efeito |
|---|---|---|---|
| auth.events | user.deleted | file.user.deleted.queue | Remove arquivos de estudante/motorista. |
| auth.events | prefecture.deleted | file.prefecture.deleted.queue | Remove arquivos da prefeitura. |
| places.events | institution.deleted | file.institution.deleted.queue | Remove arquivos da instituição. |
| places.events | boarding.deleted | file.boarding.deleted.queue | Remove arquivos do ponto. |
| transport.events | bus.deleted | file.bus.deleted.queue | Remove arquivos do ônibus. |
O audit usa uma fila por exchange:
- audit.auth.queue: todos os eventos de auth listados acima.
- audit.places.queue: CRUD de instituição e ponto.
- audit.transport.queue: CRUD de rota e ônibus, trip.running, trip.cancelled, trip.deleted e user.feedback.
- audit.file.queue: CRUD de arquivo.
O serviço não publica eventos.
| Exchange | Routing key | Fila padrão | Efeito |
|---|---|---|---|
| auth.events | user.created | notification.user.created.queue | E-mail de boas-vindas. |
| auth.events | user.deleted | notification.user.deleted.queue | E-mail de conta removida. |
| transport.events | trip.running | notification.trip.running.queue | Persiste notificação de início. |
| transport.events | trip.cancelled | notification.trip.cancelled.queue | E-mail e notificação persistida. |
| transport.events | user.feedback | notification.user.feedback.queue | E-mail de feedback. |
Os handlers de viagem capturam exceções e registram o payload contextual no log. O listener está configurado com defaultRequeueRejected=false; mensagens com falha são descartadas para evitar loop de reprocessamento. Não há DLQ configurada no código atual.
O serviço não publica eventos.
Consome user.deleted, user.email.changed e user.logout de auth.events. Os três bindings compartilham gateway.invalid.user.token.queue e adicionam o token ao Redis.
O gateway não publica eventos.
| Serviço | Publica | Consome |
|---|---|---|
| Auth | Usuários e prefeituras. | Score e contadores do transporte. |
| Places | Instituições e pontos. | Nenhum. |
| Transport | Rotas, ônibus, viagens, feedback e contadores. | Usuários e lugares. |
| File | Ciclo de arquivos. | Exclusões de auth, places e transport. |
| Audit | Nenhum. | Auth, places, transport e file. |
| Notification | Nenhum. | Auth e transport. |
| Gateway | Nenhum. | Invalidação de tokens de auth. |