Backend En producció

Vecinly: un SaaS per a comunitats de veïns

Vecinly és la plataforma tot en un per gestionar comunitats de propietaris: incidències, reserves, comptabilitat, enquestes, proveïdors, tasques i comunicació. La vaig construir sencera i l’opero jo: l’API, la web, les dues apps i la infraestructura. És en producció, amb clients de pagament.

Rol
Fundador i desenvolupador únic
Des de
Febrer del 2026
Backend
Java · Spring Boot · PostgreSQL
Clients
React · React Native (Expo)
Veure vecinly.comLa web en producció, en 11 idiomes

En xifres

~590casos d’ús, des de donar d’alta una comunitat fins a cobrar-ne la subscripció
~230models de domini que guarden les regles de negoci, sense dependre de res
11idiomes i 11 països, cadascun amb la seva llei de propietat horitzontal
~4.680tests al backend, contra PostgreSQL real

01

Context

El problema

Qui pateix la gestió d’una comunitat és el president, que no cobra per fer-ho, o l’administrador de finques, que porta desenes de comunitats alhora. Les incidències es perden en un grup de WhatsApp, els comptes viuen en un full que només entén qui el va fer i ningú no sap en quin punt és res sense preguntar.

Un producte per a això ha de servir perfils molt diferents (veïns, presidents, administradors, proveïdors i porters) sense que cap vegi el que no li toca. I com que guarda actes, quotes i dades personals de veïns d’11 països, la seguretat és el primer.

Figura 0. La portada de vecinly.com, amb la seva animació. La web es prerenderitza en els 11 idiomes perquè la puguin llegir els cercadors i els motors d’IA, que no executen JavaScript.

Què fa

Uns 50 dominis de negoci, dels quals sis sostenen el dia a dia d’una comunitat:

  • IncidènciesEn un tauler per estats: s’obre, s’assigna i es tanca, i qui la va reportar veu en quin punt és.
  • ComptabilitatMoviments amb les seves partides i el seu saldo, en lloc d’un full de càlcul.
  • FacturacióFactures de la comunitat enllaçades amb la comptabilitat i amb el cobrament recurrent.
  • XatVeïns, president, administrador i proveïdors parlen dins del producte, en temps real.
  • Enquestes i juntesConsultes d’un clic, convocatòries i seguiment de les reunions.
  • ReservesLa pista o la sala es reserven des de l’app, amb el calendari a la vista de tothom.

02

Arquitectura

Tres clients, una sola API

La web, l’app d’iOS i la d’Android parlen amb el mateix servei. Les regles de negoci i els permisos viuen en un únic lloc: una regla s’escriu una vegada i arriba als tres clients, i la web i les apps no es poden comportar de manera diferent.

El que la posa en marxa El que fa servir Adaptadors tradueixen de fora a dins Casos d’ús ~590 en total ports 165 de sortida Domini ~230 models Web App iOS App Android Cloudflare Tasques ~40 programades REST Canal en directe Planificador Persistència PostgreSQL Xifratge KMS Pagaments Stripe Facturació Holded Identitat Identitat Fitxers Emmagatzematge Avisos Correu i push les dependències apunten cap a dins
Figura 1. L’API de Vecinly, per dins. Les regles de negoci viuen al domini, que no depèn de res. Els casos d’ús les orquestren i parlen amb l’exterior només a través de ports; els adaptadors, a la vora, connecten aquests ports amb la web, les apps i els proveïdors. Xifres aproximades del codi.

Un monòlit hexagonal, per a aquesta fase

En un MVP, el que importa és validar la idea: que el desenvolupament sigui àgil, que desplegar sigui senzill i que els costos es mantinguin baixos. Un monòlit ben construït, amb les fronteres entre dominis clares, és una opció molt vàlida per a això: una sola base de codi, un sol desplegament i res de xarxa entre les peces. Els microserveis són la millor opció quan la situació ho requereix, i en aquesta fase hauria estat una mala idea: molta complexitat per a un producte que encara havia de demostrar que algú el volia.

CapaQuè contéRegla
DominiEntitats i regles de negoci (~230 models)Sense Spring ni base de dades
Casos d’úsLes accions de l’aplicació (~590 en total)Parlen amb l’exterior només per ports
AdaptadorsREST, WebSocket, PostgreSQL, Stripe, Holded, KMSImplementen els ports; es canvien sense tocar el domini

El preu és la disciplina: les fronteres entre dominis depenen del disseny i de la revisió, no de la xarxa, i hi ha tests d’arquitectura que les vigilen. Aquesta disciplina és també el que fa possible el pas següent.

El següent pas: cap als microserveis

