Statut : stable Public cible : contributeur Dernière vérification : 2026-05-01 Sources de vérité :
app/models/,app/models/concerns/,db/schema.rb,spec/models/.
Vocabulaire DDD-light (voir
../glossary.md)Les noms de classes Ruby utilisés ci-dessous sont systématiquement annotés
(cible : …)quand ils correspondent à du code legacy en cours de migration :
ContributionFormula→ContributionFormulaContribution→ContributionPlan de renommage :
../migrations/vocabulary_migration.md,phase3-model-rename.
Ce document remplace l'ancien trio docs/MODEL_EVALUATION.md + docs/CONCERNS_ANALYSIS.md + docs/ZONES_CLASSIFICATION.md qui s'étaient mis à diverger.
Person (CRM, données personnelles)
├─> User (authentification — au plus un ; tout User a une Person)
├─> Membership (adhésion annuelle)
├─> Payment (transactions)
├─> Contribution (cible : Contribution — cotisation cirque)
├─> Attendance (présence quotidienne)
└─> MemberNumberHistory (historique numéro membre)
Les formules de cotisation sont stockées dans ContributionFormula (cible : ContributionFormula).
- Séparation claire auth (
User) vs profil (Person). - Invariant :
User→Personobligatoire (person_idNOT NULL) ;Personpeut exister sansUser. - One Source of Truth pour les données personnelles.
- Délégation propre
User → Person. - Côté
Person,has_one :user, dependent: :restrict_with_error: pas de suppression incompatible tant qu’un compte web existe (archive / RGPD). Payment→PaymentLinepolymorphique = un paiement peut regrouper adhésion + cotisation + don.- Audit trail complet via
PaymentAuditLog+ UUID externe. - Versioning sur
MembershipTypeetContributionFormula(cible :ContributionFormula) (version,effective_from/until,change_reason,created_by_user_id).
| Dimension | Score | Commentaire |
|---|---|---|
| Robustesse métier | 8 / 10 | Validations solides, quelques contradictions sur Contribution (cible : Contribution) |
| Performance | 6 / 10 | Indexes composites manquants (cf. §4) |
| Maintenabilité | 7 / 10 | Concerns bien organisés, dualité expired? à clarifier |
| Testabilité | 6 / 10 | Polymorphisme PaymentLine + skip_overlap_validation augmentent le coût des tests |
Dix concerns en place :
Dateable— formatage et scopes temporels.Priceable— conversion centimes / euros.Statusable— humanization et helpers de statuts.Categorizable— humanization des catégories.Humanizable— humanization d'enums divers.Roleable— gestion des rôles utilisateur.Versionable— versioning (MembershipType,ContributionFormula(cible :ContributionFormula)).Validatable— validations communes.Duplicatable— détection / fusion de doublons.SoftDeletable— soft delete avecdeleted_at.
Dans le tableau ci-dessous,
ContributioncibleContributionetContributionFormulacibleContributionFormula(cf. encadré en tête).
| Modèle | Statusable | Dateable | Priceable | Categorizable | Humanizable | Roleable | Versionable | SoftDeletable | EmailNormalizable |
|---|---|---|---|---|---|---|---|---|---|
| Contribution | oui | oui | - | - | - | - | - | - | - |
| AttendanceList | oui | oui | - | - | - | - | - | - | - |
| AccountClaim | oui | oui | - | - | - | - | - | - | - |
| Person | - | oui | - | - | oui | - | - | oui | oui |
| NewsletterSubscriber | - | oui | - | - | - | - | - | - | oui |
| User | - | oui | - | - | - | oui | - | - | - |
| Payment | oui | oui | oui | - | oui | - | - | - | - |
| PaymentLine | - | - | oui | - | - | - | - | - | - |
| Event | - | oui | - | oui | - | - | - | - | - |
| Attendance | - | oui | - | - | - | - | - | - | - |
| ContributionFormula | - | - | oui | - | oui | - | oui | - | - |
| MembershipType | - | - | oui | oui | oui | - | oui | - | - |
| Membership | oui | oui | - | - | - | - | - | - | - |
- Tout modèle avec
enum :statusdoit inclureStatusable. - Tout modèle avec colonnes datetime métier doit considérer
Dateable. - Le pattern soft-delete passe par
SoftDeletable(et non plus des helpers ad-hoc).
Cadre de stabilité / risque pour prioriser les tests. Pour les priorités actives en flux, se référer à ../internal/todo.md.
- Zone 1 (stable) — comportement défini, tests immédiats requis.
- Zone 2 (en cours) — logique en évolution, tests après stabilisation.
- Zone 3 (future ou hors-périmètre) — pas de tests prioritaires.
User, Person, Membership, Payment, PaymentLine, MembershipType, Contribution (cible : Contribution) — tous testés, à compléter selon les gaps.
ContributionFormula(cible :ContributionFormula) — pas de spec dédiée, critique pour le pricing, priorité haute.Event— partiel, ajouter edge cases.AccountClaim— workflow à couvrir.Attendance— logique quotidienne à couvrir.
MemberNumberHistory, PaymentAuditLog — tests d'audit, priorité basse.
AttendanceList— logique quotidienne à finaliser.UserService— usage incertain, à clarifier avant tests.
Blog, Tag, TagBlog (CMS basique), PriceCatalog, PriceEntry (tarification, non urgent), EventAttendee (legacy), Session (Rails system).
MemberManagementService, People::PaymentCreator, People::MembershipUpgrader. À maintenir.
Admin::PaymentsService, People::NewsletterSignup.
Web::UserRegistration, People::Register, People::PaymentUpdater, People::PaymentCanceller, People::PaymentRestorer, People::AttachUserToPerson, People::AccountLinker, UserManagement::UserDeleter, People::AccountMerger.
Les classes
EventManagement::EventCreator/Updater/Deleterexistent dansapp/services/event_management/mais sont orphelines : aucun contrôleur ne les appelle (CRUD inline dansAdmin::EventsController). Cleanup tracé dans../internal/todo.md.
People::PaymentRefund (à concevoir), services dans app/services/admin/operations/ (non utilisés, à auditer / nettoyer).
Voir controllers.md pour le détail. Résumé :
- Zone 1 — admin CRUD critiques (
Admin::Users,Admin::Memberships,Admin::Payments,Admin::Events,Admin::Dashboard) et auth/checkout publics (Sessions,Registrations,Checkout). - Zone 2 —
AccountClaims,Passwords,Admin::ContributionFormulas,Admin::MemberNumbers, autres admin CRUD standards. - Zone 3 —
Home,Pages,Eventspublic,Blogs,Contacts,Admin::Blogs,Admin::Attendances,Admin::AttendanceLists.
Liste héritée de MODEL_EVALUATION.md (snapshot 2025-01-31). À recroiser avec ../internal/todo.md avant action.
Membership#expired? regarde status, Contribution#expired? regarde expires_at. Les scopes suivent la même asymétrie. Risque : tests déroutants, confusion développeurs.
Action ciblée :
# Membership
def expired_by_date?
Date.current > ended_at
end
def expired_status?
status == "expired"
end
# Contribution : OK tel quelsessions_remaining est forcé à 0 par défaut, validé presence: true si has_session_limit?, presence: false si Pack 10, et la validation custom contredit le default. Tests fragiles.
Action ciblée :
# Migration
change_column_null :book_of_entries, :sessions_remaining, true
remove_column_default :book_of_entries, :sessions_remaining
# Validations
validates :sessions_remaining, presence: true,
numericality: { greater_than: 0 },
if: :has_session_limit?
validates :sessions_remaining, absence: true,
if: -> { subscription_plan&.duration.in?(["trimester", "annual"]) }upgrade_to! crée la nouvelle Membership sans désactiver l'ancienne immédiatement, ce qui forcerait une no_overlapping_active_memberships. Le contournement actuel est skip_overlap_validation répandu dans le code.
Action ciblée : inactiver l'ancienne dans la même transaction, puis créer la nouvelle, puis retirer skip_overlap_validation.
add_index :payments, [:status, :created_at], name: "idx_payments_status_created"
add_index :memberships, [:membership_type_id, :status], name: "idx_memberships_type_status"
add_index :payment_lines, :amount_cents, name: "idx_payment_lines_amount"
add_index :book_of_entries, [:person_id, :status, :expires_at], name: "idx_boe_person_status_exp"
add_index :subscription_plans, [:membership_type_id, :duration], name: "idx_sub_plans_type_duration"Impact attendu : dashboards admin plus rapides, scalabilité accrue.
Pack 10 force expires_at à Time.current + 100.years faute d'accepter NULL. Conséquence : queries WHERE expires_at < ? partout, factories complexes.
Action ciblée : autoriser NULL pour Pack 10 ou table séparée pour les packs « illimités ». À discuter en équipe.
Trade-off accepté (flexibilité > intégrité référentielle) mais à garder en tête lors des migrations. Voir ../payments.md pour la dette item_type:"Payment" sur les dons.
Legacy non supprimé. Le module Newsletter a son propre modèle NewsletterSubscriber. Action : retirer l'attribut sur Person ou clarifier son rôle (drapeau de consentement, par ex.).
overview.md— architecture générale Person/User, RGPD, ViewComponents.services.md— catalogue des servicesPeople::*.controllers.md— état des contrôleurs.../domain/business_logic.md— règles métier.../development/testing.md— guide tests.