Aller au contenu
HL
DisponibleEN
Étude de cas / fintechlivré

Finance OS

PWA de finances multidevise auto-hébergée avec un conseiller Claude sur AWS Bedrock

En bref

Plateformes
Web, PWA installable
Calendrier
Avril 2026 (32 commits, 24–26 avril)
Équipe
Seul
Base de code
32 modèles Prisma, 36 route handlers d'API, 24 pages App Router
Tests
99 tests Vitest, exécutés dans GitHub Actions sur les pushes et les PR vers main
Source
Dépôt public : github.com/Hmz1hb/finance-os

Vue d'ensemble

Finance OS est une progressive web app open source et auto-hébergée pour gérer côte à côte finances personnelles et professionnelles. Elle a été conçue pour les freelances et les dirigeants de petites entreprises qui gagnent et dépensent dans plusieurs devises (MAD, GBP, USD et EUR) et gèrent une société en parallèle de leurs finances personnelles. Elle couvre les transactions, les échéanciers de revenus, les créances, la rémunération du dirigeant, la paie, les règles récurrentes, les abonnements, les prêts, les objectifs, le patrimoine net, les provisions pour impôts et les rapports, et ajoute un conseiller Claude en streaming et l'OCR des reçus sur AWS Bedrock. Je l'ai conçue, construite, déployée et testée seul.

Le défi

Les outils de budget du marché partent du principe d'une seule devise et d'un seul foyer. Ce cas d'usage nécessitait quatre devises, avec le dirham marocain comme devise de reporting, et une frontière stricte entre l'argent de la société et l'argent personnel, puisque la rémunération du dirigeant circule entre les deux. Il fallait aussi des estimations configurables de l'impôt sur les sociétés britannique et du régime marocain de l'auto-entrepreneur, ainsi qu'une trésorerie qui tienne compte de l'argent pas encore encaissé. Les données étant privées, l'application devait être auto-hébergée pour un seul utilisateur, conserver les reçus dans un stockage privé et appeler un LLM sans stocker de clés d'accès cloud dans l'application. La plupart des bugs d'argent sont silencieux : les totaux devaient donc concorder d'une page à l'autre, et les mouvements d'argent en plusieurs étapes devaient être atomiques.

Mon rôle

J'ai porté l'ensemble du projet : le modèle de domaine Prisma (32 modèles, trois migrations), 36 route handlers Next.js, 24 pages App Router et les composants d'interface, ainsi que l'intégration Bedrock pour le conseiller et l'OCR des reçus. J'ai aussi construit le stockage des pièces jointes sur S3, le proxy d'authentification et de contrôle des requêtes, la configuration PWA, la suite Vitest et la CI. Le déploiement était aussi de mon ressort, sur AWS : EC2 avec Docker Compose, configuration d'exécution depuis SSM Parameter Store, déploiements depuis GitHub via OIDC et sauvegardes Postgres nocturnes vers S3. Après le déploiement, j'ai mené dix cycles documentés de QA pilotée par agents sur l'application en ligne, consignés dans le dossier qa/ du dépôt, puis corrigé et revérifié en production les problèmes relevés.

Architecture

  1. Clients

    • PWA installable

      Next.js 16 App Router, React 19, Tailwind CSS v4, Recharts, Workbox (@ducanh2912/next-pwa)

      Tableaux de bord rendus côté serveur avec des vues personnelle et professionnelle distinctes, une barre latérale d'entités, des graphiques et une page de repli hors ligne.

  2. Edge/Diffusion

    • Cloudflare Tunnel

      cloudflared container

      Publie l'application via un tunnel sortant. Le conteneur de l'application n'expose le port 3000 que sur le réseau Compose interne.

  3. Services

    • Proxy de requêtes

      Next.js 16 proxy (src/proxy.ts), NextAuth v5

      Impose la session, compare l'en-tête Origin au Host sur les écritures d'API, n'accepte que des corps JSON ou multipart en écriture, et limite le débit des écritures par utilisateur et des tentatives de connexion par IP.

    • API financière

      Next.js route handlers, Zod 4, Prisma 7

      36 handlers couvrant les transactions, les créances, la rémunération du dirigeant, la paie, les règles récurrentes, les objectifs, les prêts, les abonnements, les estimations fiscales et l'import CSV. Tous valident les entrées avec un même ensemble de schémas Zod partagés.

    • Logique métier

      TypeScript modules (cockpit, cashflows, tax, recurring, health, net-worth)

      Calcule les soldes entre entités, les occurrences des règles récurrentes, une projection du fonds d'urgence, un score de santé financière, ainsi que des estimations configurables de l'impôt sur les sociétés britannique et du régime marocain de l'auto-entrepreneur.

  4. Média

    • Stockage des reçus

      AWS S3, presigned URLs

      Conserve les pièces jointes dans un bucket privé avec une règle de cycle de vie. Les fichiers ne sont servis que via des URL présignées à courte durée de vie.

  5. Données

    • PostgreSQL

      PostgreSQL 16, Prisma 7 with the pg adapter

      Stocke les montants en centimes entiers, avec le taux de change et l'équivalent en MAD au moment de l'écriture. Utilise des suppressions logiques (soft delete) et conserve une table quotidienne des taux de change.

  6. Services tiers

    • Taux de change

      open.er-api.com

      Récupère une fois par jour les taux par rapport au GBP, les étend en une matrice complète MAD/GBP/USD/EUR et la met en cache dans Postgres. Les taux peuvent aussi être saisis manuellement.

    • Conseiller IA et OCR des reçus

      AWS Bedrock (Claude Sonnet), InvokeModelWithResponseStream

      Diffuse en streaming les réponses du conseiller à partir d'un instantané compact des finances de l'utilisateur, et transforme les images de reçus en JSON structuré.

  7. Infrastructure

    • Hébergement et livraison

      EC2, Docker Compose, SSM Parameter Store, GitHub Actions OIDC

      Lorsque la CI passe sur main, le workflow de déploiement envoie une archive des sources sur S3, et une commande SSM reconstruit l'application sur l'hôte. Une tâche cron écrit chaque nuit une sauvegarde pg_dump sur S3.