La fase ha canviat. Ara els objectius són tres: guanyar usuaris, cosa que farà créixer el consum de recursos i el seu cost; obtenir ingressos, que permetran pagar una infraestructura millor; i ampliar el producte amb funcions que van més enllà de validar-lo. Amb aquests objectius sí que toca començar a moure’s cap als microserveis, i és el que estic plantejant.

La complexitat i la feina augmenten, però l’ús de recursos millora molt, sobretot en un entorn serverless. Amb un monòlit, tota l’aplicació escala com un bloc encara que la major part del que fa s’utilitzi poc. Separada, cada peça té la seva pròpia política: els serveis d’ús esporàdic escalen a zero i no costen res mentre ningú no els fa servir, i els que sempre han de respondre a l’instant, com l’inici de sessió o el xat, mantenen una instància encesa per evitar l’arrencada en fred. Com que cada domini ja parla amb la resta a través de ports, separar-ne un és canviar un adaptador, no reescriure’n la lògica.

Monòlit · un sol bloc Serveis · el nucli organitza la comunicació Nucli Xat Incidències Comptabilitat Reserves Documents Cobraments Juntes sempre encès escala a zero
Figura 2. Trencar el monòlit. El bloc es parteix per les fronteres entre dominis. Una peça, el nucli, queda al centre i organitza la comunicació; cada servei parla només amb ell i escala a la seva manera.

Els casos d’ús, per àrees

Cada cas d’ús respon a una pregunta concreta d’un perfil concret: «registrar una incidència», «aprovar el pressupost d’un proveïdor», «convocar una junta». Es reparteixen així:

ÀreaCasos d’ús principalsOperacions
Comunitat i altesCrear la comunitat amb un assistent, donar forma a portals i habitatges, importar coeficients, convidar veïns i llogaters, assignar administrador~130
Manteniment i proveïdorsRegistrar i seguir incidències, assignar tasques, demanar treballs, rebre i aprovar pressupostos i factures de proveïdors~120
ComptabilitatPressupost anual, quotes per coeficient, grups de despesa, moviments i saldo~65
Subscripció i cobramentsContractar un pla, canviar-lo o cancel·lar-lo, cobrar i emetre la factura~55
ComunicacióXat, comunicats, avisos d’emergència i moderació~40
Juntes i decisionsConvocar juntes, votacions amb validesa legal i enquestes~35
DocumentsCarpetes de la comunitat i de cada propietari, documents legals~30
Zones comunesReserves i places d’aparcament~20
PortersAlta, assignació a comunitats i el seu dia a dia~20

Xifres aproximades, comptades al codi l’octubre del 2026. Una operació és una acció de l’API; un cas d’ús en pot servir diverses.

No cal controlar-los tots al mateix nivell, però sí els essencials: quin perfil pot fer què, amb quines dades i què passa després. Aquests en són alguns:

Vecinly Residents Gestió professional Reservar una zona comunaRegistrar una incidènciaVotar en una juntaConvocar una juntaAprovar un pressupostEmetre les quotesEnviar un pressupostPujar una factura Veí President Administrador Proveïdor
Figura 3. Alguns casos d’ús essencials i qui els fa servir: els residents a l’esquerra, la gestió professional a la dreta. Cada perfil només veu els seus.

Dues peces, per dins

Darrere de cada pantalla hi ha molts casos d’ús alhora. L’alta d’una comunitat és un flux que encadena país, tipus d’edifici, dades, estructura i pla, i en surten els habitatges, el tier i la subscripció. La comptabilitat és un panell on es creuen pressupost, quotes per coeficient, despeses i pagaments de cada habitatge.

Figura 4. L’assistent per donar d’alta una comunitat, que s’emplena sol: país, tipus d’edificació, dades, estructura, resum i pla. És el component real de la plataforma, amb una comunitat inventada.
Dashboard de comptabilitat: saldo al banc, ingressos i despeses previstos, fons de reserva i previsió anual de tresoreria.Pressupost anual: despeses recurrents, despeses puntuals i ingressos extra, amb les seves partides.Quadrant de pagaments: cada habitatge i la seva quota de cada mes, pagada, pendent o vençuda.
Figura 5. El panell de comptabilitat, pestanya a pestanya: dashboard, pressupost anual i quadrant de pagaments. Captures de la comunitat d’exemple de vecinly.com, amb dades inventades.

03

Aïllar cada comunitat

Una base de dades per a totes, quatre capes d’aïllament

Centenars de comunitats comparteixen base de dades, i un veí no ha de veure mai res d’una altra. Una base o un esquema per client multiplicaria les migracions i les connexions per cada comunitat nova. Per això l’aïllament és lògic, per comunitat, i se sosté en quatre capes:

CapaCom
AutoritzacióCada ruta declara quin rol i quin pla la permeten; el que no declara res, es denega
ConsultesCada consulta va acotada a una comunitat, i els índexs estan pensats per a això
XifratgePràcticament tot es desa xifrat, amb la clau de la seva comunitat
Temps realLes subscripcions al xat i als avisos es deneguen per defecte

