Toti es articles

Mès enlà deth programari: construint nautaran, ua plataforma GIS entar infrastructura municipau

Publicat Jul 17, 20265 min de lectura
  • Producte
  • Engenharia
  • SIG

Idèes clau

  • Nautaran ei eth prumèr produit B2G de Maladeta: ua plataforma GIS "multi-tenant" entà qu'es ajuntaments gerissen es rets municipaus d'aigua, sanejament, plujanères e enlumenat.
  • Era plataforma se construic "multi-tenant" dès eth prumèr dia — arren dèisha fixat eth prumèr "tenant", er Ajuntament de Naut Aran — damb moduls opcionaus activables per "tenant".
  • L'aïllament entre "tenants" s'aplique damb "row-level security" de PostgreSQL emplegant un patron fail-closed damb current_setting(..., true), non solament damb un "scope" a nivèu d'ORM.

Toti es produits qu'a lançat Maladeta Studio dinc ara an ua causa en comun: un usuari finau que decidic d'èster aquiu. Es òstes de Lueira resèrven ua classa d'esquí pr'amor que vòlen esquiar. Es caminaires de MeteoTrail consulten ua prevision pr'amor qu'estan planificant ua rota. Enquia era plataforma de marketplace, sigue quina sigue era forma que prenga entà un client dat, existís pr'amor que quauquarrés volie crompar o véner quauquarren. Nautaran trinca aguest patron. Arrés non s'i apuntèc — ac an eretat en forma d'un archivador.

E açò non ei ua metafòra. Enter eth 2017 e eth 2018, quauquarrés lheuèc es rets d'aigua, sanejament, plujanères e enlumenat public d'un municipi dera Val d'Aran: 9.902 elements geografics, caminadi e cartografiadi a man. E dempús se quedèc aquiu. Sense manteniment, sense actualizacions, alunhant-se a pòc a pòc de çò que vertadèrament i a jos tèrra. Aguest ei eth punt de partença de nautaran (nòm intern: Urban), ua naua linha de produit B2G entar estudi — programari construït entàs ajuntaments, non entàs sòns vesins.

Ei un tipe de projècte diferent de tot çò qu'auem construït abans, e vòli escríuer sus es decisions que hècon que se sentisse coma ua linha de produit reau en sòrta d'un trabalh GIS puntuau entà un client.

1. D'escòles d'esquí a ajuntaments: un tipe de client nau

Es instints de tecnologia civica der estudi non son naui — eth trabalh de NaturPark e es articles sus estacionament e residús d'un temps endarrèr ja apontauen en aguesta direccion. Nautaran ei era prumèra viatge que convertim aguest instint en un produit formau e vendible en sòrta d'un projècte puntuau.

Eth client ei un ajuntament, e er "usuari" laguens d'eth se divís en ath mens dues persones: quauquarrés d'oficina qu'a besonh de sàber a on ei ua valvula abans de signar un permís d'òbra, e ua brigada — er equip de manteniment — qu'a besonh dera madeisha informacion dreta en ua hòssa, damb gants banhadi e sense cobertura. Dissenhar entàs dus dès eth prumèr dia marquèc quasi cada decision tecnica que vengue dempús, començant peth hèt qu'açò non podie èster ua soleta aplicacion.

2. Ua plataforma basa mès moduls, non foncionalitats hicadas a un solet "tenant"

Naut Aran ei eth nòste prumèr "tenant", mès arren en còdi assumís que sigue er unic. Aquerò ei ua leçon qu'auem aprés ja per çò dolent damb eth madeish trabalh de "multi-tenancy" de Lueira — reconvertir en aïllat quauquarren escriut "tenant" per "tenant" hè mès mau que hè'c plan dès eth principi. Atau que nautaran ei "multi-tenant" dès era prumèra migracion: autentificacion, contròl d'accès basat en ròtles, registre d'auditoria, emmagazinatge d'archius, un modèl de dades de capa/tipe/element, servici de teselas de mapa, un "backoffice" de "tenant" e ua consòla intèrna de Maladeta s'envien toti coma ua soleta plataforma basa, venuda coma un blòc a cada ajuntament que s'i incorpòre.

Sus aguesta basa se sèden moduls, activables per "tenant". Aué existissen dus. serveis_publics ei eth "visor" — ua vista combinada de mapa e taula entà percórrer er inventari des rets d'aigua, sanejament, plujanères e enlumenat. app_camp ei ua Progressive Web App damb capacitat "offline" construïda entara brigada: a de foncionar quan er equip ei dret ath costat d'ua tapa de clavegam sense connectivitat, atau qu'eth modèl de dades e era logica de sincronizacion se dissenhèren dès eth principi ath entorn dera consisténcia eventuau.

