colibri-informatique / volunteering-engine
Framework-agnostic volunteering and skills-based volunteering domain engine
Package info
gitlab.com/colibri-informatique/volunteering-engine
pkg:composer/colibri-informatique/volunteering-engine
Requires
- php: ^8.3
- symfony/uid: ^7.4
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.80
- pestphp/pest: ^4.7
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.3
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-09-11 19:58:03 UTC
README
volunteering-engine est le cœur métier d'une plateforme de bénévolat et de mécénat de compétences.
Le projet ne dépend d'aucun framework PHP (Laravel, Symfony, Silverstripe, WordPress…) et ne contient aucune logique liée :
- à une base de données ;
- à un ORM ;
- à HTTP ;
- à une interface d'administration ;
- à un moteur PDF ;
- à un système de notifications ;
- au stockage de fichiers.
Sa responsabilité est de porter les règles métier du domaine : cycle de vie des missions, candidatures, workflow de validation et documents associés. L'objectif est d'obtenir un moteur testable, réutilisable et totalement indépendant de toute technologie d'infrastructure.
Fonctionnalités
Le moteur couvre actuellement :
- gestion du cycle de vie des missions (brouillon → soumission → publication → dépublication → archivage) ;
- gestion des associations (création, activation/désactivation, référent) ;
- candidatures aux missions et leur cycle de vie complet ;
- acceptation et refus des candidatures par l'association ;
- bénévolat hors temps de travail et mécénat de compétences sur temps de travail ;
- workflow de validation multi-acteurs porté par les documents (et non par la participation) ;
- lettre de mission et attestation de fin de mission ;
- événements métier émis par les agrégats.
La génération effective des fichiers (PDF ou autre), l'envoi des emails, les notifications et le stockage des documents sont laissés à la charge de l'application hôte.
Philosophie
Le projet suit une architecture inspirée du Domain Driven Design (DDD) et de la Clean Architecture :
- modèles métier riches (pas d'anemic model) ;
- Value Objects plutôt que tableaux ou types primitifs ;
- états explicites et transitions contrôlées (pas de mise à jour libre d'un statut) ;
- règles métier centralisées dans les agrégats, jamais dans les services applicatifs ;
- événements métier plutôt que couplage direct à l'infrastructure ;
- séparation stricte entre domaine et infrastructure ;
- dépendances toujours orientées vers le domaine.
Architecture
src/
├── Application/
│ └── Services/ # orchestration : charger, appliquer, sauvegarder, émettre les événements
└── Domain/
├── Enums/ # statuts et types métier
├── Exceptions/ # exceptions métier (une par violation d'invariant)
├── Models/ # agrégats : Mission, Participation, Document, Association, Event
├── Policies/ # règles transverses (ex. éligibilité à candidater)
├── Repositories/ # interfaces, implémentées par l'application hôte
└── ValueObjects/ # Identifier, Audience, Validation
Les interfaces de repository appartiennent au domaine ; leurs implémentations (MariaDB, PostgreSQL, en mémoire pour les tests, etc.) sont fournies par l'application hôte.
Concepts métier
Association
Une Association porte les missions qu'elle propose. Elle a un nom, un référent (managerId) et un statut actif/inactif.
Mission
Une Mission représente une opportunité de bénévolat ou de mécénat de compétences proposée par une association.
Cycle de vie :
Draft ──submit()──> Submitted ──publish()──> Published ──unpublish()──> Unpublished ──republish()──> Published
│ │ │
└──reject(reason)──> Draft (archive() possible depuis tout état non terminal)
- Une mission refusée repasse en
Draftavec le motif conservé. - Une mission publiée ne peut plus être supprimée.
- La validation/le refus d'une mission soumise sont effectués par l'organisme validateur porté par l'application hôte (le moteur ne modélise pas qui valide, seulement que la mission est validée ou refusée).
Participation
Une Participation représente la candidature d'un collaborateur (employeeId) à une Mission.
Cycle de vie :
Requested ──acceptByAssociation()──> Waiting ──(validations complètes)──> Ongoing ──complete()──> Completed ──evaluate()──> Evaluated
│ │
├──refuseByAssociation()──> Refused └──cancelByEmployee()──> Cancelled
└──cancelByEmployee()──> Cancelled
Deux modes d'engagement, portés par le booléen outsideWorkingHours :
outsideWorkingHours = true → bénévolat hors temps de travail (validation : Association + Employee)
outsideWorkingHours = false → mécénat de compétences sur temps de travail (validation : Association + Employee + Company)
Document
Un Document représente un acte documentaire métier attaché à une Participation. Deux types existent aujourd'hui (DocumentType) :
MissionLetter → lettre de mission, workflow multi-acteurs (Association, Employee, Company selon le mode)
MissionCertificate → attestation de fin de mission, signataire unique : Association
Le contenu d'un document est figé à sa création — aucun setter n'existe pour le modifier après coup. La génération du fichier physique (PDF ou autre) reste hors périmètre du moteur.
Validation
Chaque Document porte ses propres Validation (une par rôle attendu). Une validation est Requested, Approved ou Refused. Le moteur ne parle jamais de « signature » : c'est un choix délibéré pour rester neutre vis-à-vis du mécanisme de signature électronique utilisé par l'application hôte.
Les validations appartiennent au document, pas à la participation — ce choix permet d'ajouter de nouveaux documents avec des workflows différents sans modifier Participation.
Rôles (Audience)
Admin → back-office / supervision
Association → responsable de l'association porteuse de la mission
Company → référent côté entreprise (mécénat de compétences)
Employee → collaborateur candidat
Ces rôles sont génériques : ils ne présupposent pas d'organigramme particulier côté application hôte.
Événements métier
Les agrégats produisent des événements lorsqu'une action a une valeur métier. Catalogue actuel (EventType) :
mission.created, mission.submitted, mission.published, mission.rejected,
mission.unpublished, mission.archived
participation.requested, participation.accepted.association,
participation.accepted.employee, participation.accepted.company,
participation.ongoing, participation.completed, participation.evaluated,
participation.refused, participation.cancelled
validation.requested, validation.refused
document.signed, document.completed
notification.sent
Le moteur n'utilise pas l'Event Sourcing : les événements ne constituent jamais la source de vérité, ils servent à déclencher des effets de bord côté application hôte (notifications, timeline, statistiques, intégrations).
EventRepository
Les événements collectés par un agrégat sont récupérés par le service applicatif (release()) puis transmis à un EventRepository :
interface EventRepository
{
public function record(Event $event): void;
/** @return Event[] */
public function release(): array;
}
L'implémentation (persistance, dispatch Laravel, no-op en mémoire pour les tests…) dépend entièrement de l'application hôte.
Services applicatifs
Les services actuellement fournis :
MissionService # création, soumission, publication, refus, dépublication, archivage, édition, suppression
ParticipationService # candidature, acceptation/refus par association/employee/company, complétion, attestation, annulation
AssociationService # création, activation/désactivation, changement de référent
Un service suit systématiquement le même cycle : charger l'agrégat → appliquer une opération métier → sauvegarder → récupérer les événements produits → les transmettre à l'EventRepository.
Ce que le moteur ne fait volontairement pas
Le moteur ne contient aucune logique :
- SQL, HTTP, REST, GraphQL, ORM ;
- PDF, email, notification ;
- stockage de fichiers ;
- interface graphique ;
- authentification / SSO ;
- synchronisation SIRH ;
- administration.
Toutes ces responsabilités appartiennent aux couches d'application et aux adapters de l'application hôte.
Installation
composer require colibri-informatique/volunteering-engine
Développement
composer install # installation
composer test # tests (Pest)
composer phpstan # analyse statique (niveau 5)
composer lint # formatage (PHP CS Fixer)
composer lint:check # vérification sans modification
Intégration
Le moteur est conçu pour être intégré dans n'importe quelle application PHP (Laravel, Symfony, Silverstripe, WordPress, ou autre), à condition de fournir :
- une implémentation par repository (
MissionRepository,ParticipationRepository,DocumentRepository,AssociationRepository,EventRepository) ; - les adapters nécessaires (génération de fichiers, notifications, stockage, SSO…).
Documentation complémentaire
docs/architecture.md # décisions d'architecture détaillées
docs/workflows.md # diagrammes des workflows (Mermaid)
Roadmap
- [x] Modèle métier Mission
- [x] Modèle métier Participation
- [x] Modèle métier Association
- [x] Workflow documentaire (lettre de mission, attestation)
- [x] Événements métier
- [x] Tests unitaires du domaine
- [ ]
DocumentService(orchestration de la génération documentaire) - [ ] Use Cases applicatifs (parcours complets orchestrant plusieurs services)
- [ ] Implémentations de référence des repositories (adapters Laravel/Doctrine)
Licence
MIT