# Arquitetura ## Contexto O portal é uma aplicação independente e complementar ao Snipe-IT. Usa um monólito modular para reduzir custo operacional, mas separa os domínios internamente para evitar acoplamento entre inventário, reservas, aprovações e operações. ```text Browser │ HTTPS ▼ Nginx ──► Laravel + Inertia/React ├── PostgreSQL (estado do portal) ├── Redis (cache, filas, locks) ├── SMTP/Mailpit └── Snipe-IT REST API ``` ## Responsabilidade dos sistemas | Informação/operação | Sistema oficial | |---|---| | Etiqueta, série, modelo, categoria e localização habitual | Snipe-IT | | Estado operacional e atribuição física atual | Snipe-IT | | Checkout e check-in efetivos | Snipe-IT via API | | Pedidos futuros e calendário | Portal | | Aprovação, rejeição, cancelamento e conflitos | Portal | | Entrega/devolução e evidência operacional | Portal, sincronizada com Snipe-IT | | Auditoria do fluxo de requisição | Portal | ## Módulos - `app/Domain/Identity`: perfis, permissões e identidades externas. - `app/Domain/Inventory`: cache local de ativos e consultas do catálogo. - `app/Domain/Reservations`: disponibilidade, estados, regras e requisições. - `app/Domain/Approvals`: decisões e concorrência de aprovação (a criar). - `app/Domain/Handovers`: entrega, devolução e incidentes (a criar). - `app/Domain/Audit`: logs imutáveis pela interface. - `app/Integrations/SnipeIt`: único ponto de comunicação com Snipe-IT (a criar). ## Consistência temporal Os períodos usam `[starts_at, ends_at)`: uma reserva que termina às 11:00 não entra em conflito com outra que começa às 11:00. A defesa ocorre em duas camadas: 1. `AvailabilityService` produz erros de negócio legíveis; 2. PostgreSQL mantém a garantia final com uma restrição `EXCLUDE USING gist` nos estados bloqueantes. Uma criação ou aprovação deve bloquear as linhas dos ativos, voltar a validar dentro da transação e tolerar corrida através da restrição da base de dados. ## Fluxo de dados Snipe-IT 1. Um job consulta a API paginada com token de leitura. 2. Um mapper reduz a resposta aos campos documentados. 3. `AssetSyncService` faz upsert por `snipe_asset_id` e calcula `content_hash`. 4. Ativos ausentes numa sincronização completa ficam `active = false`; não são apagados para preservar histórico. 5. Antes de aprovação ou entrega, o ativo é validado novamente. 6. Checkout/check-in usam um token operacional separado e guardam apenas a resposta necessária para auditoria. ## Segurança - Sem registo público. - Cookies seguros e HTTPS em produção. - Policies em cada ação de domínio. - Tokens e segredos apenas no ambiente. - Dois tokens Snipe-IT com menor privilégio. - MFA obrigatório a definir para perfis privilegiados antes do piloto. - Logs de auditoria não expostos a operações de update/delete.