[{"data":1,"prerenderedAt":1502},["ShallowReactive",2],{"content-query-69azE1ZqXB":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"tags":10,"createdAt":14,"readingTime":15,"body":20,"_type":1495,"_id":1496,"_source":1497,"_file":1498,"_stem":1499,"_extension":1500,"sitemap":1501},"/blog/2026/11-stateless-services","2026",false,"","Services stateless","Il n'est pas rare pour un développeur de s'être retrouvé face à un bug compliqué à reproduire : une valeur qui n'a rien à faire là, un résultat qui dépend de ce qui s'est exécuté juste avant. Très souvent, la cause est la même : un service qui garde de l'état entre deux appels. Le problème, c'est que ce genre de service fonctionne bien... tant qu'il n'est appelé qu'une fois. C'est précisément ce que cherche à éviter le service statelessness principle.",[11,12,13],"architecture","symfony","php","206-11-12",{"text":16,"minutes":17,"time":18,"words":19},"4 min read",3.075,184500,615,{"type":21,"children":22,"toc":1487},"root",[23,42,49,54,59,66,540,561,567,856,869,874,880,894,899,1147,1160,1165,1470,1476,1481],{"type":24,"tag":25,"props":26,"children":27},"element","p",{},[28,31,40],{"type":29,"value":30},"text","Il n'est pas rare pour un développeur de s'être retrouvé face à un bug compliqué à reproduire : une valeur qui n'a rien à faire là, un résultat qui dépend de ce qui s'est exécuté juste avant. Très souvent, la cause est la même : un service qui garde de l'état entre deux appels. Le problème, c'est que ce genre de service fonctionne bien... tant qu'il n'est appelé qu'une fois. C'est précisément ce que cherche à éviter le ",{"type":24,"tag":32,"props":33,"children":37},"a",{"href":34,"rel":35},"https://en.wikipedia.org/wiki/Service_statelessness_principle",[36],"nofollow",[38],{"type":29,"value":39},"service statelessness principle",{"type":29,"value":41},".",{"type":24,"tag":43,"props":44,"children":46},"h2",{"id":45},"service-stateless-cest-quoi",[47],{"type":29,"value":48},"Service stateless : c'est quoi ?",{"type":24,"tag":25,"props":50,"children":51},{},[52],{"type":29,"value":53},"Un service stateless ne retient rien entre deux appels. Pas de donnée de requête, pas d'utilisateur courant, pas de résultat intermédiaire stocké dans une propriété. Tout ce dont il a besoin arrive en paramètre, tout ce qu'il produit repart en retour.",{"type":24,"tag":25,"props":55,"children":56},{},[57],{"type":29,"value":58},"Ses dépendances (repository, logger, ...) ne sont injectées qu'une seule fois et ne changent pas. Ce qu'on évite, c'est l'état mutable lié à un contexte.",{"type":24,"tag":60,"props":61,"children":63},"h3",{"id":62},"exemple-dun-service-conservant-des-états",[64],{"type":29,"value":65},"Exemple d'un service conservant des états",{"type":24,"tag":67,"props":68,"children":71},"pre",{"className":69,"code":70,"language":13,"meta":7,"style":7},"language-php shiki shiki-themes rose-pine-dawn","final class EventImporter\n{\n    private array $errors = [];\n    private ?Organizer $organizer = null;\n\n    public function setOrganizer(Organizer $organizer): void\n    {\n        $this->organizer = $organizer;\n    }\n\n    public function import(array $rows): void\n    {\n        foreach ($rows as $row) {\n            // ... utilise $this->organizer, remplit $this->errors\n        }\n    }\n\n    public function getErrors(): array\n    {\n        return $this->errors;\n    }\n}\n",[72],{"type":24,"tag":73,"props":74,"children":75},"code",{"__ignoreMap":7},[76,99,109,145,187,197,248,257,297,306,314,361,369,415,430,439,447,455,486,494,523,531],{"type":24,"tag":77,"props":78,"children":81},"span",{"class":79,"line":80},"line",1,[82,88,93],{"type":24,"tag":77,"props":83,"children":85},{"style":84},"--shiki-default:#286983",[86],{"type":29,"value":87},"final",{"type":24,"tag":77,"props":89,"children":90},{"style":84},[91],{"type":29,"value":92}," class",{"type":24,"tag":77,"props":94,"children":96},{"style":95},"--shiki-default:#56949F",[97],{"type":29,"value":98}," EventImporter\n",{"type":24,"tag":77,"props":100,"children":102},{"class":79,"line":101},2,[103],{"type":24,"tag":77,"props":104,"children":106},{"style":105},"--shiki-default:#797593",[107],{"type":29,"value":108},"{\n",{"type":24,"tag":77,"props":110,"children":112},{"class":79,"line":111},3,[113,118,123,129,135,140],{"type":24,"tag":77,"props":114,"children":115},{"style":84},[116],{"type":29,"value":117},"    private",{"type":24,"tag":77,"props":119,"children":120},{"style":84},[121],{"type":29,"value":122}," array",{"type":24,"tag":77,"props":124,"children":126},{"style":125},"--shiki-default:#797593;--shiki-default-font-style:italic",[127],{"type":29,"value":128}," $",{"type":24,"tag":77,"props":130,"children":132},{"style":131},"--shiki-default:#575279;--shiki-default-font-style:italic",[133],{"type":29,"value":134},"errors",{"type":24,"tag":77,"props":136,"children":137},{"style":84},[138],{"type":29,"value":139}," =",{"type":24,"tag":77,"props":141,"children":142},{"style":105},[143],{"type":29,"value":144}," [];\n",{"type":24,"tag":77,"props":146,"children":148},{"class":79,"line":147},4,[149,153,158,163,167,172,176,182],{"type":24,"tag":77,"props":150,"children":151},{"style":84},[152],{"type":29,"value":117},{"type":24,"tag":77,"props":154,"children":155},{"style":84},[156],{"type":29,"value":157}," ?",{"type":24,"tag":77,"props":159,"children":160},{"style":95},[161],{"type":29,"value":162},"Organizer",{"type":24,"tag":77,"props":164,"children":165},{"style":125},[166],{"type":29,"value":128},{"type":24,"tag":77,"props":168,"children":169},{"style":131},[170],{"type":29,"value":171},"organizer",{"type":24,"tag":77,"props":173,"children":174},{"style":84},[175],{"type":29,"value":139},{"type":24,"tag":77,"props":177,"children":179},{"style":178},"--shiki-default:#D7827E",[180],{"type":29,"value":181}," null",{"type":24,"tag":77,"props":183,"children":184},{"style":105},[185],{"type":29,"value":186},";\n",{"type":24,"tag":77,"props":188,"children":190},{"class":79,"line":189},5,[191],{"type":24,"tag":77,"props":192,"children":194},{"emptyLinePlaceholder":193},true,[195],{"type":29,"value":196},"\n",{"type":24,"tag":77,"props":198,"children":200},{"class":79,"line":199},6,[201,206,211,216,221,225,229,233,238,243],{"type":24,"tag":77,"props":202,"children":203},{"style":84},[204],{"type":29,"value":205},"    public",{"type":24,"tag":77,"props":207,"children":208},{"style":84},[209],{"type":29,"value":210}," function",{"type":24,"tag":77,"props":212,"children":213},{"style":178},[214],{"type":29,"value":215}," setOrganizer",{"type":24,"tag":77,"props":217,"children":218},{"style":105},[219],{"type":29,"value":220},"(",{"type":24,"tag":77,"props":222,"children":223},{"style":95},[224],{"type":29,"value":162},{"type":24,"tag":77,"props":226,"children":227},{"style":125},[228],{"type":29,"value":128},{"type":24,"tag":77,"props":230,"children":231},{"style":131},[232],{"type":29,"value":171},{"type":24,"tag":77,"props":234,"children":235},{"style":105},[236],{"type":29,"value":237},")",{"type":24,"tag":77,"props":239,"children":240},{"style":84},[241],{"type":29,"value":242},":",{"type":24,"tag":77,"props":244,"children":245},{"style":84},[246],{"type":29,"value":247}," void\n",{"type":24,"tag":77,"props":249,"children":251},{"class":79,"line":250},7,[252],{"type":24,"tag":77,"props":253,"children":254},{"style":105},[255],{"type":29,"value":256},"    {\n",{"type":24,"tag":77,"props":258,"children":260},{"class":79,"line":259},8,[261,266,272,277,281,285,289,293],{"type":24,"tag":77,"props":262,"children":263},{"style":125},[264],{"type":29,"value":265},"        $",{"type":24,"tag":77,"props":267,"children":269},{"style":268},"--shiki-default:#286983;--shiki-default-font-style:italic",[270],{"type":29,"value":271},"this",{"type":24,"tag":77,"props":273,"children":274},{"style":84},[275],{"type":29,"value":276},"->",{"type":24,"tag":77,"props":278,"children":279},{"style":131},[280],{"type":29,"value":171},{"type":24,"tag":77,"props":282,"children":283},{"style":84},[284],{"type":29,"value":139},{"type":24,"tag":77,"props":286,"children":287},{"style":125},[288],{"type":29,"value":128},{"type":24,"tag":77,"props":290,"children":291},{"style":131},[292],{"type":29,"value":171},{"type":24,"tag":77,"props":294,"children":295},{"style":105},[296],{"type":29,"value":186},{"type":24,"tag":77,"props":298,"children":300},{"class":79,"line":299},9,[301],{"type":24,"tag":77,"props":302,"children":303},{"style":105},[304],{"type":29,"value":305},"    }\n",{"type":24,"tag":77,"props":307,"children":309},{"class":79,"line":308},10,[310],{"type":24,"tag":77,"props":311,"children":312},{"emptyLinePlaceholder":193},[313],{"type":29,"value":196},{"type":24,"tag":77,"props":315,"children":317},{"class":79,"line":316},11,[318,322,326,331,335,340,344,349,353,357],{"type":24,"tag":77,"props":319,"children":320},{"style":84},[321],{"type":29,"value":205},{"type":24,"tag":77,"props":323,"children":324},{"style":84},[325],{"type":29,"value":210},{"type":24,"tag":77,"props":327,"children":328},{"style":178},[329],{"type":29,"value":330}," import",{"type":24,"tag":77,"props":332,"children":333},{"style":105},[334],{"type":29,"value":220},{"type":24,"tag":77,"props":336,"children":337},{"style":84},[338],{"type":29,"value":339},"array",{"type":24,"tag":77,"props":341,"children":342},{"style":125},[343],{"type":29,"value":128},{"type":24,"tag":77,"props":345,"children":346},{"style":131},[347],{"type":29,"value":348},"rows",{"type":24,"tag":77,"props":350,"children":351},{"style":105},[352],{"type":29,"value":237},{"type":24,"tag":77,"props":354,"children":355},{"style":84},[356],{"type":29,"value":242},{"type":24,"tag":77,"props":358,"children":359},{"style":84},[360],{"type":29,"value":247},{"type":24,"tag":77,"props":362,"children":364},{"class":79,"line":363},12,[365],{"type":24,"tag":77,"props":366,"children":367},{"style":105},[368],{"type":29,"value":256},{"type":24,"tag":77,"props":370,"children":372},{"class":79,"line":371},13,[373,378,383,388,392,397,401,406,410],{"type":24,"tag":77,"props":374,"children":375},{"style":84},[376],{"type":29,"value":377},"        foreach",{"type":24,"tag":77,"props":379,"children":380},{"style":105},[381],{"type":29,"value":382}," (",{"type":24,"tag":77,"props":384,"children":385},{"style":125},[386],{"type":29,"value":387},"$",{"type":24,"tag":77,"props":389,"children":390},{"style":131},[391],{"type":29,"value":348},{"type":24,"tag":77,"props":393,"children":394},{"style":84},[395],{"type":29,"value":396}," as",{"type":24,"tag":77,"props":398,"children":399},{"style":125},[400],{"type":29,"value":128},{"type":24,"tag":77,"props":402,"children":403},{"style":131},[404],{"type":29,"value":405},"row",{"type":24,"tag":77,"props":407,"children":408},{"style":105},[409],{"type":29,"value":237},{"type":24,"tag":77,"props":411,"children":412},{"style":105},[413],{"type":29,"value":414}," {\n",{"type":24,"tag":77,"props":416,"children":418},{"class":79,"line":417},14,[419,424],{"type":24,"tag":77,"props":420,"children":421},{"style":125},[422],{"type":29,"value":423},"            //",{"type":24,"tag":77,"props":425,"children":427},{"style":426},"--shiki-default:#9893A5;--shiki-default-font-style:italic",[428],{"type":29,"value":429}," ... utilise $this->organizer, remplit $this->errors\n",{"type":24,"tag":77,"props":431,"children":433},{"class":79,"line":432},15,[434],{"type":24,"tag":77,"props":435,"children":436},{"style":105},[437],{"type":29,"value":438},"        }\n",{"type":24,"tag":77,"props":440,"children":442},{"class":79,"line":441},16,[443],{"type":24,"tag":77,"props":444,"children":445},{"style":105},[446],{"type":29,"value":305},{"type":24,"tag":77,"props":448,"children":450},{"class":79,"line":449},17,[451],{"type":24,"tag":77,"props":452,"children":453},{"emptyLinePlaceholder":193},[454],{"type":29,"value":196},{"type":24,"tag":77,"props":456,"children":458},{"class":79,"line":457},18,[459,463,467,472,477,481],{"type":24,"tag":77,"props":460,"children":461},{"style":84},[462],{"type":29,"value":205},{"type":24,"tag":77,"props":464,"children":465},{"style":84},[466],{"type":29,"value":210},{"type":24,"tag":77,"props":468,"children":469},{"style":178},[470],{"type":29,"value":471}," getErrors",{"type":24,"tag":77,"props":473,"children":474},{"style":105},[475],{"type":29,"value":476},"()",{"type":24,"tag":77,"props":478,"children":479},{"style":84},[480],{"type":29,"value":242},{"type":24,"tag":77,"props":482,"children":483},{"style":84},[484],{"type":29,"value":485}," array\n",{"type":24,"tag":77,"props":487,"children":489},{"class":79,"line":488},19,[490],{"type":24,"tag":77,"props":491,"children":492},{"style":105},[493],{"type":29,"value":256},{"type":24,"tag":77,"props":495,"children":497},{"class":79,"line":496},20,[498,503,507,511,515,519],{"type":24,"tag":77,"props":499,"children":500},{"style":84},[501],{"type":29,"value":502},"        return",{"type":24,"tag":77,"props":504,"children":505},{"style":125},[506],{"type":29,"value":128},{"type":24,"tag":77,"props":508,"children":509},{"style":268},[510],{"type":29,"value":271},{"type":24,"tag":77,"props":512,"children":513},{"style":84},[514],{"type":29,"value":276},{"type":24,"tag":77,"props":516,"children":517},{"style":131},[518],{"type":29,"value":134},{"type":24,"tag":77,"props":520,"children":521},{"style":105},[522],{"type":29,"value":186},{"type":24,"tag":77,"props":524,"children":526},{"class":79,"line":525},21,[527],{"type":24,"tag":77,"props":528,"children":529},{"style":105},[530],{"type":29,"value":305},{"type":24,"tag":77,"props":532,"children":534},{"class":79,"line":533},22,[535],{"type":24,"tag":77,"props":536,"children":537},{"style":105},[538],{"type":29,"value":539},"}\n",{"type":24,"tag":25,"props":541,"children":542},{},[543,545,551,553,559],{"type":29,"value":544},"Dans le conteneur, ce service est partagé : une seule instance pour toute l'application. Appelé deux fois dans la même exécution, ",{"type":24,"tag":73,"props":546,"children":548},{"className":547},[],[549],{"type":29,"value":550},"$errors",{"type":29,"value":552}," et ",{"type":24,"tag":73,"props":554,"children":556},{"className":555},[],[557],{"type":29,"value":558},"$organizer",{"type":29,"value":560}," du premier appel vont polluer le second. Et dès que du code long tourne (workers Messenger, ...), l'état survit entre les traitements. Bugs aléatoires garantis.",{"type":24,"tag":60,"props":562,"children":564},{"id":563},"la-version-corrigée",[565],{"type":29,"value":566},"La version corrigée",{"type":24,"tag":67,"props":568,"children":570},{"className":69,"code":569,"language":13,"meta":7,"style":7},"final readonly class EventImporter\n{\n    public function __construct(\n        private EventRepository $events\n    ) {}\n\n    public function import(Organizer $organizer, array $rows): array\n    {\n        $errors = [];\n\n        foreach ($rows as $row) {\n            // ... utilise $organizer, remplit $errors\n        }\n\n        return $errors;\n    }\n}\n",[571],{"type":24,"tag":73,"props":572,"children":573},{"__ignoreMap":7},[574,594,601,623,645,658,665,725,732,751,758,797,809,816,823,842,849],{"type":24,"tag":77,"props":575,"children":576},{"class":79,"line":80},[577,581,586,590],{"type":24,"tag":77,"props":578,"children":579},{"style":84},[580],{"type":29,"value":87},{"type":24,"tag":77,"props":582,"children":583},{"style":84},[584],{"type":29,"value":585}," readonly",{"type":24,"tag":77,"props":587,"children":588},{"style":84},[589],{"type":29,"value":92},{"type":24,"tag":77,"props":591,"children":592},{"style":95},[593],{"type":29,"value":98},{"type":24,"tag":77,"props":595,"children":596},{"class":79,"line":101},[597],{"type":24,"tag":77,"props":598,"children":599},{"style":105},[600],{"type":29,"value":108},{"type":24,"tag":77,"props":602,"children":603},{"class":79,"line":111},[604,608,612,618],{"type":24,"tag":77,"props":605,"children":606},{"style":84},[607],{"type":29,"value":205},{"type":24,"tag":77,"props":609,"children":610},{"style":84},[611],{"type":29,"value":210},{"type":24,"tag":77,"props":613,"children":615},{"style":614},"--shiki-default:#B4637A;--shiki-default-font-style:italic",[616],{"type":29,"value":617}," __construct",{"type":24,"tag":77,"props":619,"children":620},{"style":105},[621],{"type":29,"value":622},"(\n",{"type":24,"tag":77,"props":624,"children":625},{"class":79,"line":147},[626,631,636,640],{"type":24,"tag":77,"props":627,"children":628},{"style":84},[629],{"type":29,"value":630},"        private",{"type":24,"tag":77,"props":632,"children":633},{"style":95},[634],{"type":29,"value":635}," EventRepository",{"type":24,"tag":77,"props":637,"children":638},{"style":125},[639],{"type":29,"value":128},{"type":24,"tag":77,"props":641,"children":642},{"style":131},[643],{"type":29,"value":644},"events\n",{"type":24,"tag":77,"props":646,"children":647},{"class":79,"line":189},[648,653],{"type":24,"tag":77,"props":649,"children":650},{"style":105},[651],{"type":29,"value":652},"    )",{"type":24,"tag":77,"props":654,"children":655},{"style":105},[656],{"type":29,"value":657}," {}\n",{"type":24,"tag":77,"props":659,"children":660},{"class":79,"line":199},[661],{"type":24,"tag":77,"props":662,"children":663},{"emptyLinePlaceholder":193},[664],{"type":29,"value":196},{"type":24,"tag":77,"props":666,"children":667},{"class":79,"line":250},[668,672,676,680,684,688,692,696,701,705,709,713,717,721],{"type":24,"tag":77,"props":669,"children":670},{"style":84},[671],{"type":29,"value":205},{"type":24,"tag":77,"props":673,"children":674},{"style":84},[675],{"type":29,"value":210},{"type":24,"tag":77,"props":677,"children":678},{"style":178},[679],{"type":29,"value":330},{"type":24,"tag":77,"props":681,"children":682},{"style":105},[683],{"type":29,"value":220},{"type":24,"tag":77,"props":685,"children":686},{"style":95},[687],{"type":29,"value":162},{"type":24,"tag":77,"props":689,"children":690},{"style":125},[691],{"type":29,"value":128},{"type":24,"tag":77,"props":693,"children":694},{"style":131},[695],{"type":29,"value":171},{"type":24,"tag":77,"props":697,"children":698},{"style":105},[699],{"type":29,"value":700},",",{"type":24,"tag":77,"props":702,"children":703},{"style":84},[704],{"type":29,"value":122},{"type":24,"tag":77,"props":706,"children":707},{"style":125},[708],{"type":29,"value":128},{"type":24,"tag":77,"props":710,"children":711},{"style":131},[712],{"type":29,"value":348},{"type":24,"tag":77,"props":714,"children":715},{"style":105},[716],{"type":29,"value":237},{"type":24,"tag":77,"props":718,"children":719},{"style":84},[720],{"type":29,"value":242},{"type":24,"tag":77,"props":722,"children":723},{"style":84},[724],{"type":29,"value":485},{"type":24,"tag":77,"props":726,"children":727},{"class":79,"line":259},[728],{"type":24,"tag":77,"props":729,"children":730},{"style":105},[731],{"type":29,"value":256},{"type":24,"tag":77,"props":733,"children":734},{"class":79,"line":299},[735,739,743,747],{"type":24,"tag":77,"props":736,"children":737},{"style":125},[738],{"type":29,"value":265},{"type":24,"tag":77,"props":740,"children":741},{"style":131},[742],{"type":29,"value":134},{"type":24,"tag":77,"props":744,"children":745},{"style":84},[746],{"type":29,"value":139},{"type":24,"tag":77,"props":748,"children":749},{"style":105},[750],{"type":29,"value":144},{"type":24,"tag":77,"props":752,"children":753},{"class":79,"line":308},[754],{"type":24,"tag":77,"props":755,"children":756},{"emptyLinePlaceholder":193},[757],{"type":29,"value":196},{"type":24,"tag":77,"props":759,"children":760},{"class":79,"line":316},[761,765,769,773,777,781,785,789,793],{"type":24,"tag":77,"props":762,"children":763},{"style":84},[764],{"type":29,"value":377},{"type":24,"tag":77,"props":766,"children":767},{"style":105},[768],{"type":29,"value":382},{"type":24,"tag":77,"props":770,"children":771},{"style":125},[772],{"type":29,"value":387},{"type":24,"tag":77,"props":774,"children":775},{"style":131},[776],{"type":29,"value":348},{"type":24,"tag":77,"props":778,"children":779},{"style":84},[780],{"type":29,"value":396},{"type":24,"tag":77,"props":782,"children":783},{"style":125},[784],{"type":29,"value":128},{"type":24,"tag":77,"props":786,"children":787},{"style":131},[788],{"type":29,"value":405},{"type":24,"tag":77,"props":790,"children":791},{"style":105},[792],{"type":29,"value":237},{"type":24,"tag":77,"props":794,"children":795},{"style":105},[796],{"type":29,"value":414},{"type":24,"tag":77,"props":798,"children":799},{"class":79,"line":363},[800,804],{"type":24,"tag":77,"props":801,"children":802},{"style":125},[803],{"type":29,"value":423},{"type":24,"tag":77,"props":805,"children":806},{"style":426},[807],{"type":29,"value":808}," ... utilise $organizer, remplit $errors\n",{"type":24,"tag":77,"props":810,"children":811},{"class":79,"line":371},[812],{"type":24,"tag":77,"props":813,"children":814},{"style":105},[815],{"type":29,"value":438},{"type":24,"tag":77,"props":817,"children":818},{"class":79,"line":417},[819],{"type":24,"tag":77,"props":820,"children":821},{"emptyLinePlaceholder":193},[822],{"type":29,"value":196},{"type":24,"tag":77,"props":824,"children":825},{"class":79,"line":432},[826,830,834,838],{"type":24,"tag":77,"props":827,"children":828},{"style":84},[829],{"type":29,"value":502},{"type":24,"tag":77,"props":831,"children":832},{"style":125},[833],{"type":29,"value":128},{"type":24,"tag":77,"props":835,"children":836},{"style":131},[837],{"type":29,"value":134},{"type":24,"tag":77,"props":839,"children":840},{"style":105},[841],{"type":29,"value":186},{"type":24,"tag":77,"props":843,"children":844},{"class":79,"line":441},[845],{"type":24,"tag":77,"props":846,"children":847},{"style":105},[848],{"type":29,"value":305},{"type":24,"tag":77,"props":850,"children":851},{"class":79,"line":449},[852],{"type":24,"tag":77,"props":853,"children":854},{"style":105},[855],{"type":29,"value":539},{"type":24,"tag":25,"props":857,"children":858},{},[859,861,867],{"type":29,"value":860},"Plus aucune propriété n'est mutable, et c'est imposé par le ",{"type":24,"tag":73,"props":862,"children":864},{"className":863},[],[865],{"type":29,"value":866},"readonly",{"type":29,"value":868}," sur la classe.",{"type":24,"tag":25,"props":870,"children":871},{},[872],{"type":29,"value":873},"Pour remplacer l'état, on peut passer par des DTO en entrée pour les paramètres, ou en sortie pour les résultats, particulièrement si le nombre de variables est important.",{"type":24,"tag":43,"props":875,"children":877},{"id":876},"dto-popo-des-objets-simples-pour-stocker-les-états",[878],{"type":29,"value":879},"DTO / POPO : des objets simples pour stocker les états",{"type":24,"tag":25,"props":881,"children":882},{},[883,885,892],{"type":29,"value":884},"Un POPO (Plain Old PHP Object) est un objet PHP tout simple, sans héritage ni dépendance au framework. Un DTO (",{"type":24,"tag":32,"props":886,"children":889},{"href":887,"rel":888},"https://fr.wikipedia.org/wiki/Objet_de_transfert_de_donn%C3%A9es",[36],[890],{"type":29,"value":891},"Data Transfer Object",{"type":29,"value":893},") est un POPO dont le seul boulot est de transporter des données d'un point à un autre. Pas de logique métier, pas de services dedans.",{"type":24,"tag":25,"props":895,"children":896},{},[897],{"type":29,"value":898},"Côté entrée, il regroupe les paramètres passés à la méthode. Reprenons l'import : l'ensemble des entrées devient un DTO.",{"type":24,"tag":67,"props":900,"children":902},{"className":69,"code":901,"language":13,"meta":7,"style":7},"use Symfony\\Component\\Validator\\Constraints as Assert;\n\nfinal readonly class ImportEventsDto\n{\n    /**\n     * @param list\u003Carray{name: string, startsAt: string}> $rows\n     */\n    public function __construct(\n        public Organizer $organizer,\n\n        public array $rows,\n    ) {}\n}\n",[903],{"type":24,"tag":73,"props":904,"children":905},{"__ignoreMap":7},[906,961,968,988,995,1003,1046,1054,1073,1099,1106,1129,1140],{"type":24,"tag":77,"props":907,"children":908},{"class":79,"line":80},[909,914,919,924,929,933,938,942,947,951,957],{"type":24,"tag":77,"props":910,"children":911},{"style":84},[912],{"type":29,"value":913},"use",{"type":24,"tag":77,"props":915,"children":916},{"style":95},[917],{"type":29,"value":918}," Symfony",{"type":24,"tag":77,"props":920,"children":921},{"style":105},[922],{"type":29,"value":923},"\\",{"type":24,"tag":77,"props":925,"children":926},{"style":95},[927],{"type":29,"value":928},"Component",{"type":24,"tag":77,"props":930,"children":931},{"style":105},[932],{"type":29,"value":923},{"type":24,"tag":77,"props":934,"children":935},{"style":95},[936],{"type":29,"value":937},"Validator",{"type":24,"tag":77,"props":939,"children":940},{"style":105},[941],{"type":29,"value":923},{"type":24,"tag":77,"props":943,"children":944},{"style":95},[945],{"type":29,"value":946},"Constraints",{"type":24,"tag":77,"props":948,"children":949},{"style":84},[950],{"type":29,"value":396},{"type":24,"tag":77,"props":952,"children":954},{"style":953},"--shiki-default:#575279",[955],{"type":29,"value":956}," Assert",{"type":24,"tag":77,"props":958,"children":959},{"style":105},[960],{"type":29,"value":186},{"type":24,"tag":77,"props":962,"children":963},{"class":79,"line":101},[964],{"type":24,"tag":77,"props":965,"children":966},{"emptyLinePlaceholder":193},[967],{"type":29,"value":196},{"type":24,"tag":77,"props":969,"children":970},{"class":79,"line":111},[971,975,979,983],{"type":24,"tag":77,"props":972,"children":973},{"style":84},[974],{"type":29,"value":87},{"type":24,"tag":77,"props":976,"children":977},{"style":84},[978],{"type":29,"value":585},{"type":24,"tag":77,"props":980,"children":981},{"style":84},[982],{"type":29,"value":92},{"type":24,"tag":77,"props":984,"children":985},{"style":95},[986],{"type":29,"value":987}," ImportEventsDto\n",{"type":24,"tag":77,"props":989,"children":990},{"class":79,"line":147},[991],{"type":24,"tag":77,"props":992,"children":993},{"style":105},[994],{"type":29,"value":108},{"type":24,"tag":77,"props":996,"children":997},{"class":79,"line":189},[998],{"type":24,"tag":77,"props":999,"children":1000},{"style":125},[1001],{"type":29,"value":1002},"    /**\n",{"type":24,"tag":77,"props":1004,"children":1005},{"class":79,"line":199},[1006,1011,1016,1022,1027,1031,1036,1041],{"type":24,"tag":77,"props":1007,"children":1008},{"style":426},[1009],{"type":29,"value":1010},"     * ",{"type":24,"tag":77,"props":1012,"children":1013},{"style":268},[1014],{"type":29,"value":1015},"@param",{"type":24,"tag":77,"props":1017,"children":1019},{"style":1018},"--shiki-default:#56949F;--shiki-default-font-style:italic",[1020],{"type":29,"value":1021}," list",{"type":24,"tag":77,"props":1023,"children":1024},{"style":426},[1025],{"type":29,"value":1026},"\u003C",{"type":24,"tag":77,"props":1028,"children":1029},{"style":268},[1030],{"type":29,"value":339},{"type":24,"tag":77,"props":1032,"children":1033},{"style":426},[1034],{"type":29,"value":1035},"{",{"type":24,"tag":77,"props":1037,"children":1038},{"style":1018},[1039],{"type":29,"value":1040},"name",{"type":24,"tag":77,"props":1042,"children":1043},{"style":426},[1044],{"type":29,"value":1045},": string, startsAt: string}> $rows\n",{"type":24,"tag":77,"props":1047,"children":1048},{"class":79,"line":250},[1049],{"type":24,"tag":77,"props":1050,"children":1051},{"style":125},[1052],{"type":29,"value":1053},"     */\n",{"type":24,"tag":77,"props":1055,"children":1056},{"class":79,"line":259},[1057,1061,1065,1069],{"type":24,"tag":77,"props":1058,"children":1059},{"style":84},[1060],{"type":29,"value":205},{"type":24,"tag":77,"props":1062,"children":1063},{"style":84},[1064],{"type":29,"value":210},{"type":24,"tag":77,"props":1066,"children":1067},{"style":614},[1068],{"type":29,"value":617},{"type":24,"tag":77,"props":1070,"children":1071},{"style":105},[1072],{"type":29,"value":622},{"type":24,"tag":77,"props":1074,"children":1075},{"class":79,"line":299},[1076,1081,1086,1090,1094],{"type":24,"tag":77,"props":1077,"children":1078},{"style":84},[1079],{"type":29,"value":1080},"        public",{"type":24,"tag":77,"props":1082,"children":1083},{"style":95},[1084],{"type":29,"value":1085}," Organizer",{"type":24,"tag":77,"props":1087,"children":1088},{"style":125},[1089],{"type":29,"value":128},{"type":24,"tag":77,"props":1091,"children":1092},{"style":131},[1093],{"type":29,"value":171},{"type":24,"tag":77,"props":1095,"children":1096},{"style":105},[1097],{"type":29,"value":1098},",\n",{"type":24,"tag":77,"props":1100,"children":1101},{"class":79,"line":308},[1102],{"type":24,"tag":77,"props":1103,"children":1104},{"emptyLinePlaceholder":193},[1105],{"type":29,"value":196},{"type":24,"tag":77,"props":1107,"children":1108},{"class":79,"line":316},[1109,1113,1117,1121,1125],{"type":24,"tag":77,"props":1110,"children":1111},{"style":84},[1112],{"type":29,"value":1080},{"type":24,"tag":77,"props":1114,"children":1115},{"style":84},[1116],{"type":29,"value":122},{"type":24,"tag":77,"props":1118,"children":1119},{"style":125},[1120],{"type":29,"value":128},{"type":24,"tag":77,"props":1122,"children":1123},{"style":131},[1124],{"type":29,"value":348},{"type":24,"tag":77,"props":1126,"children":1127},{"style":105},[1128],{"type":29,"value":1098},{"type":24,"tag":77,"props":1130,"children":1131},{"class":79,"line":363},[1132,1136],{"type":24,"tag":77,"props":1133,"children":1134},{"style":105},[1135],{"type":29,"value":652},{"type":24,"tag":77,"props":1137,"children":1138},{"style":105},[1139],{"type":29,"value":657},{"type":24,"tag":77,"props":1141,"children":1142},{"class":79,"line":371},[1143],{"type":24,"tag":77,"props":1144,"children":1145},{"style":105},[1146],{"type":29,"value":539},{"type":24,"tag":25,"props":1148,"children":1149},{},[1150,1152,1158],{"type":29,"value":1151},"Les contraintes sont portées par le DTO lui-même : elles décrivent ce qu'est une donnée valide, au même endroit que la donnée. Quant au phpdoc, il précise le contenu de ",{"type":24,"tag":73,"props":1153,"children":1155},{"className":1154},[],[1156],{"type":29,"value":1157},"$rows",{"type":29,"value":1159}," pour l'IDE et les analyseurs statiques tel que PHPStan.",{"type":24,"tag":25,"props":1161,"children":1162},{},[1163],{"type":29,"value":1164},"Le service d'import prend désormais ce DTO en un seul paramètre, et reste stateless :",{"type":24,"tag":67,"props":1166,"children":1168},{"className":69,"code":1167,"language":13,"meta":7,"style":7},"final readonly class EventImporter\n{\n    public function __construct(private EventRepository $events) {}\n\n    /** @return list\u003Cstring> les erreurs rencontrées */\n    public function import(ImportEventsDto $dto): array\n    {\n        $errors = [];\n\n        foreach ($dto->rows as $row) {\n            // ... utilise $dto->organizer, remplit $errors\n        }\n\n        return $errors;\n    }\n}\n",[1169],{"type":24,"tag":73,"props":1170,"children":1171},{"__ignoreMap":7},[1172,1191,1198,1243,1250,1286,1331,1338,1357,1364,1411,1423,1430,1437,1456,1463],{"type":24,"tag":77,"props":1173,"children":1174},{"class":79,"line":80},[1175,1179,1183,1187],{"type":24,"tag":77,"props":1176,"children":1177},{"style":84},[1178],{"type":29,"value":87},{"type":24,"tag":77,"props":1180,"children":1181},{"style":84},[1182],{"type":29,"value":585},{"type":24,"tag":77,"props":1184,"children":1185},{"style":84},[1186],{"type":29,"value":92},{"type":24,"tag":77,"props":1188,"children":1189},{"style":95},[1190],{"type":29,"value":98},{"type":24,"tag":77,"props":1192,"children":1193},{"class":79,"line":101},[1194],{"type":24,"tag":77,"props":1195,"children":1196},{"style":105},[1197],{"type":29,"value":108},{"type":24,"tag":77,"props":1199,"children":1200},{"class":79,"line":111},[1201,1205,1209,1213,1217,1222,1226,1230,1235,1239],{"type":24,"tag":77,"props":1202,"children":1203},{"style":84},[1204],{"type":29,"value":205},{"type":24,"tag":77,"props":1206,"children":1207},{"style":84},[1208],{"type":29,"value":210},{"type":24,"tag":77,"props":1210,"children":1211},{"style":614},[1212],{"type":29,"value":617},{"type":24,"tag":77,"props":1214,"children":1215},{"style":105},[1216],{"type":29,"value":220},{"type":24,"tag":77,"props":1218,"children":1219},{"style":84},[1220],{"type":29,"value":1221},"private",{"type":24,"tag":77,"props":1223,"children":1224},{"style":95},[1225],{"type":29,"value":635},{"type":24,"tag":77,"props":1227,"children":1228},{"style":125},[1229],{"type":29,"value":128},{"type":24,"tag":77,"props":1231,"children":1232},{"style":131},[1233],{"type":29,"value":1234},"events",{"type":24,"tag":77,"props":1236,"children":1237},{"style":105},[1238],{"type":29,"value":237},{"type":24,"tag":77,"props":1240,"children":1241},{"style":105},[1242],{"type":29,"value":657},{"type":24,"tag":77,"props":1244,"children":1245},{"class":79,"line":147},[1246],{"type":24,"tag":77,"props":1247,"children":1248},{"emptyLinePlaceholder":193},[1249],{"type":29,"value":196},{"type":24,"tag":77,"props":1251,"children":1252},{"class":79,"line":189},[1253,1258,1263,1267,1271,1276,1281],{"type":24,"tag":77,"props":1254,"children":1255},{"style":125},[1256],{"type":29,"value":1257},"    /**",{"type":24,"tag":77,"props":1259,"children":1260},{"style":268},[1261],{"type":29,"value":1262}," @return",{"type":24,"tag":77,"props":1264,"children":1265},{"style":1018},[1266],{"type":29,"value":1021},{"type":24,"tag":77,"props":1268,"children":1269},{"style":426},[1270],{"type":29,"value":1026},{"type":24,"tag":77,"props":1272,"children":1273},{"style":268},[1274],{"type":29,"value":1275},"string",{"type":24,"tag":77,"props":1277,"children":1278},{"style":426},[1279],{"type":29,"value":1280},"> les erreurs rencontrées ",{"type":24,"tag":77,"props":1282,"children":1283},{"style":125},[1284],{"type":29,"value":1285},"*/\n",{"type":24,"tag":77,"props":1287,"children":1288},{"class":79,"line":199},[1289,1293,1297,1301,1305,1310,1314,1319,1323,1327],{"type":24,"tag":77,"props":1290,"children":1291},{"style":84},[1292],{"type":29,"value":205},{"type":24,"tag":77,"props":1294,"children":1295},{"style":84},[1296],{"type":29,"value":210},{"type":24,"tag":77,"props":1298,"children":1299},{"style":178},[1300],{"type":29,"value":330},{"type":24,"tag":77,"props":1302,"children":1303},{"style":105},[1304],{"type":29,"value":220},{"type":24,"tag":77,"props":1306,"children":1307},{"style":95},[1308],{"type":29,"value":1309},"ImportEventsDto",{"type":24,"tag":77,"props":1311,"children":1312},{"style":125},[1313],{"type":29,"value":128},{"type":24,"tag":77,"props":1315,"children":1316},{"style":131},[1317],{"type":29,"value":1318},"dto",{"type":24,"tag":77,"props":1320,"children":1321},{"style":105},[1322],{"type":29,"value":237},{"type":24,"tag":77,"props":1324,"children":1325},{"style":84},[1326],{"type":29,"value":242},{"type":24,"tag":77,"props":1328,"children":1329},{"style":84},[1330],{"type":29,"value":485},{"type":24,"tag":77,"props":1332,"children":1333},{"class":79,"line":250},[1334],{"type":24,"tag":77,"props":1335,"children":1336},{"style":105},[1337],{"type":29,"value":256},{"type":24,"tag":77,"props":1339,"children":1340},{"class":79,"line":259},[1341,1345,1349,1353],{"type":24,"tag":77,"props":1342,"children":1343},{"style":125},[1344],{"type":29,"value":265},{"type":24,"tag":77,"props":1346,"children":1347},{"style":131},[1348],{"type":29,"value":134},{"type":24,"tag":77,"props":1350,"children":1351},{"style":84},[1352],{"type":29,"value":139},{"type":24,"tag":77,"props":1354,"children":1355},{"style":105},[1356],{"type":29,"value":144},{"type":24,"tag":77,"props":1358,"children":1359},{"class":79,"line":299},[1360],{"type":24,"tag":77,"props":1361,"children":1362},{"emptyLinePlaceholder":193},[1363],{"type":29,"value":196},{"type":24,"tag":77,"props":1365,"children":1366},{"class":79,"line":308},[1367,1371,1375,1379,1383,1387,1391,1395,1399,1403,1407],{"type":24,"tag":77,"props":1368,"children":1369},{"style":84},[1370],{"type":29,"value":377},{"type":24,"tag":77,"props":1372,"children":1373},{"style":105},[1374],{"type":29,"value":382},{"type":24,"tag":77,"props":1376,"children":1377},{"style":125},[1378],{"type":29,"value":387},{"type":24,"tag":77,"props":1380,"children":1381},{"style":131},[1382],{"type":29,"value":1318},{"type":24,"tag":77,"props":1384,"children":1385},{"style":84},[1386],{"type":29,"value":276},{"type":24,"tag":77,"props":1388,"children":1389},{"style":131},[1390],{"type":29,"value":348},{"type":24,"tag":77,"props":1392,"children":1393},{"style":84},[1394],{"type":29,"value":396},{"type":24,"tag":77,"props":1396,"children":1397},{"style":125},[1398],{"type":29,"value":128},{"type":24,"tag":77,"props":1400,"children":1401},{"style":131},[1402],{"type":29,"value":405},{"type":24,"tag":77,"props":1404,"children":1405},{"style":105},[1406],{"type":29,"value":237},{"type":24,"tag":77,"props":1408,"children":1409},{"style":105},[1410],{"type":29,"value":414},{"type":24,"tag":77,"props":1412,"children":1413},{"class":79,"line":316},[1414,1418],{"type":24,"tag":77,"props":1415,"children":1416},{"style":125},[1417],{"type":29,"value":423},{"type":24,"tag":77,"props":1419,"children":1420},{"style":426},[1421],{"type":29,"value":1422}," ... utilise $dto->organizer, remplit $errors\n",{"type":24,"tag":77,"props":1424,"children":1425},{"class":79,"line":363},[1426],{"type":24,"tag":77,"props":1427,"children":1428},{"style":105},[1429],{"type":29,"value":438},{"type":24,"tag":77,"props":1431,"children":1432},{"class":79,"line":371},[1433],{"type":24,"tag":77,"props":1434,"children":1435},{"emptyLinePlaceholder":193},[1436],{"type":29,"value":196},{"type":24,"tag":77,"props":1438,"children":1439},{"class":79,"line":417},[1440,1444,1448,1452],{"type":24,"tag":77,"props":1441,"children":1442},{"style":84},[1443],{"type":29,"value":502},{"type":24,"tag":77,"props":1445,"children":1446},{"style":125},[1447],{"type":29,"value":128},{"type":24,"tag":77,"props":1449,"children":1450},{"style":131},[1451],{"type":29,"value":134},{"type":24,"tag":77,"props":1453,"children":1454},{"style":105},[1455],{"type":29,"value":186},{"type":24,"tag":77,"props":1457,"children":1458},{"class":79,"line":432},[1459],{"type":24,"tag":77,"props":1460,"children":1461},{"style":105},[1462],{"type":29,"value":305},{"type":24,"tag":77,"props":1464,"children":1465},{"class":79,"line":441},[1466],{"type":24,"tag":77,"props":1467,"children":1468},{"style":105},[1469],{"type":29,"value":539},{"type":24,"tag":43,"props":1471,"children":1473},{"id":1472},"en-résumé",[1474],{"type":29,"value":1475},"En résumé",{"type":24,"tag":25,"props":1477,"children":1478},{},[1479],{"type":29,"value":1480},"Un service stateless, c'est un service dont le comportement ne dépend que de ce qu'on lui passe, jamais de ce qui s'est exécuté avant lui. Il suffit de ne rien stocker d'un appel à l'autre, et readonly aide à s'y tenir. Le gain se voit surtout le jour où le même service est appelé plusieurs fois dans la même exécution, ou dans un processus qui dure : plus de surprise, et des tests qui se résument à appeler une méthode et regarder ce qu'elle renvoie.",{"type":24,"tag":1482,"props":1483,"children":1484},"style",{},[1485],{"type":29,"value":1486},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":101,"depth":101,"links":1488},[1489,1493,1494],{"id":45,"depth":101,"text":48,"children":1490},[1491,1492],{"id":62,"depth":111,"text":65},{"id":563,"depth":111,"text":566},{"id":876,"depth":101,"text":879},{"id":1472,"depth":101,"text":1475},"markdown","content:blog:2026:11-stateless-services.md","content","blog/2026/11-stateless-services.md","blog/2026/11-stateless-services","md",{"loc":4},1794493869525]