Un modul se pòt desactivar entà un "tenant" sense tocar es sues dades. Desactivar app_camp entà un ajuntament que non ei prèst entad eth simplament hè que es sues rotas, teselas e entradas de navegacion dèishen de resòlver-se — arren se suprimís. Ei era madeisha idia de moduls activables de NaturPark, aplicada a quauquarren damb implicacions operatives reaus: un ajuntament pòt pilotar un modul e tornar-lo a desactivar se non li escad, sense arriscar jamès es dades deth lheuament.

3. Perqué "row-level security" en sòrta de solament un "scope" der ORM

Er aïllament "multi-tenant" de Lueira se recòlze en un "scope" per defècte a nivèu d'ORM damb ua restriccion de basa de dats coma supòrt — raonable entà ua plataforma a on era pejor huida possibla ei que quauquarrés vege es resèrvas de classes d'esquí d'ua auta escòla. Eth pejor cas de nautaran ei fòrça pejor: ua mapa filtrada dera ret d'aigua o sanejament deth municipi que non tòque ei informacion d'infrastructura vertadèrament sensibla, non ua vergonha. Aguesta diferéncia de nivèu de risc ei era rason per qué me recolgè en "row-level security" natiu de PostgreSQL coma mecanisme d'aplicacion prumèr, en sòrta de coma dusau linha de defensa.

Eth detalh deth quau sò mès erguelhós ei coma se liege eth contèxte deth "tenant". Es politicas de RLS trègen eth "tenant" actuau d'ua variabla de session per mejan de current_setting(..., true) — era fòrma damb dus arguments, que torne NULL en sòrta de lançar un error quan era variabla non ei definida:

ALTER TABLE network_elements ENABLE ROW LEVEL SECURITY;
ALTER TABLE network_elements FORCE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON network_elements
    USING (
        tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::uuid
    );

Se ua peticion arribe de quauqua manèra ena basa de dats sense auer hicat app.tenant_id — un "bug" en "middleware", un SET LOCAL desbrembat, un "job" en dessèga que sautèc era configuracion de contèxte — aguesta comparason s'evalue contra NULL, e tenant_id = NULL non ei jamès vertat entà cap fila. Era consulta torne zero filas. Non falhe damb un error, e non torne pas tanpòc accidentaument es dades de toti es "tenants", qu'ei eth mòde de falhida qu'aurés damb un "fallback" ingenú de "se non i a tenant, ensenha tot". Barre en faus per construccion, aplicat a nivèu de basa de dats sense importar per quau des tres "frontends" en Next.js o per quau rota de còdi deth "backend" age entrat era peticion.

Eth servidor de teselas recep eth madeish tractament dès un aute angle. Martin servís teselas vectoriaus MVT dirèctament dès PostGIS, e es URLs de MVT pòden semblar recorsi estatics inofensius se non i pares atencion — eth tipe de causa qu'ei facil de deishar accessible ath mon per accident. Botèrem Caddy dauant hènt forward_auth, atau qu'eth pròpi servidor de teselas non a cap accès public sense autentificar.

4. Çò qu'ei dissenhat mès encara non construït

Eth modèl de dades ja a lòc entà moduls que non existissen encara: cadastre (registre dera proprietat), padró (eth registre municipau de residents), activitats economicas (eth registre d'activitats economicas locaus) e 3D tiles. Cap non ei construït. Dissenhar er esquèma entad eri ara, en sòrta de descobrir mès endauant qu'era basa non pòt escàder un modul de padró sense cirurgia, ei era madeisha apòsta qu'era "multi-tenancy": pagar eth prètz dera generalitat lèu, mentre ei barat, enes parts de qué ès rasonablament segur que les auràs besonh.

Tancament

Nautaran non ei publicat, non ei provat a escala, e a exactament un "tenant" per ara. Mès aqueth "tenant" ei un ajuntament reau qu'es sòns 9.902 elements auien passat quasi ua decada sense tocar-se, e tornar aguestes dades entà un sistèma qu'era gent vertadèrament emplegue ja a justificat eth trabalh dera plataforma. Que era rèsta des moduls arriben a construir-se depen de se mès ajuntaments vòlen sumar-s'i — mès era basa ei construïda entà que posquen hè'c, sense qu'auem de tornar a escríuer arren entà permeter-lo.