Skip to content

Monitoramento Inteligente de Chuvas no Rio de Janeiro

Notifications You must be signed in to change notification settings

LeonardoJaques/HidroGeo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🌊 HidroGeo

Monitoramento Inteligente de Chuvas no Rio de Janeiro

Aplicação Spring Boot que coleta, armazena e visualiza dados pluviométricos em tempo real das estações do Alerta Rio, cobrindo 33 estações espalhadas pelo município do Rio de Janeiro.


📋 Índice


🛠 Stack Tecnológica

Camada Tecnologia
Linguagem Java 25
Framework Spring Boot 4.0.3-SNAPSHOT
Web Spring WebMVC + WebFlux
Persistência Spring Data JPA + Hibernate Spatial
Banco PostgreSQL 15 (via Docker)
Resiliência Resilience4j (Circuit Breaker)
Scraping Jsoup
Template Thymeleaf + Chart.js
Infra Docker Compose
Build Maven

🏗 Arquitetura

O projeto segue a Arquitetura Hexagonal (Ports & Adapters), garantindo separação clara entre domínio, lógica de aplicação e infraestrutura.

com.hidrogeo/
├── domain/                          # Núcleo do domínio (Records + Enum)
│   ├── Rainfall.java                # Record com dados pluviométricos
│   ├── Incident.java                # Record de incidentes urbanos
│   ├── Station.java                 # Enum com 33 estações do Rio
│   └── NeighborhoodRainfallStat.java # Record de estatísticas por bairro
│
├── application/                     # Casos de uso e portas
│   ├── ports/
│   │   ├── in/                      # Portas de entrada
│   │   │   ├── FetchExternalDataUseCase.java
│   │   │   └── GetWaterShortageStatsUseCase.java
│   │   └── out/                     # Portas de saída
│   │       ├── RainfallPort.java
│   │       ├── SaveRainfallPort.java
│   │       ├── LoadRainfallPort.java
│   │       └── IncidentPort.java
│   ├── DataCollectionService.java   # Orquestra coleta com Virtual Threads
│   └── DashboardService.java        # Lógica de consulta e estatísticas
│
├── infrastructure/                  # Adaptadores concretos
│   ├── adapters/
│   │   ├── in/
│   │   │   ├── web/DashboardController.java      # Controller Thymeleaf + REST
│   │   │   └── scheduler/DataCollectionScheduler.java  # Agendamento @Scheduled
│   │   └── out/
│   │       ├── external/
│   │       │   ├── AlertaRioAdapter.java         # Scraping real do Alerta Rio
│   │       │   └── CorRioMockAdapter.java        # Mock de incidentes
│   │       └── persistence/
│   │           ├── RainfallEntity.java           # Entidade JPA
│   │           ├── RainfallRepository.java       # Spring Data Repository
│   │           └── RainfallPersistenceAdapter.java # Adapter de persistência
│   └── config/
│       └── RainfallDataSeeder.java              # Seed de 2 anos de dados
│
└── config/
    ├── AppConfig.java               # Bean RestTemplate
    └── ResilienceConfig.java        # Configuração Circuit Breaker

🎨 Design Patterns

1. Ports & Adapters (Hexagonal Architecture)

O domínio define interfaces (portas) e a infraestrutura fornece as implementações (adaptadores), desacoplando completamente o core da aplicação das tecnologias externas.

  • Portas de Entrada: FetchExternalDataUseCase, GetWaterShortageStatsUseCase
  • Portas de Saída: RainfallPort, SaveRainfallPort, LoadRainfallPort, IncidentPort
  • Adaptadores: AlertaRioAdapter, RainfallPersistenceAdapter, CorRioMockAdapter

2. Circuit Breaker (Resilience4j)

O AlertaRioAdapter usa @CircuitBreaker para proteger chamadas ao Alerta Rio. Quando o site está indisponível, o fallback gera dados mock automaticamente, garantindo que o dashboard sempre funcione.

3. Strategy Pattern

As portas de saída (RainfallPort, IncidentPort) funcionam como contratos de estratégia - os adaptadores concretos podem ser trocados sem alterar a lógica de aplicação.

4. Adapter Pattern

Cada adaptador converte dados entre formatos externos e os records do domínio (ex: RainfallEntityRainfall no RainfallPersistenceAdapter).

5. Scheduler / Observer

O DataCollectionScheduler utiliza @Scheduled do Spring para disparar a coleta de dados a cada 15 segundos, desacoplado da lógica de negócio via FetchExternalDataUseCase.

6. Seeder Pattern

O RainfallDataSeeder implementa CommandLineRunner para popular o banco com 2 anos de dados históricos simulados ao iniciar, com variâncias sazonais e geográficas realistas.


⚡ Virtual Threads

O projeto utiliza Virtual Threads (Project Loom) em dois níveis:

Nível de Configuração

spring:
  threads:
    virtual:
      enabled: true   # Todas as requisições HTTP usam virtual threads

Nível de Aplicação

Em DataCollectionService.execute(), a coleta de dados das 33 estações é feita concorrentemente via Executors.newVirtualThreadPerTaskExecutor():

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (Station station : Station.values()) {
        executor.submit(() -> {
            rainfallPort.fetchRainfall(stationId).ifPresentOrElse(
                r -> saveRainfallPort.save(r),
                () -> logger.warn("Failed to fetch...")
            );
        });
    }
} // Aguarda todas as threads completarem