Una clau de xifratge per comunitat

Es xifra pràcticament tota la informació, també la que la normativa europea no obliga a xifrar: dades personals, converses, documents, comptes. Només queda sense xifrar el que de debò no té cap importància. Una clau mestra que no surt mai del KMS embolcalla les claus de dades, i cada comunitat té la seva. L’identificador de la comunitat entra al xifratge com a dada associada: un text xifrat no serveix en una altra comunitat.

Clau mestra no surt mai del KMS Clau · comunitat A Dades de la comunitat A veí · 9f3a… Clau · comunitat B Dades de la comunitat B veí · 7d21…acta · c5e9…quota · 1a6f… Clau · comunitat C Dades de la comunitat C veí · b4d0…acta · 0e7c…quota · 93a2… quota · AAD = A 9f3a·c1e7·08b2 1.240,00 € no desxifra xifratge autenticat · l’identificador de la comunitat forma part del xifratge
Figura 6. Una clau per comunitat. Pràcticament tot es desa xifrat, i una dada xifrada d’una comunitat no serveix en una altra: l’identificador de la comunitat forma part del xifratge. Si la clau de sistema no arriba del KMS, l’aplicació no arrenca.

Si la clau no arriba del KMS, l’aplicació es nega a arrencar: falla tancada. I per poder cercar una dada xifrada sense desxifrar-la, s’hi desa al costat una empremta criptogràfica (un índex cec): les cerques i la unicitat passen per aquesta empremta.

El cicle d’una petició

Cada petició passa diverses comprovacions abans de tocar una dada, i la clau de la comunitat només es carrega del KMS quan cal.

Origen d’on arriba? Volum límit de peticions Identitat qui és? Regla sense regla, denegat Permís rol i pla? Clau la de la comunitat Les dades xifrades Laura · veïna veure les quotes Una petició que pot passar
Figura 7. El cicle d’una petició. Abans de tocar una dada, cada petició passa sis controls. La que té permís arriba a les dades de la seva comunitat, que es desxifren amb la seva clau; la que no té el rol o el pla, l’atura el control de permís.
Un cas d’ús, de principi a fi Veí API Cas d’ús Dades alt [no li correspon] registrar una incidència qui és i què pot fer? denegat desar, xifrat, a la seva comunitat incidència registrada
Figura 8. Un cas d’ús, de principi a fi. Les regles viuen al cas d’ús: la web i les dues apps passen pel mateix lloc.

04

Cobraments

Stripe cobra, Holded factura

Vecinly cobra subscripcions per trams d’habitatges en diversos països de la UE i ha d’emetre factures legals. El cobrament el fa Stripe, amb Stripe Tax. La factura la genera l’aplicació a Holded quan es confirma el pagament, amb l’IVA exacte que va cobrar Stripe, per complir VeriFactu. Les targetes no passen mai pel meu servidor.

Cobrar una sola vegada

Stripe reintenta els seus avisos de pagament i els pot enviar desordenats. Així que, quan se’n rep un, només se’n verifica la signatura, es desa amb clau única i es respon de seguida; la feina real va a part, per una cua.

no sí sí no sí no Arriba un avís des de Stripe Desar i respondre amb clau única Encuar la feina real Processar a part en segon pla Activar el pla de la comunitat Emetre la factura a Holded Rebutjar no és de Stripe Descartar sense tornar a cobrar Reintentar si persisteix, a revisió Signatura vàlida? Ja s’havia rebut? S’aplica bé? avís nou
Figura 9. Cobrar una sola vegada. Un avís nou arriba fins al pla actiu i la factura; si Stripe repeteix el mateix avís, la clau única l’atura abans de tocar res. Si en processar alguna cosa falla, es reintenta, i si continua fallant passa a revisió.

En processar-lo, es comprova si ja s’havia processat i es marca com a processat en la mateixa transacció que aplica el canvi, perquè una fallada a mitges no deixi un pagament a mitges. I si la subscripció deixa d’estar sana, la comunitat torna al pla gratuït.

Un pagament i la seva factura Administrador Stripe API Cas d’ús Holded pagar la subscripció pagament confirmat ja l’havia rebut? activar el pla emetre la factura factura
Figura 10. Un pagament i la seva factura. Només es factura un pagament confirmat, i un avís repetit no s’aplica dues vegades.

05

L’app

Un codi, dues apps

Veïns i presidents necessiten una app; administradors, proveïdors i porters, una altra de molt diferent. Totes dues surten d’un únic codi en Expo i React Native: Vecinly i Vecinly Pro, a iOS i Android, amb el mateix inici de sessió. Les apps no venen res, per les normes de les botigues: el pagament i la creació de comunitats es fan a la web.

