Groupes hierarchiques a 6 niveaux : la feature la plus complexe
Points cles : Construire un systeme de groupes hierarchiques a 6 niveaux de profondeur avec 4 roles, des requetes recursives PostgreSQL (CTE), et 31 politiques RLS, c'est la feature la plus complexe de TAMSIV. Cet article couvre le modele de donnees, l'heritage des permissions, le composant frontend HierarchicalGroupPicker, et les lecons apprises.
Il y a des features qui semblent simples sur le papier. "Ajoute des groupes" — ca a l'air innocent. Comme ajouter un panier dans une app e-commerce. Puis tu commences a creuser et tu realises que tu viens d'ouvrir la boite de Pandore.
TAMSIV supporte des groupes hierarchiques jusqu'a 6 niveaux de profondeur. Mon club de plongee a ete le cas d'usage parfait : Club → Commission Technique → Niveau 1 → Groupe du mardi. Une famille utilise : Famille → Maison → Cuisine / Jardin / Garage. Une PME : Entreprise → Departement → Equipe → Projet.
C'est la feature qui m'a pris le plus de temps. C'est aussi celle qui rend TAMSIV utilisable pour des organisations reelles, pas juste pour un usage solo.
Comment modeliser des groupes hierarchiques en PostgreSQL ?
Le modele de donnees repose sur un concept simple : chaque groupe a un parent_id qui pointe vers son groupe parent. Un groupe racine a parent_id = NULL.
-- Table simplifiee
CREATE TABLE collaborative.groups (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
parent_id UUID REFERENCES collaborative.groups(id),
created_by UUID REFERENCES auth.users(id),
depth INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now()
);
Pour afficher l'arbre complet d'un groupe, j'utilise des requetes recursives (Common Table Expressions, ou CTE) en PostgreSQL :
WITH RECURSIVE group_tree AS (
-- Base : le groupe racine
SELECT id, name, parent_id, 0 AS depth
FROM collaborative.groups
WHERE id = p_group_id
UNION ALL
-- Recursion : les sous-groupes
SELECT g.id, g.name, g.parent_id, gt.depth + 1
FROM collaborative.groups g
JOIN group_tree gt ON g.parent_id = gt.id
WHERE gt.depth < 6 -- Garde-fou
)
SELECT * FROM group_tree;
La clause WHERE depth < 6 est un garde-fou critique. Sans elle, une erreur de donnees (un cycle de references par exemple) ferait tourner la requete en boucle infinie. Six niveaux couvrent tous les cas d'usage reels que j'ai rencontres.
Ce modele de donnees vit dans le schema collaborative, separe du reste — j'explique pourquoi dans l'article sur la structuration de la base de donnees.
Quels sont les quatre roles et comment fonctionnent-ils ?
Chaque membre d'un groupe a un role qui determine ses permissions :
- Admin — Tout faire : creer, modifier, supprimer, gerer les membres, inviter, modifier les parametres du groupe. C'est le createur du groupe par defaut.
- Manager — Gerer le contenu : creer/modifier/supprimer les taches et memos, assigner des membres, valider les checklists. Mais pas gerer les membres ni les parametres.
- Member — Contribuer : creer du contenu, voir tout le contenu du groupe, valider ses propres items de checklist.
- Viewer — Lecture seule : voir le contenu, rien d'autre. Utile pour les observateurs ou les parties prenantes qui veulent suivre l'avancement sans intervenir.
Le piege de l'heritage des permissions
Si tu es Admin du groupe "Club de plongee", est-ce que tu es automatiquement Admin de "Commission Technique" (un sous-groupe) ? Dans TAMSIV, la reponse est oui — mais avec des nuances.
L'heritage fonctionne vers le bas : un Admin d'un groupe parent a les memes droits dans tous les sous-groupes. Mais un Admin d'un sous-groupe n'a aucun droit dans le groupe parent. Cette asymetrie est naturelle (le directeur voit tout, le chef d'equipe voit son equipe), mais elle ajoute de la complexite aux requetes SQL.
Pour verifier les permissions, chaque requete doit remonter l'arbre hierarchique jusqu'a trouver un role ou atteindre la racine :
-- L'utilisateur a-t-il un role dans ce groupe ou un parent ?
WITH RECURSIVE parent_chain AS (
SELECT id, parent_id FROM collaborative.groups WHERE id = p_group_id
UNION ALL
SELECT g.id, g.parent_id
FROM collaborative.groups g
JOIN parent_chain pc ON g.id = pc.parent_id
)
SELECT role FROM collaborative.group_members
WHERE group_id IN (SELECT id FROM parent_chain)
AND user_id = auth.uid()
ORDER BY role ASC -- Admin > Manager > Member > Viewer
LIMIT 1;
Comment ecrire et tester 31 politiques RLS ?
C'est le chiffre qui fait mal : 31 politiques Row Level Security pour le schema collaborative. Supabase utilise RLS pour securiser l'acces aux donnees — chaque table a des regles qui determinent qui peut lire, ecrire, modifier, supprimer.
Voici des exemples concrets :
Politique de lecture simple
-- Un membre peut voir les taches de ses groupes
CREATE POLICY "members_read_tasks" ON collaborative.group_tasks
FOR SELECT USING (
EXISTS (
SELECT 1 FROM collaborative.group_members gm
WHERE gm.group_id = group_tasks.group_id
AND gm.user_id = auth.uid()
)
);
Politique avec heritage hierarchique
-- Un admin peut supprimer les taches de ses groupes ET sous-groupes
CREATE POLICY "admin_delete_tasks" ON collaborative.group_tasks
FOR DELETE USING (
EXISTS (
WITH RECURSIVE parent_chain AS (
SELECT id, parent_id FROM collaborative.groups
WHERE id = group_tasks.group_id
UNION ALL
SELECT g.id, g.parent_id
FROM collaborative.groups g
JOIN parent_chain pc ON g.id = pc.parent_id
)
SELECT 1 FROM collaborative.group_members gm
WHERE gm.group_id IN (SELECT id FROM parent_chain)
AND gm.user_id = auth.uid()
AND gm.role = 'admin'
)
);
Tester 31 politiques, c'est 31 scenarios de test minimum. En realite, c'est bien plus, parce que chaque politique doit etre testee positivement (l'acces est bien accorde) ET negativement (l'acces est bien refuse). Methodique, ennuyeux, indispensable. Le detail sur l'approche de test est dans l'article sur l'audit de securite.
Comment construire le composant HierarchicalGroupPicker ?
Cote frontend, afficher un arbre de groupes avec indentation, icones de role, et interactions, c'est un defi UI a part entiere.
Le HierarchicalGroupPicker est un composant React Native qui :
- Affiche l'arbre avec indentation proportionnelle a la profondeur (utilisant
spacing()du systeme de scaling). - Indique le role de l'utilisateur dans chaque groupe via une icone coloree.
- Permet la selection avec un toggle "inclure les sous-groupes" — quand tu selectionnes un groupe parent, les sous-groupes peuvent etre inclus automatiquement.
- Supporte le fold/unfold : les sous-groupes sont repliables pour ne pas encombrer l'ecran sur les hierarchies profondes.
Ce composant est reutilise partout : dans le filtre de l'agenda, dans la creation de taches, dans l'ecran de gestion des groupes. C'est un investissement de temps enorme au depart, mais un gain de temps colossal ensuite.
Le pattern FilterBar
Le HierarchicalGroupPicker fonctionne avec la FilterBar, un composant de filtrage avec 3 contextes (Prive / Partage / Tout) et des modes (toutes les taches / creees par moi / assignees a moi). La combinaison des deux permet des requetes tres precises : "montre-moi les taches assignees a moi dans le groupe Famille et ses sous-groupes".
Quels sont les cas d'usage reels des groupes hierarchiques ?
Voici les cas d'usage que mes testeurs utilisent activement :
- Famille : Famille → Maison (taches menageres) / Vacances (planning) / Ecole (devoirs des enfants). J'en parle dans l'article sur l'organisation familiale.
- Club sportif : Club → Commissions → Niveaux → Groupes par creneau.
- Petite equipe : Entreprise → Departement → Projet → Sprint.
- Association : Association → Pole → Evenement.
La limite de 6 niveaux est rarement atteinte. En pratique, 3-4 niveaux couvrent 95% des besoins. Mais cette profondeur maximale est un filet de securite — mieux vaut l'avoir et ne pas en avoir besoin.
Quelles lecons tirer de cette implementation ?
- Les requetes recursives PostgreSQL sont puissantes mais couteuses : Chaque requete qui remonte la hierarchie fait N jointures (ou N = la profondeur). Un index sur
parent_idest indispensable. - Les politiques RLS s'accumulent vite : Chaque table × chaque operation (SELECT, INSERT, UPDATE, DELETE) × chaque role = beaucoup de politiques. Documente-les dans un fichier dedie.
- L'heritage des permissions est le point le plus delicat : Decide tot si les permissions se propagent vers le bas, vers le haut, ou les deux. Changer cette decision plus tard est extremement couteux.
- Le composant frontend est un investissement : Un bon HierarchicalGroupPicker prend une semaine a construire correctement, mais se reutilise partout.
- Teste avec des donnees reelles : Un arbre de 3 groupes en dev ne revele pas les memes bugs qu'un arbre de 15 groupes sur 4 niveaux en production.
Si tu construis un systeme collaboratif, la hierarchie est ce qui transforme une simple "liste de groupes" en un outil que les organisations adoptent reellement. C'est dur, c'est long, mais c'est ce qui fait la difference avec les concurrents qui se limitent a des groupes plats.
FAQ
Pourquoi limiter a 6 niveaux de profondeur ?
Six niveaux couvrent tous les cas d'usage reels que j'ai rencontres. Au-dela, l'arbre devient ingerable pour l'utilisateur. La limite protege aussi contre les requetes recursives trop profondes qui pourraient impacter les performances. En pratique, 3-4 niveaux suffisent pour 95% des organisations.
Est-ce que les requetes recursives posent des problemes de performance ?
Avec un index sur parent_id et une limite de profondeur, les performances sont excellentes meme avec des centaines de groupes. Une requete recursive sur 6 niveaux prend moins de 10ms sur Supabase PostgreSQL. Le vrai risque est un cycle de references (group A parent de B, B parent de A) — la clause WHERE depth < 6 protege contre ca.
Comment gerer l'invitation de nouveaux membres ?
L'admin d'un groupe peut inviter par email ou par lien de partage. L'invite rejoint le groupe avec le role choisi par l'admin. Si le groupe a des sous-groupes, l'invite n'a acces qu'au groupe dans lequel il a ete invite — sauf si un admin du parent lui donne explicitement acces.
Les checklists de groupe fonctionnent-elles differemment des checklists personnelles ?
Oui. Les checklists de groupe ont deux modes de validation : "single" (une seule personne valide pour tout le monde) et "everyone" (chaque membre doit valider individuellement). Le mode "everyone" est parfait pour les listes de courses familiales ou personne n'achete la meme chose.
Peut-on deplacer un groupe dans un autre groupe parent ?
Oui, un admin peut reorganiser la hierarchie en changeant le parent_id d'un groupe. Mais c'est une operation sensible : les permissions heritees changent immediatement, et les membres du nouveau parent voient le contenu du groupe deplace. Un message de confirmation explicite avertit l'admin des consequences.