Décisions clés

  1. 01

    Centimes entiers et instantané du taux de change par ligne

    Chaque transaction stocke amountCents, sa devise, le taux de change utilisé et une valeur madEquivalentCents figée à l'écriture de la ligne. Les rapports historiques ne changent donc pas quand les taux évoluent, et les totaux concordent d'une page à l'autre. Le coût : des colonnes supplémentaires sur chaque table monétaire, et toute conversion de devise doit passer par un unique module money, qui a ses propres tests.

  2. 02

    Un modèle de domaine centré sur les entités

    Tôt dans le développement, j'ai repensé le modèle pour placer en son cœur les entités financières (par exemple une société et une entité personnelle). Créances, revenus attendus, rémunération du dirigeant, provisions pour impôts et règles récurrentes appartiennent chacun à une entité. Cela garde l'argent professionnel et l'argent personnel séparés, et fait de la rémunération du dirigeant un flux explicite entre une entité professionnelle et une entité personnelle plutôt que deux transactions sans lien.

  3. 03

    Des flux monétaires multi-étapes atomiques

    Le flux de rémunération du dirigeant écrit quatre enregistrements : une dépense professionnelle, un revenu personnel, un upsert de la personne salariée et un paiement de paie. Les quatre s'exécutent dans une même $transaction Prisma interactive : si une étape échoue, les autres sont annulées. Un test dédié couvre ce rollback.

  4. 04

    Claude sur Bedrock avec des identifiants de rôle IAM

    Le conseiller et l'OCR appellent Claude via AWS Bedrock avec des identifiants AWS basés sur un rôle, si bien que l'application ne stocke aucune clé d'accès AWS. Le prompt système du conseiller contient un instantané compact des finances de l'utilisateur plutôt que les tables brutes, ce qui garde chaque requête légère. Si un appel à Bedrock échoue, les routes d'IA renvoient une réponse de repli au lieu d'une erreur.

  5. 05

    Taux de change sans clé avec une matrice en cache

    L'API Frankfurter, adossée à la BCE, ne propose pas le MAD : je récupère donc les taux depuis open.er-api.com, qui ne nécessite aucune clé d'API. Une seule requête quotidienne par rapport au GBP est étendue aux 16 paires de devises, enregistrées par upsert dans une seule transaction. Si une mise à jour échoue, l'application continue d'utiliser les derniers taux en cache, et les taux peuvent aussi être saisis manuellement.

  6. 06

    Déploiement AWS sur un seul hôte, sans secrets de longue durée

    Je fais tourner l'application avec Docker Compose sur une seule instance EC2 derrière un Cloudflare Tunnel, et non sur un service de conteneurs managé. GitHub Actions obtient l'accès à AWS via OIDC, et la configuration d'exécution vient de SSM Parameter Store : aucune clé AWS de longue durée n'est donc stockée dans GitHub. C'est simple et peu coûteux pour un seul utilisateur. Les contreparties : pas de scalabilité horizontale, et un rate limiter qui garde ses compteurs en mémoire.

Résultats

  • J'ai construit une PWA de finances multidevise en trois jours (24–26 avril 2026). Elle couvre les revenus, les dépenses, la paie, les créances, les règles récurrentes, les abonnements, les prêts (avec une option boule de neige), les objectifs, le patrimoine net, les provisions pour impôts et les rapports.
  • J'ai ajouté un conseiller Claude en streaming et l'OCR des reçus sur AWS Bedrock.
  • J'ai écrit 99 tests Vitest couvrant les calculs monétaires, la récurrence, les schémas de validation partagés, les contrôles CSRF et de limitation de débit, le plafond des contributions aux objectifs, les vérifications préalables de taille d'upload et le rollback de la rémunération du dirigeant.
  • J'ai mis en place une CI qui exécute le lint, le typecheck, les tests, le build de production et un build Docker sur les pushes et les PR vers main. Un workflow de déploiement distinct ne s'exécute qu'une fois la CI passée et livre main sur AWS.
  • J'ai mené dix cycles de QA documentés sur l'application déployée et vérifié les correctifs en ligne. Parmi ces correctifs : une validation partagée pour les requêtes POST et PATCH et une pagination par curseur pour les transactions.
  • J'ai publié le projet sous forme de dépôt public avec documentation d'installation, documentation des secrets et scripts d'infrastructure.

Stack

Frontend
Next.js 16 (App Router)React 19TypeScriptTailwind CSS v4RechartsSonnerlucide-reactWorkbox PWA
Backend
Next.js route handlersNextAuth v5 (credentials)Zod 4date-fnsbcryptjs
Données
PostgreSQL 16Prisma 7
IA
AWS BedrockClaude Sonnet (streaming + vision OCR)
Cloud et DevOps
AWS EC2AWS S3AWS SSM Parameter StoreDocker ComposeCloudflare TunnelGitHub Actions (OIDC)
Tests
VitestTesting Libraryjsdom