Descarrega-la a l’App Store Disponible a Google Play
App Vecinly: resum de la comunitat amb incidències, avisos i tasques. Resum
App Vecinly: reserves de zones comunes en un calendari. Reserves
App Vecinly: tasques de manteniment per estat. Tasques
App Vecinly: comptabilitat de la comunitat amb saldo i moviments. Comptabilitat
App Vecinly: xat entre veïns de la comunitat. Xat
Figura 11. L’app de Vecinly amb dades d’exemple: resum, reserves, tasques, comptabilitat i xat.

Com arriben els avisos

Qualsevol cosa que passa en una comunitat pot generar un avís. El recorregut és sempre el mateix: l’API executa el cas d’ús i desa el canvi a la base de dades; només quan està desat publica un esdeveniment en una cua. Així qui ha fet l’acció no espera que s’enviï res, i mai no arriba un avís d’una cosa que no ha quedat registrada.

Un consumidor recull l’esdeveniment a part, decideix a qui d’aquella comunitat li toca assabentar-se’n i per quin canal ho prefereix cadascú, i el lliura: notificació push a través de Firebase, correu o a l’instant pel canal en directe si té l’app oberta. Si alguna cosa falla, l’esdeveniment es queda a la cua i es reintenta.

1 · Passa alguna cosa 2 · Es desa 3 · Es publica 4 · Es reparteix 5 · Arriba App o web una acció API cas d’ús PostgreSQL el canvi, desat Cua esdeveniments esdeveniment Consumidor a qui i per on Firebase push al mòbil Correu a la safata Canal en directe a l’app oberta L’esdeveniment surt després de desar: mai no s’avisa d’una cosa que no ha quedat registrada.
Figura 12. Com arriba un avís. L’API desa el canvi i, un cop desat, publica un esdeveniment en una cua. Un consumidor el recull a part, decideix a qui avisar i per quin canal, i el lliura sense que qui ha fet l’acció hagi d’esperar.

06

Producció

Desplegar només amb una etiqueta

Un desplegament equivocat afecta dades reals de comunitats. Per això producció només es desplega des d’una versió etiquetada, i els tests s’executen sobre aquesta mateixa versió. Tornar enrere és desplegar l’etiqueta anterior.

Clients Web11 idiomesApp VecinlyiOS · AndroidApp Vecinly ProiOS · Android inici de sessió Cloudflare DNS · CDN · tallafocs Núvol a la UE Xarxa privada HTTPS API · Java contenidors serverless · tasques programades només per la xarxa privada PostgreSQL gestionada · sense adreça pública Serveis gestionats KMSclau mestra Secretsfora del codi Cuesfeina en segon pla Fitxersdocuments i fotos Desplegament CI: tests → imatge → registre privat → contenidors, només des d’una versió etiquetada Infraestructura com a codi; la base de dades en queda fora i cap canvi no la pot esborrar Serveis externs Firebaseidentitat i push Stripecobraments Holdedfactures legals Correutransaccional
Figura 13. Vecinly en producció. Els clients entren per Cloudflare fins a l’API. L’API i la base de dades comparteixen una xarxa privada: la base de dades no té adreça pública i només l’API hi arriba. Al costat, els serveis gestionats del mateix núvol; a fora, Firebase, Stripe, Holded i el correu.
QuèCom
Tests~4.680; els d’integració, contra PostgreSQL real en contenidors, mai contra una base simulada
Abans de desplegarEls unitaris i els d’integració crítics: diners, subscripcions, permisos i la comptabilitat
Mutation testingSobre subscripcions i domini, amb un llindar mínim que no es pot abaixar sense que falli el build
Base de dadesFora de la infraestructura com a codi: cap canvi d’infraestructura no la pot recrear ni esborrar
EsquemaEs valida en arrencar contra el codi; si no coincideix, l’aplicació no arrenca

Migracions sense aturar el servei

Les migracions són sempre idempotents, i expandir i contraure van en versions diferents: primer s’afegeix i s’emplena, i la restricció arriba a la versió següent, perquè una restricció que falla bloqueja tots els desplegaments posteriors.

07

Conclusions

  • L’arquitectura acompanya la fase. Un monòlit ben partit per dins era l’adequat per validar el producte; aquestes mateixes fronteres són les que ara permeten repartir-lo en serveis quan créixer ho requereix.
  • Aïllar sense multiplicar bases. Quatre capes i una clau per comunitat donen aïllament real sense operar centenars de bases de dades.
  • Cobrar és un problema d’idempotència. El doble cobrament no l’evita la passarel·la: l’eviten la clau única, la transacció i els escombratges.
  • Fallar tancat. Sense clau no arrenca, sense regla es denega i sense subscripció sana es baixa al pla gratuït.
Escriu-me sobre aquest projecte →