Isso permite que todas as 33 requisições de scraping rodem em paralelo sem consumir threads do sistema operacional.


📊 Diagramas de Sequência

Fluxo de Coleta de Dados (Scheduled)

sequenceDiagram
    participant SCH as DataCollectionScheduler
    participant DCS as DataCollectionService
    participant VT as Virtual Thread Pool
    participant ARA as AlertaRioAdapter
    participant CB as Circuit Breaker
    participant WEB as Alerta Rio Website
    participant RPA as RainfallPersistenceAdapter
    participant DB as PostgreSQL

    SCH->>DCS: execute() [@Scheduled 15s]
    
    loop Para cada estação (33x em paralelo)
        DCS->>VT: submit(task)
        VT->>ARA: fetchRainfall(stationId)
        ARA->>CB: @CircuitBreaker check
        
        alt Circuit Breaker CLOSED
            CB->>WEB: HTTP GET (Jsoup scraping)
            WEB-->>CB: HTML Response
            CB-->>ARA: Parse tabela → Rainfall
        else Circuit Breaker OPEN
            CB->>ARA: fetchRainfallFallback()
            ARA-->>ARA: Gerar dados mock
        end
        
        ARA-->>DCS: Optional<Rainfall>
        DCS->>RPA: save(rainfall)
        RPA->>DB: INSERT rainfall
    end

    DCS->>DCS: fetchIncidents() [mock]
Loading

Fluxo de Visualização do Dashboard

sequenceDiagram
    participant USR as Navegador
    participant DC as DashboardController
    participant TH as Thymeleaf
    participant DS as DashboardService
    participant RPA as RainfallPersistenceAdapter
    participant DB as PostgreSQL

    USR->>DC: GET /
    DC->>TH: Renderizar index.html
    TH-->>USR: HTML + Chart.js

    USR->>DC: GET /api/stats (AJAX)
    DC->>DS: getRainfallStatsSortedByShortage()
    DS->>RPA: loadRecentRainfall()
    RPA->>DB: SELECT DISTINCT ON (station_id) ...
    DB-->>RPA: List<RainfallEntity>
    RPA-->>DS: List<Rainfall>
    DS-->>DC: Lista ordenada por volume 24h
    DC-->>USR: JSON Response

    USR->>DC: GET /api/history/neighborhood (AJAX)
    DC->>DS: getHistoricalAverageByNeighborhood(2)
    DS->>RPA: loadHistoricalRainfall(2)
    RPA->>DB: SELECT ... WHERE timestamp BETWEEN ...
    DB-->>RPA: List<RainfallEntity>
    RPA-->>DS: List<NeighborhoodRainfallStat>
    DC-->>USR: JSON Response
Loading

Fluxo de Inicialização da Aplicação

sequenceDiagram
    participant APP as Spring Boot
    participant SEED as RainfallDataSeeder
    participant REPO as RainfallRepository
    participant DB as PostgreSQL

    APP->>APP: Inicialização
    APP->>SEED: CommandLineRunner.run()
    SEED->>REPO: deleteAll()
    REPO->>DB: TRUNCATE rainfall
    
    loop Para cada estação (33 estações × ~730 dias)
        SEED->>SEED: Calcular multiplicador regional
        SEED->>SEED: Gerar dados com sazonalidade
    end
    
    SEED->>REPO: saveAll(entities)
    REPO->>DB: BATCH INSERT (~7000+ registros)
    SEED-->>APP: Seeding concluído
Loading

🔌 Endpoints da API

Método Rota Descrição
GET / Dashboard HTML com gráficos Chart.js
GET /api/stats Dados de chuva em tempo real (JSON)
GET /api/history Dados históricos de chuva (JSON)
GET /api/history/neighborhood Média de chuva por bairro - 2 anos (JSON)

🚀 Como Executar

Pré-requisitos

  • Java 25+
  • Maven 3.9+
  • Docker e Docker Compose

1. Subir o banco de dados

docker compose up -d

2. Executar a aplicação

./mvnw spring-boot:run

3. Acessar o dashboard

Abra o navegador em http://localhost:8080


🐳 Docker

O docker-compose.yml provisiona um PostgreSQL 15 Alpine com as configurações alinhadas ao application.yaml:

Configuração docker-compose application.yaml
Database POSTGRES_DB: hidrogeo url: ...localhost:5432/hidrogeo
User POSTGRES_USER: postgres username: postgres
Password POSTGRES_PASSWORD: password password: password
Port 5432:5432 localhost:5432

Volume persistente postgres_data garante que os dados sobrevivem a reinicializações do container.


🧪 Testes

O projeto inclui um teste de contexto (HidroGeoApplicationTests) que valida que o contexto Spring carrega sem erros. Os starters de teste incluídos são:

  • spring-boot-starter-actuator-test
  • spring-boot-starter-data-jpa-test
  • spring-boot-starter-webflux-test
  • spring-boot-starter-webmvc-test

📝 Licença

Projeto acadêmico / laboratório de estudo.

About

Monitoramento Inteligente de Chuvas no Rio de Janeiro

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published