{"id":503147,"date":"2026-11-13T19:04:39","date_gmt":"2026-11-13T16:04:39","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=503147"},"modified":"2026-11-13T19:04:39","modified_gmt":"2026-11-13T16:04:39","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=503147","title":{"rendered":"SOLID \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043c\u0438\u0440\u0435: DIP \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0438\u0445 \u0441\u043b\u043e\u0451\u0432"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0414\u0438\u0441\u043a\u043b\u0435\u0439\u043c\u0435\u0440: \u0441\u0442\u0430\u0442\u044c\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0443\u0440\u043e\u0432\u043d\u044f junior \u0438 middle, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044f\u0442\u0441\u044f \u0441 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430\u043c\u0438 \u0447\u0438\u0441\u0442\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438 SOLID. \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u043a\u043e\u0434\u0430 \u2014 PHP 8.1+.<\/p>\n<p>\u0412\u0441\u0435\u043c \u0434\u043e\u0431\u0440\u043e\u0433\u043e \u0434\u043d\u044f! \u041d\u0430 \u0441\u0432\u044f\u0437\u0438 \u0412\u0430\u043b\u0435\u0432\u0438\u0447 \u0410\u0440\u0442\u0435\u043c \u2014 \u0442\u0438\u043c\u043b\u0438\u0434 \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 <a href=\"https:\/\/www.agima.ru\/\">AGIMA<\/a>.<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/4d\/07\/1f\/4d071f89a36e002fd2a73a9f181a725b.png\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/webt\/4d\/07\/1f\/4d071f89a36e002fd2a73a9f181a725b.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/webt\/4d\/07\/1f\/4d071f89a36e002fd2a73a9f181a725b.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0412 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1015408\/\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438<\/a> \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438 SRP, \u0432\u043e <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1065040\/\">\u0432\u0442\u043e\u0440\u043e\u0439<\/a> \u2014 OCP, \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1070242\/\">\u0442\u0440\u0435\u0442\u044c\u0435\u0439<\/a> \u2014 LSP, \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1082450\/\">\u0447\u0435\u0442\u0432\u0451\u0440\u0442\u043e\u0439<\/a> \u2014 ISP. \u041a\u0435\u0439\u0441 \u0441 \u043e\u0442\u0437\u044b\u0432\u0430\u043c\u0438 \u043a \u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0438\u043b\u0438\u0447\u043d\u043e: \u0443 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0441\u0432\u043e\u044f \u043f\u0440\u0438\u0447\u0438\u043d\u0430 \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f, \u043d\u043e\u0432\u044b\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0431\u0435\u0437 \u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0443\u0437\u043a\u0438\u0435.<\/p>\n<p>\u0418 \u0432\u0441\u0451 \u0436\u0435 \u043e\u0434\u043d\u0430\u0436\u0434\u044b \u0432 <code>FeedbackReportService<\/code> \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>use App\\Infrastructure\\FeedbackRepository<\/code>, \u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043e\u0442\u0447\u0451\u0442\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u043d\u0430\u0442\u044c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL.<\/p>\n<p>\u0421 \u044d\u0442\u0438\u043c \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 <strong>Dependency Inversion Principle<\/strong> (DIP). \u0412 \u0443\u0447\u0435\u0431\u043d\u0438\u043a\u0430\u0445 \u0435\u0433\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u0432\u043e\u0434\u044f\u0442 \u043a \u00ab\u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u00bb \u0438\u043b\u0438 \u043a \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435 Clean Architecture, \u0433\u0434\u0435 \u0431\u0438\u0437\u043d\u0435\u0441 \u0432 \u0446\u0435\u043d\u0442\u0440\u0435, \u0431\u0430\u0437\u0430 \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u0430 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0451\u0432 \u043f\u0430\u043f\u043e\u043a. \u041d\u0438 \u0442\u043e, \u043d\u0438 \u0434\u0440\u0443\u0433\u043e\u0435 \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u0433\u0434\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u043b\u0435\u0436\u0430\u0442\u044c <code>FeedbackReader<\/code> \u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043e\u0442\u0447\u0451\u0442\u0430 \u043d\u0435\u043b\u044c\u0437\u044f \u0432\u0438\u0434\u0435\u0442\u044c \u043a\u043b\u0430\u0441\u0441 \u0441 SQL.<\/p>\n<p><strong>\u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441:<\/strong> \u043a\u0430\u043a \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0441\u0442\u0440\u0435\u043b\u043a\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u0441\u043f\u0443\u0442\u0430\u0442\u044c DIP \u0441 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u043c \u0438 \u0441 \u043f\u0430\u043f\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u0433\u0430\u043b\u043e\u0447\u043a\u0438?<\/p>\n<h3>\u0427\u0442\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 DIP<\/h3>\n<p>\u041f\u0440\u0438\u043d\u0446\u0438\u043f \u0441\u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043b \u0420\u043e\u0431\u0435\u0440\u0442 \u041c\u0430\u0440\u0442\u0438\u043d \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 <em>The Dependency Inversion Principle<\/em> (<em>C++ Report<\/em>, 1996), \u043f\u043e\u0437\u0436\u0435 \u043e\u043d \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u043b \u0435\u0433\u043e \u0432 \u043a\u043d\u0438\u0433\u0430\u0445 <em>Agile Software Development<\/em> \u0438 <em>Clean Architecture<\/em>:<\/p>\n<blockquote>\n<p>A. High level modules should not depend upon low level modules. Both should depend upon abstractions.<\/p>\n<p>B. Abstractions should not depend upon details. Details should depend upon abstractions.<\/p>\n<\/blockquote>\n<p>\u041d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c:<\/p>\n<blockquote>\n<p>\u041c\u043e\u0434\u0443\u043b\u0438 \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u043d\u0438\u0436\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f. \u041e\u0431\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0439.<\/p>\n<p>\u0410\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0438 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0434\u0435\u0442\u0430\u043b\u0435\u0439. \u0414\u0435\u0442\u0430\u043b\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0439.<\/p>\n<\/blockquote>\n<p>\u0427\u0430\u0441\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u044e\u0442 \u0442\u0430\u043a:<\/p>\n<blockquote>\n<p>\u0417\u043d\u0430\u0447\u0438\u0442, \u0440\u0438\u0441\u0443\u0435\u043c \u0441\u043b\u043e\u0438, \u0432\u044b\u043d\u043e\u0441\u0438\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u0430\u043f\u043a\u0443, \u0432\u0441\u0451 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u2014 \u0438\u043d\u0430\u0447\u0435 \u043d\u0430\u0440\u0443\u0448\u0438\u043c SOLID.<\/p>\n<\/blockquote>\n<p>\u041f\u0440\u0438\u043d\u0446\u0438\u043f \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043e \u0434\u0440\u0443\u0433\u043e\u043c \u2014 \u043e \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u041e\u0431\u044b\u0447\u043d\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0442 \u043d\u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u0442. DIP \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0441\u0442\u0440\u0435\u043b\u043a\u0443: \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0435\u043c\u0443 \u043d\u0443\u0436\u0435\u043d, \u0430 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL \u044d\u0442\u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442. \u0412\u044b\u0437\u043e\u0432 \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0438\u0434\u0451\u0442 \u043e\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u043a \u0431\u0430\u0437\u0435, \u043d\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0432 \u043a\u043e\u0434\u0435 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u043e\u0442 \u0431\u0430\u0437\u044b \u043a \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044e.<\/p>\n<p>\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0443 \u041c\u0430\u0440\u0442\u0438\u043d\u0430 \u2014 \u044d\u0442\u043e \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0430, \u0442\u043e \u0435\u0441\u0442\u044c \u0448\u0430\u0433\u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f: \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043e\u0442\u0437\u044b\u0432\u044b, \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043e\u0442\u0447\u0451\u0442, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e. \u041d\u0438\u0436\u043d\u0438\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u2014 \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u043e\u0434\u0438\u0442 \u043d\u0430\u0440\u0443\u0436\u0443: \u0431\u0430\u0437\u0430, \u043f\u043e\u0447\u0442\u0430, Slack, \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f CSV. \u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0437\u043d\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e <code>FeedbackReader<\/code>, \u0430 <code>FeedbackRepository<\/code> \u0437\u043d\u0430\u0435\u0442 SQL \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u044d\u0442\u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/p>\n<p>\u041d\u0430 \u0441\u0445\u0435\u043c\u0435 \u0441\u043b\u0435\u0432\u0430 \u0441\u0435\u0440\u0432\u0438\u0441 \u043e\u0442\u0447\u0451\u0442\u0430 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 \u0441 SQL. \u0421\u043f\u0440\u0430\u0432\u0430 \u043e\u0431\u0430 \u043a\u043b\u0430\u0441\u0441\u0430 \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 <code>FeedbackReader<\/code>: \u0441\u0435\u0440\u0432\u0438\u0441 \u0435\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442, \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0435\u0433\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442.<\/p>\n<pre><code>flowchart LR  subgraph bad[\"\u0421\u0435\u0440\u0432\u0438\u0441 \u0437\u043d\u0430\u0435\u0442 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL\"]    S1[FeedbackReportService] --&gt; R1[FeedbackRepository]  end  subgraph good[\"\u041e\u0431\u0430 \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\"]    S2[FeedbackReportService] --&gt;|\u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442| I[FeedbackReader]    R2[FeedbackRepository] -.-&gt;|\u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442| I  end<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper\"><\/span><\/a><\/div><\/pre>\n<h3>DIP \u2014 \u044d\u0442\u043e \u043d\u0435 \u00ab\u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u00bb<\/h3>\n<p>\u0415\u0441\u0442\u044c \u0442\u0440\u0438 \u0440\u0430\u0437\u043d\u044b\u0435 \u0432\u0435\u0449\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0447\u0430\u0441\u0442\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u043e\u0434\u043d\u0438\u043c \u0441\u043b\u043e\u0432\u043e\u043c.<\/p>\n<p><strong>\u0412\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 (DI)<\/strong> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441, \u043a\u0430\u043a \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u043e\u043f\u0430\u043b \u0432\u043d\u0443\u0442\u0440\u044c \u043a\u043b\u0430\u0441\u0441\u0430. \u0415\u0441\u043b\u0438 \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 \u0441\u043d\u0430\u0440\u0443\u0436\u0438 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0438\u043b\u0438 \u0441\u0435\u0442\u0442\u0435\u0440, \u044d\u0442\u043e DI. \u0415\u0441\u043b\u0438 \u043a\u043b\u0430\u0441\u0441 \u0441\u0430\u043c \u0432\u044b\u0437\u0432\u0430\u043b <code>new<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u0430, \u044d\u0442\u043e \u0443\u0436\u0435 \u043d\u0435 DI.<\/p>\n<p><strong>DIP<\/strong> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441, \u043a\u0430\u043a\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u044d\u0442\u043e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442: \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u0435\u043d \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044e, \u0438\u043b\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL.<\/p>\n<p><strong>\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440<\/strong> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0442\u043e, \u043a\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>new<\/code>. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c Symfony, Laravel \u0438\u043b\u0438 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0441\u0431\u043e\u0440\u043a\u0438. \u041d\u0430 \u0442\u043e, \u043a\u0443\u0434\u0430 \u0441\u043c\u043e\u0442\u0440\u0438\u0442 \u0442\u0438\u043f, \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0443 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e.<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\n<\/th>\n<th>\n<p align=\"left\">DI<\/p>\n<\/th>\n<th>\n<p align=\"left\">DIP<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0412\u043e\u043f\u0440\u043e\u0441<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041a\u0430\u043a \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u043e\u043f\u0430\u043b \u0432\u043d\u0443\u0442\u0440\u044c?<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041e\u0442 \u0447\u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439?<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041a\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043e\u0431\u044a\u0435\u043a\u0442\u044b?<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">\u0427\u0435\u043c \u0434\u0435\u043b\u0430\u044e\u0442<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440, \u0441\u0435\u0442\u0442\u0435\u0440<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0440\u044f\u0434\u043e\u043c \u0441\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u043c<\/p>\n<\/td>\n<td>\n<p align=\"left\">Symfony, Laravel, <code>bootstrap.php<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u042d\u0442\u0438 \u0442\u0440\u0438 \u0432\u0435\u0449\u0438 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f \u043f\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438:<\/p>\n<ul>\n<li>\n<p>\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0436\u0434\u0451\u0442 <code>FeedbackRepository<\/code>: \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u043d\u043e \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0441\u043c\u043e\u0442\u0440\u0438\u0442 \u043d\u0430 SQL;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0441\u043e\u0431\u0440\u0430\u043b\u0438 \u0440\u0443\u043a\u0430\u043c\u0438 \u0432 <code>bootstrap.php<\/code>: \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u043d\u0435\u0442, \u0430 \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e.<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"php\">class FeedbackReportService{    public function __construct(        private FeedbackRepository $repository \/\/ \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u043d\u043e \u0442\u0438\u043f \u2014 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL    ) {}}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441\u043e\u0437\u0434\u0430\u043b \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043b \u0435\u0433\u043e \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440, \u0438 DI \u0437\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c. \u041d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0441\u044e\u0434\u0430 \u0447\u0442\u043e-\u0442\u043e, \u043a\u0440\u043e\u043c\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f, \u043d\u0435\u043b\u044c\u0437\u044f: \u0442\u0438\u043f \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442. \u041e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043b\u0438\u0431\u043e \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a <code>FeedbackRepository<\/code>, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e <code>save()<\/code> \u043f\u0430\u0434\u0430\u0435\u0442 (\u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u043c\u044b \u0443\u0448\u043b\u0438 \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1070242\/\">LSP<\/a>), \u043b\u0438\u0431\u043e \u043f\u0440\u0430\u0432\u043a\u0430 \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043e\u0442\u0447\u0451\u0442\u043e\u0432.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u0443\u0436\u0435 \u0440\u0430\u0437\u0432\u0451\u0440\u043d\u0443\u0442\u0430, \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0441\u0430\u043c\u0430 \u0441\u043e\u0431\u043e\u0439: \u0432 \u0441\u0431\u043e\u0440\u043a\u0435 <code>FeedbackRepository<\/code> \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0442\u0443\u0434\u0430, \u0433\u0434\u0435 \u0436\u0434\u0443\u0442 <code>FeedbackReader<\/code>. \u041e\u0431\u0440\u0430\u0442\u043d\u043e\u0435 \u043d\u0435\u0432\u0435\u0440\u043d\u043e \u2014 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0435\u0449\u0451 \u043d\u0435 \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u0440\u0430\u0437\u0432\u0451\u0440\u043d\u0443\u0442\u0430.<\/p>\n<h3>\u0413\u0434\u0435 \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u0441\u043c\u043e\u0442\u0440\u0438\u0442 \u043d\u0435 \u0442\u0443\u0434\u0430<\/h3>\n<h4>\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0437\u043d\u0430\u0435\u0442 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL<\/h4>\n<pre><code class=\"php\">namespace App\\Feedback;use App\\Infrastructure\\FeedbackRepository;class FeedbackReportService{    public function __construct(        private FeedbackRepository $repository    ) {}}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u041e\u0431\u044a\u0435\u043a\u0442 \u043f\u0440\u0438\u0448\u0451\u043b \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u043d\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0437\u043d\u0430\u0435\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0431\u0430\u0437\u0443.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u044f \u0442\u0435 \u0436\u0435, \u0447\u0442\u043e \u0432 \u043f\u0440\u043e\u0448\u043b\u044b\u0445 \u0447\u0430\u0441\u0442\u044f\u0445. \u0422\u0435\u0441\u0442 \u043b\u0438\u0431\u043e \u0445\u043e\u0434\u0438\u0442 \u0432 \u0431\u0430\u0437\u0443, \u043b\u0438\u0431\u043e \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u0442 \u0432\u0435\u0441\u044c <code>FeedbackRepository<\/code>, \u0438 \u0432 \u043f\u043e\u0434\u043c\u0435\u043d\u0435 \u0432\u0438\u0434\u0435\u043d <code>save()<\/code>, \u0445\u043e\u0442\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044e \u043d\u0443\u0436\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u0438\u0441\u043e\u043a \u043e\u0442\u0437\u044b\u0432\u043e\u0432. \u0415\u0441\u043b\u0438 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u043b\u043e\u0441\u044c \u0447\u0438\u0442\u0430\u0442\u044c \u043e\u0442\u0437\u044b\u0432\u044b \u0438\u0437 \u043f\u0430\u043c\u044f\u0442\u0438, \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441. \u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u043d\u0430\u043b\u0438\u0437 \u0442\u043e\u0436\u0435 \u043d\u0435 \u043f\u043e\u043c\u043e\u0436\u0435\u0442: \u0435\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u0432\u044b\u0437\u043e\u0432\u0435\u0442 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u043e\u0442\u0447\u0451\u0442\u0430 <code>$this-&gt;repository-&gt;save()<\/code>, PHPStan \u043f\u0440\u043e\u043c\u043e\u043b\u0447\u0438\u0442, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 \u0443 \u043a\u043b\u0430\u0441\u0441\u0430 \u0435\u0441\u0442\u044c. \u0423\u0437\u043a\u0438\u0439 <code>FeedbackReader<\/code> \u0438\u0437 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1070242\/\">LSP<\/a> \u0438 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1082450\/\">ISP<\/a> \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u043d\u043e \u0441\u0435\u0440\u0432\u0438\u0441 \u0435\u0433\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442.<\/p>\n<p>\u0415\u0449\u0451 \u0445\u0443\u0436\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u043a\u043e\u0433\u0434\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 <code>send()<\/code> \u043f\u0440\u044f\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u044e\u0442 <code>new \\PDO(...)<\/code>. \u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043d\u0438\u043a\u0442\u043e \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442, \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0431\u0435\u0437 \u0431\u0430\u0437\u044b \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e.<\/p>\n<p>\u041d\u0443\u0436\u043d\u0430 \u043e\u0431\u0440\u0430\u0442\u043d\u0430\u044f \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f: \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 <code>FeedbackReader<\/code>, \u0430 SQL \u0436\u0438\u0432\u0451\u0442 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u043c\u0435\u0441\u0442\u0435.<\/p>\n<h4>\u00ab\u0423 \u043d\u0430\u0441 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u2014 \u0437\u043d\u0430\u0447\u0438\u0442, DIP \u0443\u0436\u0435 \u0435\u0441\u0442\u044c\u00bb<\/h4>\n<blockquote>\n<p>\u0423 \u043d\u0430\u0441 Laravel \/ Symfony, autowire \u0432\u043a\u043b\u044e\u0447\u0451\u043d \u2014 DIP \u0441\u043e\u0431\u043b\u044e\u0434\u0451\u043d.<\/p>\n<\/blockquote>\n<p>Autowire \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u043f\u043e \u0442\u0438\u043f\u0430\u043c. \u041a\u0430\u043a\u0438\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u043e\u043d \u043d\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442.<\/p>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u043b\u043e\u0432\u0443\u0448\u043a\u0430: \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043e\u0431\u044a\u044f\u0432\u0438\u043b\u0438 \u0440\u044f\u0434\u043e\u043c \u0441 SQL, \u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u0435\u0433\u043e \u043e\u0442\u0442\u0443\u0434\u0430.<\/p>\n<pre><code class=\"php\">namespace App\\Infrastructure;use App\\Feedback\\DateRange;use App\\Feedback\\Feedback;interface FeedbackReader{    \/** @return Feedback[] *\/    public function getForPeriod(DateRange $period): array;}class FeedbackRepository implements FeedbackReader { \/* SQL *\/ }<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<pre><code class=\"php\">namespace App\\Feedback;use App\\Infrastructure\\FeedbackReader;class FeedbackReportService{    public function __construct(private FeedbackReader $reader) {}}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u0412 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u0441\u0442\u043e\u0438\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043d\u043e <code>use<\/code> \u0432\u0435\u0434\u0451\u0442 \u0432 \u043f\u0430\u043f\u043a\u0443 \u0441 SQL. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u044b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430: <code>Infrastructure<\/code> \u0437\u043d\u0430\u0435\u0442 \u043f\u0440\u043e <code>DateRange<\/code> \u0438 <code>Feedback<\/code> \u0438\u0437 \u043f\u0430\u043f\u043a\u0438 \u043e\u0442\u0437\u044b\u0432\u043e\u0432, \u0430 \u043f\u0430\u043f\u043a\u0430 \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u2014 \u043f\u0440\u043e <code>Infrastructure<\/code>. \u041f\u043e\u043b\u0443\u0447\u0438\u043b\u0430\u0441\u044c \u043a\u0440\u0443\u0433\u043e\u0432\u0430\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c, \u0438 \u043c\u043e\u0434\u0443\u043b\u044c \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u0431\u0435\u0437 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0443\u0436\u0435 \u043d\u0435 \u0441\u043e\u0431\u0440\u0430\u0442\u044c.<\/p>\n<p>\u0412\u0442\u043e\u0440\u0430\u044f \u043b\u043e\u0432\u0443\u0448\u043a\u0430: \u0444\u0430\u0439\u043b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043b\u0435\u0436\u0438\u0442 \u0433\u0434\u0435 \u043d\u0430\u0434\u043e, \u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0443 \u0432 \u043d\u0435\u0433\u043e \u043f\u0440\u043e\u0442\u0435\u043a\u043b\u0430 \u043e\u0434\u043d\u0430 \u0438\u0437 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439.<\/p>\n<pre><code class=\"php\">interface ReportSender{    public function send(Report $report, string $slackChannelId): void;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u043f\u043e \u043f\u043e\u0447\u0442\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c id \u043a\u0430\u043d\u0430\u043b\u0430 Slack \u0438 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e, \u0430 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043e\u0442\u0447\u0451\u0442\u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u0437\u043d\u0430\u0435\u0442 \u043f\u0440\u043e Slack. \u0412 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1070242\/\">LSP<\/a> \u044d\u0442\u043e\u0442 id \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u043b\u0438 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 <code>SlackReportSender<\/code>, \u0438 \u0442\u0430\u043c \u0435\u043c\u0443 \u0441\u0430\u043c\u043e\u0435 \u043c\u0435\u0441\u0442\u043e. \u0422\u0430 \u0436\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442, \u0435\u0441\u043b\u0438 \u043e\u0431\u0449\u0438\u0439 <code>format()<\/code> \u043d\u0430\u0447\u043d\u0451\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c <code>Spreadsheet<\/code> \u0438\u0437 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 XLSX.<\/p>\n<p>\u0414\u0435\u0442\u0430\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0442\u0435\u0447\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b:<\/p>\n<ul>\n<li>\n<p><strong>\u0447\u0435\u0440\u0435\u0437 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435<\/strong> \u2014 \u0435\u0441\u043b\u0438 <code>getForPeriod()<\/code> \u043e\u0442\u0434\u0430\u0451\u0442 <code>PDOStatement<\/code>, Doctrine <code>Collection<\/code> \u0438\u043b\u0438 \u043c\u0430\u0441\u0441\u0438\u0432 \u0441\u0442\u0440\u043e\u043a \u043f\u0440\u044f\u043c\u043e \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u043d\u0430\u0442\u044c, \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435;<\/p>\n<\/li>\n<li>\n<p><strong>\u0447\u0435\u0440\u0435\u0437 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f<\/strong> \u2014 \u0435\u0441\u043b\u0438 \u0441\u043a\u0432\u043e\u0437\u044c <code>FeedbackReader<\/code> \u043f\u0440\u043e\u043b\u0435\u0442\u0430\u0435\u0442 <code>PDOException<\/code>, \u0430 \u0441\u0435\u0440\u0432\u0438\u0441 \u0435\u0433\u043e \u043b\u043e\u0432\u0438\u0442, \u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>use<\/code> \u043d\u0430 \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0431\u0430\u0437\u044b.<\/p>\n<\/li>\n<\/ul>\n<p>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0442\u043e, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044e (<code>getForPeriod<\/code>, <code>send<\/code>), \u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u0438\u043f\u044b \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f (<code>DateRange<\/code>, <code>Feedback<\/code>, <code>Report<\/code>). <code>PDO<\/code>, <code>S3Client<\/code> \u0438 <code>SlackWebhook<\/code> \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0432\u043d\u0443\u0442\u0440\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439.<\/p>\n<h4>\u041f\u0430\u043f\u043a\u0438 \u0440\u0430\u0434\u0438 \u043f\u0430\u043f\u043e\u043a<\/h4>\n<p>\u0422\u0430\u043a \u0447\u0430\u0441\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0440\u0435\u0430\u043a\u0446\u0438\u044f \u043d\u0430 \u0441\u0445\u0435\u043c\u0443 \u0438\u0437 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043f\u0440\u043e \u0447\u0438\u0441\u0442\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443:<\/p>\n<pre><code>src\/  Domain\/    Entities\/    ValueObjects\/    DomainServices\/  Application\/    UseCases\/    Ports\/      In\/      Out\/  Infrastructure\/    Adapters\/      Persistence\/      Messaging\/  Bootstrap\/<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0439 \u0444\u0438\u0447\u0438, \u0438 \u0432 \u0434\u0435\u0440\u0435\u0432\u0435 \u0443\u0436\u0435 \u0447\u0435\u0442\u044b\u0440\u043d\u0430\u0434\u0446\u0430\u0442\u044c \u043f\u0430\u043f\u043e\u043a, \u0445\u043e\u0442\u044f \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0435\u0449\u0451 \u043d\u0435\u0442.<\/p>\n<p><code>Ports<\/code> \u0432 \u0442\u0430\u043a\u043e\u043c \u0434\u0435\u0440\u0435\u0432\u0435 \u2014 \u043f\u0430\u043f\u043a\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432, <code>Adapters<\/code> \u2014 \u043f\u0430\u043f\u043a\u0430 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0434\u044f\u0442 \u0432 \u0431\u0430\u0437\u0443, \u043f\u043e\u0447\u0442\u0443 \u0438 Slack. \u0412\u0441\u043a\u043e\u0440\u0435 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>GetFeedbackForReportUseCase<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>FeedbackReaderPort<\/code>. \u0415\u0433\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 <code>PostgresFeedbackReaderAdapter<\/code>, \u0430 \u0437\u0430\u043e\u0434\u043d\u043e \u0442\u0430\u0449\u0438\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0439 \u00ab\u043d\u0430 \u0431\u0443\u0434\u0443\u0449\u0435\u0435\u00bb.<\/p>\n<p>\u0424\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u043a\u043e\u0434\u0443 \u043d\u0435 \u043f\u0440\u0438\u0434\u0440\u0430\u0442\u044c\u0441\u044f. \u041d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043d\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u0435 \u043e\u0442\u0437\u044b\u0432\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0432 \u0447\u0435\u0442\u044b\u0440\u0451\u0445 \u043c\u0435\u0441\u0442\u0430\u0445, <code>getForPeriod()<\/code> \u0434\u043e\u043b\u0433\u043e \u0438\u0449\u0443\u0442 \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443, \u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c \u0432\u0442\u043e\u0440\u044b\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439.<\/p>\n<p>\u042d\u0442\u043e \u0442\u043e\u0442 \u0436\u0435 \u043a\u043e\u0441\u043c\u043e\u043b\u0451\u0442, \u0447\u0442\u043e \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1015408\/\">SRP<\/a> \u0438 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1065040\/\">OCP<\/a>, \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043c\u0435\u0441\u0442\u043e \u043b\u0438\u0448\u043d\u0438\u0445 \u0444\u0430\u0431\u0440\u0438\u043a \u0437\u0434\u0435\u0441\u044c \u043f\u0430\u043f\u043a\u0430 <code>Ports\/Out<\/code>. DIP \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0451\u0432, \u0435\u043c\u0443 \u0432\u0430\u0436\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0435\u043b\u043a\u0438. \u0414\u043b\u044f \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u0445\u0432\u0430\u0442\u0438\u0442 \u0434\u0432\u0443\u0445 \u043f\u0430\u043f\u043e\u043a.<\/p>\n<h3>\u0414\u0432\u0435 \u043f\u0430\u043f\u043a\u0438<\/h3>\n<pre><code>src\/  Feedback\/                         # \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043c \u043d\u0443\u0436\u043d\u044b    Feedback.php    DateRange.php    Report.php    ReportDeliveryException.php    FeedbackReader.php    FeedbackWriter.php    FeedbackReportFormatter.php    ReportSender.php    HighRatedFeedbackReader.php     # \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u00ab\u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0441\u043e\u043a\u0438\u0435 \u043e\u0446\u0435\u043d\u043a\u0438\u00bb    FeedbackReportService.php    FeedbackService.php  Infrastructure\/                   # \u0431\u0430\u0437\u0430, \u043f\u043e\u0447\u0442\u0430, Slack, \u0444\u0430\u0439\u043b\u044b    FeedbackRepository.php    CachingFeedbackReader.php       # \u0442\u0435 \u0436\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437 \u043a\u0435\u0448\u0430    EmailReportSender.php    SlackReportSender.php    CsvFeedbackReportFormatter.php    XlsxFeedbackReportFormatter.phpbootstrap.php<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b \u0438 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432 \u0434\u0435\u0440\u0435\u0432\u0435 \u043d\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u044b: \u044d\u0442\u043e \u0442\u043e\u0447\u043a\u0438 \u0432\u0445\u043e\u0434\u0430 \u043f\u043e HTTP \u0438 \u043f\u043e cron, \u0430 \u043d\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438.<\/p>\n<p><code>Feedback\/<\/code> \u2014 \u043f\u0430\u043f\u043a\u0430 \u0444\u0438\u0447\u0438. \u0412 \u043d\u0435\u0439 \u043b\u0435\u0436\u0430\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438, \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043c \u043d\u0443\u0436\u043d\u044b, \u0438 \u0442\u0438\u043f\u044b \u0432\u0440\u043e\u0434\u0435 <code>Feedback<\/code>, <code>DateRange<\/code> \u0438 <code>Report<\/code>. \u0412 <code>Infrastructure\/<\/code> \u0443\u0445\u043e\u0434\u0438\u0442 \u0432\u0441\u0451, \u0447\u0442\u043e \u0445\u043e\u0434\u0438\u0442 \u043d\u0430\u0440\u0443\u0436\u0443.<\/p>\n<p>\u0424\u043e\u0440\u043c\u0430\u0442\u0442\u0435\u0440\u044b \u0441\u0430\u043c\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 CSV \u0438\u043b\u0438 XLSX, \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0430\u0434 <code>CsvWriter<\/code> \u043c\u044b \u043d\u0435 \u0437\u0430\u0432\u043e\u0434\u0438\u043c. \u0424\u043e\u0440\u043c\u0430\u0442\u0442\u0435\u0440 \u0438 \u0442\u0430\u043a \u0441\u043b\u0443\u0436\u0438\u0442 \u043e\u0431\u0451\u0440\u0442\u043a\u043e\u0439 \u043d\u0430\u0434 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439, \u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>FeedbackReportFormatter<\/code> \u0443\u0436\u0435 \u0435\u0441\u0442\u044c.<\/p>\n<p>\u0420\u0430\u0437\u043d\u0438\u0446\u0430 \u043c\u0435\u0436\u0434\u0443 <code>HighRatedFeedbackReader<\/code> \u0438 <code>CachingFeedbackReader<\/code> \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u0430\u043f\u043a\u0443. \u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u0434\u0430\u0451\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0432\u044b\u0431\u043e\u0440\u043a\u0438, \u0442\u043e \u0435\u0441\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043b\u0435\u0436\u0438\u0442 \u0432 <code>Feedback\/<\/code>. \u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u0435 \u0436\u0435 \u0441\u0442\u0440\u043e\u043a\u0438, \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435, \u2014 \u044d\u0442\u043e \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0434\u0435\u0442\u0430\u043b\u044c, \u0438 \u043e\u043d \u043b\u0435\u0436\u0438\u0442 \u0432 <code>Infrastructure\/<\/code>. \u041e\u0431\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0442 <code>FeedbackReader<\/code> \u0438\u0437 \u043f\u0430\u043f\u043a\u0438 \u0444\u0438\u0447\u0438.<\/p>\n<p>\u0412\u043e\u0442 <code>HighRatedFeedbackReader<\/code> \u0438\u0437 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1070242\/\">LSP<\/a>, \u0442\u0435\u043f\u0435\u0440\u044c \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u0435\u043c namespace. \u041e\u043d \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0439 <code>FeedbackReader<\/code> \u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e \u0442\u043e\u043c, \u043e\u0442\u043a\u0443\u0434\u0430 \u0431\u0435\u0440\u0443\u0442\u0441\u044f \u043e\u0442\u0437\u044b\u0432\u044b:<\/p>\n<pre><code class=\"php\">namespace App\\Feedback;final class HighRatedFeedbackReader implements FeedbackReader{    public function __construct(private FeedbackReader $inner) {}    public function getForPeriod(DateRange $period): array    {        return array_values(array_filter(            $this-&gt;inner-&gt;getForPeriod($period),            fn(Feedback $f) =&gt; $f-&gt;rating &gt;= 4        ));    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u043a\u0435\u0448 \u0438\u043b\u0438 \u0447\u0442\u0435\u043d\u0438\u0435 \u0438\u0437 \u043f\u0430\u043c\u044f\u0442\u0438 \u0432 \u0442\u0435\u0441\u0442\u0435. \u041f\u0440\u0430\u0432\u0438\u043b\u043e \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f, \u0430 \u0432 <code>Feedback\/<\/code> \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u043d\u0435\u0442 \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0430 \u0438\u0437 <code>Infrastructure<\/code>.<\/p>\n<p>\u041d\u0438 \u043e\u0434\u0438\u043d \u0444\u0430\u0439\u043b \u0432 <code>Feedback\/<\/code> \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 <code>use App\\Infrastructure\\...<\/code>. \u0417\u0430\u0442\u043e <code>Infrastructure\/<\/code> \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 <code>FeedbackReader<\/code> \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0435\u0433\u043e. \u0421\u0430\u043c PHP \u0442\u0430\u043a\u043e\u0439 \u0438\u043c\u043f\u043e\u0440\u0442 \u043d\u0435 \u0437\u0430\u043f\u0440\u0435\u0442\u0438\u0442. \u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u0440\u044f\u043c\u043e \u0441\u0435\u0439\u0447\u0430\u0441 \u043c\u043e\u0436\u043d\u043e \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439:<\/p>\n<pre><code class=\"bash\">grep -rn 'use App\\\\Infrastructure' src\/Feedback<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u041f\u0443\u0441\u0442\u043e\u0439 \u0432\u044b\u0432\u043e\u0434 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0441\u0442\u0440\u0435\u043b\u043a\u0438 \u0441\u043c\u043e\u0442\u0440\u044f\u0442 \u043a\u0443\u0434\u0430 \u043d\u0430\u0434\u043e. \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u043d\u0435 \u043d\u0430\u0440\u0443\u0448\u0438\u043b\u043e\u0441\u044c \u0447\u0435\u0440\u0435\u0437 \u043c\u0435\u0441\u044f\u0446, \u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442 \u0432 CI \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/github.com\/deptrac\/deptrac\">deptrac<\/a> \u0438\u043b\u0438 <a href=\"https:\/\/github.com\/carlosas\/phpat\">PHPat<\/a>. \u0414\u043b\u044f deptrac \u0445\u0432\u0430\u0442\u0438\u0442 \u0442\u0430\u043a\u043e\u0433\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0430:<\/p>\n<pre><code class=\"yaml\">deptrac:  paths:    - .\/src  layers:    - name: Feedback      collectors:        - type: directory          value: src\/Feedback\/.*    - name: Infrastructure      collectors:        - type: directory          value: src\/Infrastructure\/.*  ruleset:    Feedback: ~            # \u043f\u0430\u043f\u043a\u0435 \u0444\u0438\u0447\u0438 \u043d\u0435\u043b\u044c\u0437\u044f \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043d\u0438 \u043e\u0442 \u043a\u043e\u0433\u043e    Infrastructure:      - Feedback           # \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0444\u0438\u0447\u0438<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u0440\u0438\u043d\u0430\u0434\u043b\u0435\u0436\u0438\u0442 \u0442\u043e\u043c\u0443, \u043a\u0442\u043e \u0435\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442. \u0412 <em>Agile Software Development<\/em> \u041c\u0430\u0440\u0442\u0438\u043d \u043a\u043b\u0430\u0434\u0451\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432 \u043e\u0434\u0438\u043d \u043f\u0430\u043a\u0435\u0442 \u0441 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u043c \u043a\u043e\u0434\u043e\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 <code>FeedbackReader<\/code> \u043b\u0435\u0436\u0438\u0442 \u0440\u044f\u0434\u043e\u043c \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c \u043e\u0442\u0447\u0451\u0442\u0430. \u0417\u043d\u0430\u0447\u0438\u0442, \u043e\u0448\u0438\u0431\u043a\u0430 \u0438\u0437 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u0431\u044b\u043b\u0430 \u0434\u0430\u0436\u0435 \u043d\u0435 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043b\u0435\u0436\u0430\u043b \u0440\u044f\u0434\u043e\u043c \u0441 SQL. \u041e\u043d \u043b\u0435\u0436\u0430\u043b \u0434\u0430\u043b\u0435\u043a\u043e \u043e\u0442 \u043a\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0435\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442.<\/p>\n<p>\u0422\u043e \u0436\u0435 \u043a\u0430\u0441\u0430\u0435\u0442\u0441\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439. <code>ReportDeliveryException<\/code> \u2014 \u0447\u0430\u0441\u0442\u044c \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0430 <code>ReportSender<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u043d \u0442\u043e\u0436\u0435 \u0436\u0438\u0432\u0451\u0442 \u0432 <code>Feedback\/<\/code>. \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0435\u0433\u043e \u0440\u044f\u0434\u043e\u043c \u0441\u043e <code>SlackReportSender<\/code>, \u0442\u043e <code>catch<\/code> \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u043f\u043e\u0442\u044f\u043d\u0435\u0442 \u0437\u0430 \u0441\u043e\u0431\u043e\u0439 \u0438\u043c\u043f\u043e\u0440\u0442 \u0438\u0437 <code>Infrastructure<\/code>. \u042d\u0442\u043e \u0438 \u0435\u0441\u0442\u044c \u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u043a\u0438 \u041c\u0430\u0440\u0442\u0438\u043d\u0430: \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044f \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0434\u0435\u0442\u0430\u043b\u0435\u0439.<\/p>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043b\u043e\u0432\u0438\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0441\u0432\u043e\u0435\u0433\u043e HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0435\u0451 \u0432 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0430. \u0412\u043e\u0437\u044c\u043c\u0451\u043c <code>SlackReportSender<\/code> \u0438\u0437 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1070242\/\">LSP<\/a> \u0438 \u0434\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0431\u0443\u0434\u0435\u043c \u0441\u0447\u0438\u0442\u0430\u0442\u044c, \u0447\u0442\u043e <code>SlackClient<\/code> \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0432\u0435\u0440\u0445 PSR-18:<\/p>\n<pre><code class=\"php\">namespace App\\Infrastructure;use App\\Feedback\\Report;use App\\Feedback\\ReportDeliveryException;use App\\Feedback\\ReportSender;use Psr\\Http\\Client\\ClientExceptionInterface;final class SlackReportSender implements ReportSender{    public function __construct(        private SlackClient $slack,        private string $channelId,    ) {}    public function send(Report $report): void    {        try {            \/\/ postMessage() \u0438\u043b\u0438 uploadFile(), \u043a\u0430\u043a \u0432 LSP        } catch (ClientExceptionInterface $e) {            throw new ReportDeliveryException('\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0447\u0451\u0442 \u0432 Slack', previous: $e);        }    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043b\u043e\u0432\u0438\u0442 <code>ReportDeliveryException<\/code> \u0438 \u043d\u0435 \u0437\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 HTTP-\u043a\u043b\u0438\u0435\u043d\u0442 \u0441\u0442\u043e\u0438\u0442 \u0432\u043d\u0443\u0442\u0440\u0438. \u0418\u0441\u0445\u043e\u0434\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u0432 <code>previous<\/code> \u0438 \u043f\u043e\u043f\u0430\u0434\u0451\u0442 \u0432 \u043b\u043e\u0433\u0438.<\/p>\n<pre><code class=\"php\">namespace App\\Feedback;class FeedbackReportService{    public function __construct(        private FeedbackReader $reader    ) {}    public function send(        DateRange $period,        FeedbackReportFormatter $formatter,        ReportSender $sender    ): void {        $feedback = $this-&gt;reader-&gt;getForPeriod($period);        $report = $formatter-&gt;format($feedback);        $sender-&gt;send($report);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0432 \u0442\u0435\u0441\u0442\u0435 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c PostgreSQL \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 \u0438\u0437 \u043f\u0430\u043c\u044f\u0442\u0438, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0441\u0431\u043e\u0440\u043a\u0443, \u0430 \u044d\u0442\u043e\u0442 \u043a\u043b\u0430\u0441\u0441 \u0442\u0440\u043e\u0433\u0430\u0442\u044c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e.<\/p>\n<p>\u0421 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0438\u0437 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1065040\/\">OCP<\/a> \u0432\u0441\u0451 \u0442\u0430\u043a \u0436\u0435: <code>ReportSender<\/code> \u043b\u0435\u0436\u0438\u0442 \u0432 <code>Feedback\/<\/code>, <code>SlackReportSender<\/code> \u2014 \u0432 <code>Infrastructure\/<\/code>. \u041f\u043e\u043a\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043d\u0443\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442\u0437\u044b\u0432\u0430\u043c, \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0436\u0438\u0432\u0451\u0442 \u0440\u044f\u0434\u043e\u043c \u0441\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u043c. \u041a\u043e\u0433\u0434\u0430 \u0435\u0433\u043e \u043d\u0430\u0447\u043d\u0443\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0444\u0438\u0447, \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432 \u043e\u0431\u0449\u0443\u044e \u043f\u0430\u043f\u043a\u0443, \u043d\u043e \u043d\u0435 \u0440\u0430\u043d\u044c\u0448\u0435.<\/p>\n<p>LSP \u0438 ISP \u0443\u0431\u0440\u0430\u043b\u0438 <code>save()<\/code> \u0438\u0437 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0432\u0438\u0434\u0438\u0442 \u043e\u0442\u0447\u0451\u0442. DIP \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u0433\u0434\u0435 \u043b\u0435\u0436\u0438\u0442 \u0444\u0430\u0439\u043b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430. \u041e\u0442\u0432\u0435\u0442 \u2014 \u0440\u044f\u0434\u043e\u043c \u0441 \u0442\u0435\u043c, \u043a\u0442\u043e \u0435\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442.<\/p>\n<h4>\u0413\u0434\u0435 \u044d\u0442\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f<\/h4>\n<p>\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u2014 \u0432 \u0444\u0430\u0439\u043b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c <code>bootstrap.php<\/code>, <code>config\/services.yaml<\/code> \u0438\u043b\u0438 <code>AppServiceProvider<\/code>.<\/p>\n<pre><code class=\"php\">$pdo = new \\PDO($_ENV['DB_DSN'], $_ENV['DB_USER'], $_ENV['DB_PASS']);$repository = new FeedbackRepository($pdo);$reportService = new FeedbackReportService($repository);   \/\/ \u0436\u0434\u0451\u0442 FeedbackReader$feedbackService = new FeedbackService($repository);       \/\/ \u0436\u0434\u0451\u0442 FeedbackWriter\/\/ \u043e\u0442\u0447\u0451\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e \u0432\u044b\u0441\u043e\u043a\u0438\u043c \u043e\u0446\u0435\u043d\u043a\u0430\u043c \u2014 \u0442\u043e\u0442 \u0436\u0435 \u0441\u0435\u0440\u0432\u0438\u0441, \u0434\u0440\u0443\u0433\u0430\u044f \u0441\u0431\u043e\u0440\u043a\u0430$highRatedReportService = new FeedbackReportService(    new HighRatedFeedbackReader($repository));<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u0421\u0431\u043e\u0440\u043a\u0430 \u0437\u043d\u0430\u0435\u0442 \u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u0438 \u0431\u0430\u0437\u0443, \u0430 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043f\u0440\u043e \u0431\u0430\u0437\u0443 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442. \u041e\u0442\u0447\u0451\u0442 \u043f\u043e \u0432\u044b\u0441\u043e\u043a\u0438\u043c \u043e\u0446\u0435\u043d\u043a\u0430\u043c \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1070242\/\">LSP<\/a>, \u0438 <code>FeedbackReportService<\/code> \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f.<\/p>\n<p>\u041e\u0434\u0438\u043d <code>FeedbackRepository<\/code> \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043e\u0431\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430, \u0438 \u043e\u0431\u043e\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043e\u0434\u043d\u0438\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c. \u0412 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u0445 \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u043a.<\/p>\n<p><strong>Symfony.<\/strong> \u0415\u0441\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0440\u043e\u0432\u043d\u043e \u043e\u0434\u0438\u043d \u0441\u0435\u0440\u0432\u0438\u0441, Symfony \u043f\u0440\u0438 autodiscovery \u0441\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c, \u0438 \u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u043e. \u041d\u043e \u0443 <code>FeedbackReader<\/code> \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0442\u0440\u0438: \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, <code>HighRatedFeedbackReader<\/code> \u0438 <code>CachingFeedbackReader<\/code>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u044f\u0432\u043d\u043e:<\/p>\n<pre><code class=\"yaml\"># config\/services.yamlApp\\Feedback\\FeedbackReader: '@App\\Infrastructure\\FeedbackRepository'<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p><strong>Laravel.<\/strong> \u0417\u0434\u0435\u0441\u044c \u043b\u0435\u0433\u043a\u043e \u043e\u0448\u0438\u0431\u0438\u0442\u044c\u0441\u044f: <code>bind()<\/code> \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043d\u043e\u0432\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435. \u0427\u0442\u043e\u0431\u044b \u043e\u0431\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043e\u0434\u0438\u043d \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u0441\u0430\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043a\u0430\u043a <code>singleton<\/code>:<\/p>\n<pre><code class=\"php\">\/\/ AppServiceProvider::register()$this-&gt;app-&gt;singleton(FeedbackRepository::class);$this-&gt;app-&gt;bind(FeedbackReader::class, FeedbackRepository::class);$this-&gt;app-&gt;bind(FeedbackWriter::class, FeedbackRepository::class);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u0424\u043e\u0440\u043c\u0430\u0442 \u0438 \u043a\u0430\u043d\u0430\u043b \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 cron, \u043a\u0430\u043a \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1065040\/\">OCP<\/a>: \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u043d\u044c CSV \u0443\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u043f\u043e\u0447\u0442\u0443, \u0440\u0430\u0437 \u0432 \u043d\u0435\u0434\u0435\u043b\u044e XLSX \u2014 \u0432 Slack. \u0412 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043c\u044b \u0438\u0445 \u043d\u0435 \u043a\u043b\u0430\u0434\u0451\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0443 \u043a\u0430\u0436\u0434\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 cron \u043e\u043d\u0438 \u0441\u0432\u043e\u0438.<\/p>\n<h4>\u0422\u0435\u0441\u0442 \u0431\u0435\u0437 \u0431\u0430\u0437\u044b<\/h4>\n<p>\u0412\u0441\u0435 \u0442\u0440\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u043a\u043b\u0430\u0441\u0441\u0430\u043c\u0438 \u0438\u0437 <code>tests\/<\/code>. \u0424\u0435\u0439\u043a\u043e\u0432\u044b\u0439 reader \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442, \u0437\u0430 \u043a\u0430\u043a\u043e\u0439 \u043f\u0435\u0440\u0438\u043e\u0434 \u0443 \u043d\u0435\u0433\u043e \u043f\u0440\u043e\u0441\u0438\u043b\u0438 \u043e\u0442\u0437\u044b\u0432\u044b. \u0424\u0435\u0439\u043a\u043e\u0432\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442\u0442\u0435\u0440 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u0438\u0435 \u043e\u0442\u0437\u044b\u0432\u044b \u0435\u043c\u0443 \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438, \u0438 \u043e\u0442\u0434\u0430\u0451\u0442 \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 <code>Report<\/code>. \u0424\u0435\u0439\u043a\u043e\u0432\u044b\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442, \u0447\u0442\u043e \u0435\u043c\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043b\u0438. \u041d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 CSV \u0432 \u044d\u0442\u043e\u043c \u0442\u0435\u0441\u0442\u0435 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f.<\/p>\n<pre><code class=\"php\">final class InMemoryFeedbackReader implements FeedbackReader{    public ?DateRange $requestedPeriod = null;    \/** @param Feedback[] $feedback *\/    public function __construct(private array $feedback) {}    public function getForPeriod(DateRange $period): array    {        $this-&gt;requestedPeriod = $period;        return $this-&gt;feedback;    }}final class FakeFeedbackReportFormatter implements FeedbackReportFormatter{    \/** @var Feedback[] *\/    public array $received = [];    public function __construct(private Report $report) {}    public function format(array $feedback): Report    {        $this-&gt;received = $feedback;        return $this-&gt;report;    }}final class FakeReportSender implements ReportSender{    public ?Report $sent = null;    public function send(Report $report): void    {        $this-&gt;sent = $report;    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<pre><code class=\"php\">$createdAt = new \\DateTimeImmutable('2026-03-10 12:00:00');$period = new DateRange(    new \\DateTimeImmutable('2026-03-10 00:00:00'),    new \\DateTimeImmutable('2026-03-11 00:00:00'),);$feedback = [new Feedback(1, 5, '\u043e\u0442\u043b\u0438\u0447\u043d\u043e', $createdAt, 'site')];$report = new Report('csv', 'fake-body', 'daily.csv');$formatter = new FakeFeedbackReportFormatter($report);$sender = new FakeReportSender();$reader = new InMemoryFeedbackReader($feedback);$service = new FeedbackReportService($reader);$service-&gt;send($period, $formatter, $sender);self::assertSame($period, $reader-&gt;requestedPeriod);self::assertSame($feedback, $formatter-&gt;received);self::assertSame($report, $sender-&gt;sent);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><span class=\"sc-logo-wrapper icon-only\"><\/span><\/a><\/div><\/pre>\n<p>\u0424\u0435\u0439\u043a\u0438 \u043b\u0435\u0436\u0430\u0442 \u0432 <code>tests\/<\/code>, \u0432 <code>Feedback\/<\/code> \u0438\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u0447\u0435\u0433\u043e. SQL \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0433\u043e CSV \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442 \u0442\u0435\u0441\u0442\u044b \u0441\u0430\u043c\u0438\u0445 <code>FeedbackRepository<\/code> \u0438 <code>CsvFeedbackReportFormatter<\/code>, \u0430 \u0442\u0435\u0441\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0448\u0430\u0433\u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f.<\/p>\n<h3>\u041a\u043e\u0433\u0434\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0435 \u043d\u0443\u0436\u0435\u043d<\/h3>\n<p>\u041d\u0435 \u043a\u0430\u0436\u0434\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043d\u0443\u0436\u0435\u043d \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/p>\n<p><strong>\u0422\u0438\u043f\u044b \u0444\u0438\u0447\u0438.<\/strong> <code>DateRange<\/code>, <code>Feedback<\/code>, <code>Report<\/code> \u2014 \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b. \u041e\u043d\u0438 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 \u043c\u043e\u0434\u0435\u043b\u044c \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u0438 \u043d\u0435 \u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0431\u0430\u0437\u0430 \u0438\u043b\u0438 Slack. DIP \u043d\u0443\u0436\u0435\u043d \u043d\u0430 \u0433\u0440\u0430\u043d\u0438\u0446\u0435 \u0441 \u043a\u043e\u0434\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u043e \u0434\u0440\u0443\u0433\u0438\u043c \u043f\u0440\u0438\u0447\u0438\u043d\u0430\u043c.<\/p>\n<p><strong>\u041e\u0434\u043d\u0430 \u0431\u0430\u0437\u0430, \u0438 \u0442\u0435\u0441\u0442\u044b \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0432 \u043d\u0435\u0451 \u0445\u043e\u0434\u044f\u0442.<\/strong> \u0415\u0441\u043b\u0438 \u043e\u0442\u0437\u044b\u0432\u044b \u0432\u0441\u0435\u0433\u0434\u0430 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 PostgreSQL, \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u0431\u0435\u0437 \u0431\u0430\u0437\u044b \u043d\u0438\u043a\u043e\u043c\u0443 \u043d\u0435 \u043d\u0443\u0436\u0435\u043d \u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0430\u044f, \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u043c\u043e\u0436\u0435\u0442 \u043e\u0441\u0442\u0430\u0442\u044c\u0441\u044f <code>FeedbackRepository<\/code>. \u042d\u0442\u043e \u043e\u0441\u043e\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441, \u043d\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0435\u0433\u043e DIP \u043d\u0435 \u0441\u0442\u043e\u0438\u0442. \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u043e\u043a\u0443\u043f\u0430\u0442\u044c\u0441\u044f, \u043a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0431\u0435\u0437 \u0431\u0430\u0437\u044b, \u0430 \u0432\u0442\u043e\u0440\u0430\u044f \u0431\u0430\u0437\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u0430. \u0427\u0442\u0435\u043d\u0438\u0435 \u0441 \u0440\u0435\u043f\u043b\u0438\u043a\u0438 \u0442\u043e\u0436\u0435 \u043d\u0435 \u043f\u043e\u0432\u043e\u0434 \u0437\u0430\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441: \u044d\u0442\u043e \u0442\u043e\u0442 \u0436\u0435 \u043a\u043b\u0430\u0441\u0441 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c \u0432 \u0441\u0431\u043e\u0440\u043a\u0435.<\/p>\n<p><strong>\u041c\u043e\u0434\u0435\u043b\u0438 ORM.<\/strong> \u0412 Laravel <code>Feedback extends Model<\/code> \u2014 \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u0430\u043c\u0430 \u0437\u043d\u0430\u0435\u0442 \u0441\u0432\u043e\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443, \u0442\u043e \u0435\u0441\u0442\u044c \u043e\u043d\u0430 \u0443\u0436\u0435 \u0434\u0435\u0442\u0430\u043b\u044c. \u0412 Doctrine \u043f\u043e\u0445\u043e\u0436\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f: \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b <code>#[ORM\\Entity]<\/code> \u0438 <code>#[ORM\\Column]<\/code> \u0442\u044f\u043d\u0443\u0442 \u0432 \u043f\u0430\u043f\u043a\u0443 \u0444\u0438\u0447\u0438 <code>use Doctrine\\ORM\\Mapping<\/code>. \u0412 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u044d\u0442\u043e \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441, \u0440\u0430\u0434\u0438 \u0447\u0438\u0441\u0442\u043e\u0442\u044b \u043f\u0430\u043f\u043e\u043a \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u0451 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u043e\u0434\u0435\u043b\u0438. \u041d\u043e \u043e \u043d\u0451\u043c \u0432\u0430\u0436\u043d\u043e \u0437\u043d\u0430\u0442\u044c. \u0414\u043b\u044f Doctrine \u0435\u0441\u0442\u044c \u0447\u0438\u0441\u0442\u044b\u0439 \u0432\u044b\u0445\u043e\u0434: \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u0432 XML \u0432\u043d\u0443\u0442\u0440\u0438 <code>Infrastructure\/<\/code>. \u0414\u043b\u044f Eloquent \u0447\u0435\u0441\u0442\u043d\u0435\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u0442\u044c, \u0447\u0442\u043e \u043c\u043e\u0434\u0435\u043b\u044c \u2014 \u0447\u0430\u0441\u0442\u044c \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b, \u0438 \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0438\u0434, \u0447\u0442\u043e <code>FeedbackReader<\/code>, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 <code>Model<\/code>, \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e \u0431\u0430\u0437\u0435. \u0425\u043e\u0440\u043e\u0448\u0430\u044f \u043d\u043e\u0432\u043e\u0441\u0442\u044c \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0442\u0435\u0441\u0442\u0430\u043c \u044d\u0442\u043e \u043f\u043e\u0447\u0442\u0438 \u043d\u0435 \u043c\u0435\u0448\u0430\u0435\u0442: \u043c\u043e\u0434\u0435\u043b\u044c Eloquent \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 <code>new Feedback([...])<\/code> \u0431\u0435\u0437 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0431\u0430\u0437\u0435, \u0438 \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u0439 reader \u0432\u0435\u0440\u043d\u0451\u0442 \u0435\u0451 \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435.<\/p>\n<p><strong>\u041e\u0431\u044a\u0435\u043a\u0442\u044b \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u043d\u0430 \u0432\u0445\u043e\u0434\u0435.<\/strong> \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>Request<\/code> \u0432 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e, \u0442\u0430\u0449\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u043f\u0430\u043f\u043a\u0443 \u0444\u0438\u0447\u0438 \u043d\u0435\u0437\u0430\u0447\u0435\u043c. \u0413\u0440\u0430\u043d\u0438\u0446\u0430 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0442\u0430\u043c, \u0433\u0434\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>FeedbackService::submit()<\/code>: \u0441\u0435\u0440\u0432\u0438\u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 <code>FeedbackWriter<\/code> \u0438 \u043f\u0440\u043e <code>Request<\/code> \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442.<\/p>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0437\u0430\u0432\u043e\u0434\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043e\u0442\u0432\u0435\u0442\u044c\u0442\u0435 \u043d\u0430 \u043f\u044f\u0442\u044c \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432:<\/p>\n<ol>\n<li>\n<p><strong>\u0414\u0435\u0442\u0430\u043b\u044c \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f?<\/strong> SQL, \u043f\u043e\u0447\u0442\u0430 \u0438 Slack \u2014 \u0434\u0430. \u0428\u0430\u0433\u0438 \u00ab\u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043b \u2192 \u0441\u043e\u0431\u0440\u0430\u043b \u2192 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043b\u00bb \u2014 \u043d\u0435\u0442.<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0431\u0435\u0437 \u044d\u0442\u043e\u0439 \u0434\u0435\u0442\u0430\u043b\u0438?<\/strong> \u0415\u0441\u043b\u0438 \u0442\u0435\u0441\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043e\u0442\u0447\u0451\u0442\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0431\u0430\u0437\u0443, \u043e\u0442\u0432\u0435\u0442 \u0443\u0436\u0435 \u00ab\u0434\u0430\u00bb.<\/p>\n<\/li>\n<li>\n<p><strong>\u0415\u0441\u0442\u044c \u043b\u0438 \u0432 \u043f\u0430\u043f\u043a\u0435 \u0444\u0438\u0447\u0438 \u0438\u043c\u043f\u043e\u0440\u0442 \u0438\u0437 <\/strong><code><strong>Infrastructure<\/strong><\/code><strong>?<\/strong> \u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0438\u0434\u043d\u0430 \u043b\u0438 \u0432 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043e\u0434\u043d\u0430 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f?<\/strong> <code>slackChannelId<\/code> \u0443 \u043e\u0431\u0449\u0435\u0433\u043e <code>send()<\/code> \u2014 \u043a\u0430\u043a \u0440\u0430\u0437 \u0442\u0430\u043a\u043e\u0439 \u0441\u043b\u0443\u0447\u0430\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0442\u043e\u0440\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0438\u043b\u0438 \u0443\u0436\u0435 \u043d\u0443\u0436\u043d\u0430?<\/strong> \u0427\u0442\u0435\u043d\u0438\u0435 \u0438\u0437 \u043f\u0430\u043c\u044f\u0442\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f. \u00ab\u0412\u0434\u0440\u0443\u0433 \u043f\u0435\u0440\u0435\u0435\u0434\u0435\u043c \u043d\u0430 Mongo\u00bb \u2014 \u043d\u0435\u0442.<\/p>\n<\/li>\n<\/ol>\n<h3>\u0427\u0442\u043e \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c<\/h3>\n<ul>\n<li>\n<p><code>FeedbackReaderPort<\/code> \u0432 <code>Application\/Ports\/Out<\/code> \u0432\u043c\u0435\u0441\u0442\u043e <code>Feedback\/FeedbackReader.php<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0438\u043c\u043f\u043e\u0440\u0442\u043e\u0432 \u2014 \u0441 \u044d\u0442\u0438\u043c \u0441\u043f\u0440\u0430\u0432\u044f\u0442\u0441\u044f deptrac \u0438\u043b\u0438 PHPat;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0430\u043a\u0435\u0442 <code>Contracts\/<\/code> \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u043e\u0434\u043d\u043e\u0439 \u0444\u0438\u0447\u0438. \u041e\u0431\u0449\u0430\u044f \u043f\u0430\u043f\u043a\u0430 \u043d\u0443\u0436\u043d\u0430, \u043a\u043e\u0433\u0434\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0443\u0436\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0444\u0438\u0447;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0443 <code>I<\/code> \u0432 \u0438\u043c\u0435\u043d\u0430\u0445 \u0438 \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0430\u0434 <code>CsvWriter<\/code> \u0438 <code>XlsxWriter<\/code>;<\/p>\n<\/li>\n<li>\n<p>CQRS \u0438 \u0448\u0438\u043d\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u00ab\u043d\u0430 \u0441\u043b\u0443\u0447\u0430\u0439, \u0435\u0441\u043b\u0438 \u043e\u0442\u0447\u0451\u0442 \u0441\u0442\u0430\u043d\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u0439\u043d\u044b\u043c\u00bb, \u043a\u043e\u0433\u0434\u0430 \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0434\u0435\u0441\u044f\u0442\u044c \u0432 \u0434\u0435\u043d\u044c.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0427\u0435\u043c \u0437\u0430 \u044d\u0442\u043e \u043f\u043b\u0430\u0442\u0438\u043c<\/h3>\n<p>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u0440\u0438 \u043e\u0434\u043d\u043e\u0439 \u0431\u0430\u0437\u0435 \u043e\u043a\u0443\u043f\u0430\u0435\u0442\u0441\u044f, \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0443\u0436\u0435 \u043d\u0443\u0436\u043d\u044b \u0442\u0435\u0441\u0442\u044b \u0431\u0435\u0437 \u043d\u0435\u0451. \u0417\u0430\u0432\u043e\u0434\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u00ab\u043d\u0430 \u0431\u0443\u0434\u0443\u0449\u0435\u0435\u00bb, \u043f\u043e\u043a\u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0435\u0442, \u2014 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0440\u0430\u043d\u043e. \u0418\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1015408\/\">SRP<\/a> \u0443 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u043d\u0435 \u0431\u044b\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430: \u043e\u0442\u0437\u044b\u0432\u044b \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0441\u044c \u0432 \u043e\u0434\u043d\u043e\u0439 \u0431\u0430\u0437\u0435, \u0434\u0440\u0443\u0433\u0438\u0445 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0439 \u043d\u0435 \u0431\u044b\u043b\u043e. <code>FeedbackReader<\/code> \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1070242\/\">LSP<\/a>, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u043b\u0430\u0441\u044c \u0447\u0435\u0441\u0442\u043d\u0430\u044f \u0437\u0430\u043c\u0435\u043d\u0430.<\/p>\n<p>\u0423\u0437\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0438\u0437 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1082450\/\">ISP<\/a> \u0441\u0442\u043e\u044f\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a \u0432 \u0441\u0431\u043e\u0440\u043a\u0435: \u043a\u0430\u0436\u0434\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0441 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u043c. \u0414\u0432\u0435 \u043f\u0430\u043f\u043a\u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0449\u0435, \u0447\u0435\u043c Domain, Application, Ports \u0438 Adapters. \u041f\u0440\u0430\u0432\u0434\u0430, \u044f\u0437\u044b\u043a \u043d\u0435 \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u0435\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>Infrastructure<\/code> \u0432 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u043d\u0430 \u0440\u0435\u0432\u044c\u044e \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0432 CI.<\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u0432\u043e\u0437\u0440\u0430\u0437\u0438\u0442\u044c, \u0447\u0442\u043e PHPUnit \u0443\u043c\u0435\u0435\u0442 \u043f\u043e\u0434\u043c\u0435\u043d\u0438\u0442\u044c \u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 <code>FeedbackRepository<\/code>: <code>createMock()<\/code> \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \u0431\u0435\u0437 \u0432\u044b\u0437\u043e\u0432\u0430 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0433\u043e \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0430, \u0438 \u0431\u0430\u0437\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u0430. \u042d\u0442\u043e \u043f\u0440\u0430\u0432\u0434\u0430, \u043d\u043e \u0443 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u043c\u0435\u043d\u044b \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0435\u0441\u0442\u044c <code>save()<\/code>, \u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0435\u0433\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c. \u0410 \u0435\u0441\u043b\u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 <code>final<\/code>, \u043a\u0430\u043a \u0441\u043e\u0432\u0435\u0442\u043e\u0432\u0430\u043b\u0438 \u0432 <a href=\"https:\/\/habr.com\/ru\/companies\/agima\/articles\/1070242\/\">LSP<\/a>, PHPUnit \u043f\u043e\u0434\u043c\u0435\u043d\u0438\u0442\u044c \u0435\u0433\u043e \u0443\u0436\u0435 \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442. \u041e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/p>\n<h3>\u0412\u044b\u0432\u043e\u0434<\/h3>\n<p>DIP \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0438 \u043f\u0430\u043f\u043e\u043a Domain, Application \u0438 Ports \u043d\u0430 \u043a\u0430\u0436\u0434\u0443\u044e \u0444\u0438\u0447\u0443. \u0415\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043e\u0434\u043d\u043e: \u0447\u0442\u043e\u0431\u044b \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0435\u043b \u043e\u0442 \u0434\u0435\u0442\u0430\u043b\u0435\u0439.<\/p>\n<ol>\n<li>\n<p><strong>\u0421\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0430 \u0442\u0438\u043f \u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u044b, \u0430 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440.<\/strong> Autowire \u043d\u0435 \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442 <code>use App\\Infrastructure<\/code> \u0432 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043b\u0435\u0436\u0438\u0442 \u0440\u044f\u0434\u043e\u043c \u0441 \u0442\u0435\u043c, \u043a\u0442\u043e \u0435\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442.<\/strong> <code>FeedbackReader<\/code> \u0436\u0438\u0432\u0451\u0442 \u0440\u044f\u0434\u043e\u043c \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c \u043e\u0442\u0447\u0451\u0442\u0430, \u0430 \u043d\u0435 \u0440\u044f\u0434\u043e\u043c \u0441 PDO.<\/p>\n<\/li>\n<li>\n<p><strong>\u041a\u043b\u0430\u0441\u0441 \u0441 SQL \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f.<\/strong> \u0421\u0442\u0440\u0435\u043b\u043a\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0438\u0434\u0451\u0442 \u043e\u0442 \u0431\u0430\u0437\u044b \u043a <code>FeedbackReader<\/code>.<\/p>\n<\/li>\n<li>\n<p><strong>\u0427\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0435 \u043f\u0440\u043e\u0442\u0435\u043a\u0430\u044e\u0442 \u0434\u0435\u0442\u0430\u043b\u0438<\/strong> \u2014 \u043d\u0438 \u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u0445, \u043d\u0438 \u0432 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445, \u043d\u0438 \u0432 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\u0445.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u043a\u0430 \u0444\u0438\u0447\u0430 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f, \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0434\u0432\u0443\u0445 \u043f\u0430\u043f\u043e\u043a.<\/strong><\/p>\n<\/li>\n<\/ol>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u0427\u0430\u0441\u0442\u044c<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u0412\u043e\u043f\u0440\u043e\u0441<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u041d\u0430 \u043a\u0435\u0439\u0441\u0435 \u043e\u0442\u0437\u044b\u0432\u043e\u0432<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">SRP<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041a\u0442\u043e \u043f\u0440\u043e\u0441\u0438\u0442 \u043c\u0435\u043d\u044f\u0442\u044c \u043a\u043e\u0434?<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0421\u0430\u0439\u0442, \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 PWA \u2014 \u0440\u0430\u0437\u043d\u044b\u0435 \u043f\u0440\u0438\u0447\u0438\u043d\u044b \u043c\u0435\u043d\u044f\u0442\u044c \u043a\u043e\u0434; \u043f\u0440\u0438\u0451\u043c, \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438 \u043e\u0442\u0447\u0451\u0442 \u2014 \u0440\u0430\u0437\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">OCP<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041a\u0443\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442?<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0424\u043e\u0440\u043c\u0430\u0442 \u043e\u0442\u0447\u0451\u0442\u0430 \u0438 \u043a\u0430\u043d\u0430\u043b \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">LSP<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041c\u043e\u0436\u043d\u043e \u043b\u0438 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0434\u0440\u0443\u0433\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u043d\u0435 \u0441\u043b\u043e\u043c\u0430\u0442\u044c \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u043a\u043e\u0434?<\/p>\n<\/td>\n<td>\n<p align=\"left\">Slack \u043d\u0435 \u0440\u0435\u0436\u0435\u0442 CSV \u043c\u043e\u043b\u0447\u0430, \u00ab\u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u0435\u043d\u0438\u0435\u00bb \u043d\u0435 \u0447\u0435\u0440\u0435\u0437 <code>throw<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">ISP<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0412\u0441\u0435 \u043b\u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u043d\u0443\u0436\u043d\u044b \u0442\u043e\u043c\u0443, \u043a\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442?<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>FeedbackReader<\/code> \u0431\u0435\u0437 <code>save()<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">DIP<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041a\u0442\u043e \u043e\u0442 \u043a\u043e\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u0442?<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u2192 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u2190 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u0441\u0435\u0440\u0432\u0438\u0441 \u043e\u0442\u0447\u0451\u0442\u0430 \u043d\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u0438\u0437 <code>Infrastructure<\/code>. \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0435\u0433\u043e, \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0442\u0440\u0451\u0445 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0445 \u0444\u0435\u0439\u043a\u043e\u0432, \u0430 \u043d\u043e\u0432\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0441\u0431\u043e\u0440\u043a\u0435, \u0438 \u0441\u0430\u043c \u0441\u0435\u0440\u0432\u0438\u0441 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f.<\/p>\n<hr\/>\n<h3>\u0412\u043c\u0435\u0441\u0442\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0441\u0435\u0440\u0438\u0438<\/h3>\n<p>\u041f\u044f\u0442\u044c \u0441\u0442\u0430\u0442\u0435\u0439 \u043f\u043e\u0434\u0440\u044f\u0434 \u043c\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u0438 \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u043a\u0435\u0439\u0441 \u0441 \u043e\u0442\u0437\u044b\u0432\u0430\u043c\u0438. \u041d\u0438 \u0440\u0430\u0437\u0443 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u043b\u0438 \u0441 \u0432\u043e\u043f\u0440\u043e\u0441\u0430 \u00ab\u043a\u0430\u043a\u043e\u0439 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c\u00bb. \u041a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u044f\u0432\u043b\u044f\u043b\u0430\u0441\u044c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0430\u044f \u0431\u043e\u043b\u044c. \u041f\u0440\u043e\u0434\u0430\u043a\u0442\u044b \u0441\u0430\u0439\u0442\u0430, \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 PWA \u043c\u0435\u043d\u044f\u043b\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0441\u0431\u043e\u0440\u0430 \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u0434\u0440\u0443\u0433 \u043e\u0442 \u0434\u0440\u0443\u0433\u0430, \u0438 SRP \u0440\u0430\u0437\u0432\u0451\u043b \u043f\u0440\u0438\u0451\u043c, \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438 \u043e\u0442\u0447\u0451\u0442 \u043f\u043e \u0440\u0430\u0437\u043d\u044b\u043c \u043a\u043b\u0430\u0441\u0441\u0430\u043c. \u041a\u0430\u0436\u0434\u044b\u0439 \u043d\u043e\u0432\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u043e\u0442\u0447\u0451\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u043b \u0432\u0435\u0442\u043a\u0443 \u0432 <code>match<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u2014 \u043f\u043e\u043c\u043e\u0433 OCP. Slack \u043c\u043e\u043b\u0447\u0430 \u043e\u0431\u0440\u0435\u0437\u0430\u043b \u0445\u0432\u043e\u0441\u0442 CSV, \u0430 read-only \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043f\u0430\u0434\u0430\u043b \u043d\u0430 <code>save()<\/code> \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0431\u044d\u043a\u0444\u0438\u043b\u043b\u0430 \u2014 \u043f\u043e\u043c\u043e\u0433 LSP. \u0427\u0435\u0440\u0435\u0437 \u00ab\u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0438\u0442\u0430\u044e\u0449\u0443\u044e\u00bb \u0440\u0443\u0447\u043a\u0443 \u0430\u0434\u043c\u0438\u043d\u043a\u0438 \u0432\u044b\u0437\u0432\u0430\u043b\u0438 <code>delete()<\/code>, \u0438 \u0438\u0437 \u043f\u0440\u043e\u0434\u0430 \u043f\u0440\u043e\u043f\u0430\u043b\u0438 \u043e\u0442\u0437\u044b\u0432\u044b \u2014 \u043f\u043e\u043c\u043e\u0433 ISP. \u0422\u0435\u0441\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b \u0431\u0430\u0437\u0443 \u2014 \u043f\u043e\u043c\u043e\u0433 DIP. \u0422\u0430\u043c, \u0433\u0434\u0435 \u0431\u043e\u043b\u0438 \u043d\u0435 \u0431\u044b\u043b\u043e, \u043c\u044b \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u043b\u0438 \u043a\u043e\u0434 \u043a\u0430\u043a \u0435\u0441\u0442\u044c: \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0431\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u0432 SRP, \u0444\u043e\u0440\u043c\u0430\u0442\u0442\u0435\u0440 \u0431\u0435\u0437 \u043e\u0431\u0451\u0440\u0442\u043a\u0438 \u043d\u0430\u0434 <code>CsvWriter<\/code>, \u0434\u0432\u0435 \u043f\u0430\u043f\u043a\u0438 \u0432\u043c\u0435\u0441\u0442\u043e \u0447\u0435\u0442\u044b\u0440\u043d\u0430\u0434\u0446\u0430\u0442\u0438.<\/p>\n<p>SOLID \u2014 \u044d\u0442\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u0430 \u043d\u0435 \u0443\u0441\u0442\u0430\u0432. \u041a\u0430\u0436\u0434\u044b\u0439 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0441\u0442\u043e\u0438\u0442 \u0434\u0435\u043d\u0435\u0433: \u043b\u0438\u0448\u043d\u0438\u0439 \u043a\u043b\u0430\u0441\u0441, \u043b\u0438\u0448\u043d\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043b\u0438\u0448\u043d\u044f\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u0432 \u0441\u0431\u043e\u0440\u043a\u0435. \u041f\u043b\u0430\u0442\u0438\u0442\u044c \u0441\u0442\u043e\u0438\u0442 \u0442\u043e\u0433\u0434\u0430, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442\u0435, \u043a\u0430\u043a\u0443\u044e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u044d\u0442\u0438\u043c \u0440\u0435\u0448\u0430\u0435\u0442\u0435. \u00ab\u0422\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u0443 \u041c\u0430\u0440\u0442\u0438\u043d\u0430\u00bb \u0438\u043b\u0438 \u00ab\u0442\u0430\u043a \u043f\u0440\u0438\u043d\u044f\u0442\u043e \u0432 \u0447\u0438\u0441\u0442\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435\u00bb \u2014 \u043f\u043b\u043e\u0445\u0430\u044f \u043f\u0440\u0438\u0447\u0438\u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043b\u043e\u0439. \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 \u0442\u044f\u0436\u0435\u043b\u043e \u0432\u043d\u043e\u0441\u0438\u0442\u044c, \u0438\u043b\u0438 \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435\u043b\u044c\u0437\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c, \u2014 \u0445\u043e\u0440\u043e\u0448\u0430\u044f.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0430 \u0440\u0435\u0432\u044c\u044e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c, \u043e\u0442 \u043a\u0430\u043a\u043e\u0439 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0437\u0430\u0449\u0438\u0449\u0430\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0432\u0430\u0441. \u0415\u0441\u043b\u0438 \u043e\u0431\u044a\u044f\u0441\u043d\u0435\u043d\u0438\u0435 \u0441\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043a \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u0432\u044b \u0441\u0442\u0440\u043e\u0438\u0442\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u043e\u0439 \u043a\u043e\u0441\u043c\u043e\u043b\u0451\u0442.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1107656\/\">https:\/\/habr.com\/ru\/articles\/1107656\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0414\u0438\u0441\u043a\u043b\u0435\u0439\u043c\u0435\u0440: \u0441\u0442\u0430\u0442\u044c\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0443\u0440\u043e\u0432\u043d\u044f junior \u0438 middle, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044f\u0442\u0441\u044f \u0441 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430\u043c\u0438 \u0447\u0438\u0441\u0442\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438 SOLID. \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u043a\u043e\u0434\u0430 \u2014 PHP 8.1+.\u0412\u0441\u0435\u043c \u0434\u043e\u0431\u0440\u043e\u0433\u043e \u0434\u043d\u044f! \u041d\u0430 \u0441\u0432\u044f\u0437\u0438 \u0412\u0430\u043b\u0435\u0432\u0438\u0447 \u0410\u0440\u0442\u0435\u043c \u2014 \u0442\u0438\u043c\u043b\u0438\u0434 \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 AGIMA.\u0412 \u043f\u0435\u0440\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438 SRP, \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u0439 \u2014 OCP, \u0432 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u2014 LSP, \u0432 \u0447\u0435\u0442\u0432\u0451\u0440\u0442\u043e\u0439 \u2014 ISP. \u041a\u0435\u0439\u0441 \u0441 \u043e\u0442\u0437\u044b\u0432\u0430\u043c\u0438 \u043a \u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0438\u043b\u0438\u0447\u043d\u043e: \u0443 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0441\u0432\u043e\u044f \u043f\u0440\u0438\u0447\u0438\u043d\u0430 \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f, \u043d\u043e\u0432\u044b\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0431\u0435\u0437 \u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0443\u0437\u043a\u0438\u0435.\u0418 \u0432\u0441\u0451 \u0436\u0435 \u043e\u0434\u043d\u0430\u0436\u0434\u044b \u0432 FeedbackReportService \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f use App\\Infrastructure\\FeedbackRepository, \u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043e\u0442\u0447\u0451\u0442\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u043d\u0430\u0442\u044c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL.\u0421 \u044d\u0442\u0438\u043c \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 Dependency Inversion Principle (DIP). \u0412 \u0443\u0447\u0435\u0431\u043d\u0438\u043a\u0430\u0445 \u0435\u0433\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u0432\u043e\u0434\u044f\u0442 \u043a \u00ab\u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u00bb \u0438\u043b\u0438 \u043a \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435 Clean Architecture, \u0433\u0434\u0435 \u0431\u0438\u0437\u043d\u0435\u0441 \u0432 \u0446\u0435\u043d\u0442\u0440\u0435, \u0431\u0430\u0437\u0430 \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u0430 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0451\u0432 \u043f\u0430\u043f\u043e\u043a. \u041d\u0438 \u0442\u043e, \u043d\u0438 \u0434\u0440\u0443\u0433\u043e\u0435 \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u0433\u0434\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u043b\u0435\u0436\u0430\u0442\u044c FeedbackReader \u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u043e\u0442\u0447\u0451\u0442\u0430 \u043d\u0435\u043b\u044c\u0437\u044f \u0432\u0438\u0434\u0435\u0442\u044c \u043a\u043b\u0430\u0441\u0441 \u0441 SQL.\u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u043a\u0430\u043a \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0441\u0442\u0440\u0435\u043b\u043a\u0443 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u0441\u043f\u0443\u0442\u0430\u0442\u044c DIP \u0441 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u043c \u0438 \u0441 \u043f\u0430\u043f\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u0433\u0430\u043b\u043e\u0447\u043a\u0438?\u0427\u0442\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 DIP\u041f\u0440\u0438\u043d\u0446\u0438\u043f \u0441\u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043b \u0420\u043e\u0431\u0435\u0440\u0442 \u041c\u0430\u0440\u0442\u0438\u043d \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 The Dependency Inversion Principle (C++ Report, 1996), \u043f\u043e\u0437\u0436\u0435 \u043e\u043d \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u043b \u0435\u0433\u043e \u0432 \u043a\u043d\u0438\u0433\u0430\u0445 Agile Software Development \u0438 Clean Architecture:A. High level modules should not depend upon low level modules. Both should depend upon abstractions.B. Abstractions should not depend upon details. Details should depend upon abstractions.\u041d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c:\u041c\u043e\u0434\u0443\u043b\u0438 \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u043d\u0438\u0436\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f. \u041e\u0431\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0439.\u0410\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0438 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0434\u0435\u0442\u0430\u043b\u0435\u0439. \u0414\u0435\u0442\u0430\u043b\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0439.\u0427\u0430\u0441\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0441\u0441\u0443\u0436\u0434\u0430\u044e\u0442 \u0442\u0430\u043a:\u0417\u043d\u0430\u0447\u0438\u0442, \u0440\u0438\u0441\u0443\u0435\u043c \u0441\u043b\u043e\u0438, \u0432\u044b\u043d\u043e\u0441\u0438\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u0430\u043f\u043a\u0443, \u0432\u0441\u0451 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u2014 \u0438\u043d\u0430\u0447\u0435 \u043d\u0430\u0440\u0443\u0448\u0438\u043c SOLID.\u041f\u0440\u0438\u043d\u0446\u0438\u043f \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043e \u0434\u0440\u0443\u0433\u043e\u043c \u2014 \u043e \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u041e\u0431\u044b\u0447\u043d\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0442 \u043d\u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u0442. DIP \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0441\u0442\u0440\u0435\u043b\u043a\u0443: \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0435\u043c\u0443 \u043d\u0443\u0436\u0435\u043d, \u0430 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL \u044d\u0442\u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442. \u0412\u044b\u0437\u043e\u0432 \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0438\u0434\u0451\u0442 \u043e\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u043a \u0431\u0430\u0437\u0435, \u043d\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0432 \u043a\u043e\u0434\u0435 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u043e\u0442 \u0431\u0430\u0437\u044b \u043a \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044e.\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0443 \u041c\u0430\u0440\u0442\u0438\u043d\u0430 \u2014 \u044d\u0442\u043e \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0430, \u0442\u043e \u0435\u0441\u0442\u044c \u0448\u0430\u0433\u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f: \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043e\u0442\u0437\u044b\u0432\u044b, \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043e\u0442\u0447\u0451\u0442, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e. \u041d\u0438\u0436\u043d\u0438\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u2014 \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u043e\u0434\u0438\u0442 \u043d\u0430\u0440\u0443\u0436\u0443: \u0431\u0430\u0437\u0430, \u043f\u043e\u0447\u0442\u0430, Slack, \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f CSV. \u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0437\u043d\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e FeedbackReader, \u0430 FeedbackRepository \u0437\u043d\u0430\u0435\u0442 SQL \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u044d\u0442\u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.\u041d\u0430 \u0441\u0445\u0435\u043c\u0435 \u0441\u043b\u0435\u0432\u0430 \u0441\u0435\u0440\u0432\u0438\u0441 \u043e\u0442\u0447\u0451\u0442\u0430 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 \u0441 SQL. \u0421\u043f\u0440\u0430\u0432\u0430 \u043e\u0431\u0430 \u043a\u043b\u0430\u0441\u0441\u0430 \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 FeedbackReader: \u0441\u0435\u0440\u0432\u0438\u0441 \u0435\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442, \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0435\u0433\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442.flowchart LR  subgraph bad[&#171;\u0421\u0435\u0440\u0432\u0438\u0441 \u0437\u043d\u0430\u0435\u0442 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL&#187;]    S1[FeedbackReportService] &#8212;&gt; R1[FeedbackRepository]  end  subgraph good[&#171;\u041e\u0431\u0430 \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430&#187;]    S2[FeedbackReportService] &#8212;&gt;|\u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442| I[FeedbackReader]    R2[FeedbackRepository] -.-&gt;|\u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442| I  endDIP \u2014 \u044d\u0442\u043e \u043d\u0435 \u00ab\u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u00bb\u0415\u0441\u0442\u044c \u0442\u0440\u0438 \u0440\u0430\u0437\u043d\u044b\u0435 \u0432\u0435\u0449\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0447\u0430\u0441\u0442\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u043e\u0434\u043d\u0438\u043c \u0441\u043b\u043e\u0432\u043e\u043c.\u0412\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 (DI) \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441, \u043a\u0430\u043a \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u043e\u043f\u0430\u043b \u0432\u043d\u0443\u0442\u0440\u044c \u043a\u043b\u0430\u0441\u0441\u0430. \u0415\u0441\u043b\u0438 \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 \u0441\u043d\u0430\u0440\u0443\u0436\u0438 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0438\u043b\u0438 \u0441\u0435\u0442\u0442\u0435\u0440, \u044d\u0442\u043e DI. \u0415\u0441\u043b\u0438 \u043a\u043b\u0430\u0441\u0441 \u0441\u0430\u043c \u0432\u044b\u0437\u0432\u0430\u043b new \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u0435\u0442\u043e\u0434\u0430, \u044d\u0442\u043e \u0443\u0436\u0435 \u043d\u0435 DI.DIP \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441, \u043a\u0430\u043a\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u044d\u0442\u043e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442: \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u0435\u043d \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044e, \u0438\u043b\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL.\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0442\u043e, \u043a\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 new. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c Symfony, Laravel \u0438\u043b\u0438 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0441\u0431\u043e\u0440\u043a\u0438. \u041d\u0430 \u0442\u043e, \u043a\u0443\u0434\u0430 \u0441\u043c\u043e\u0442\u0440\u0438\u0442 \u0442\u0438\u043f, \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0443 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e.DIDIP\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0412\u043e\u043f\u0440\u043e\u0441\u041a\u0430\u043a \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u043e\u043f\u0430\u043b \u0432\u043d\u0443\u0442\u0440\u044c?\u041e\u0442 \u0447\u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439?\u041a\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043e\u0431\u044a\u0435\u043a\u0442\u044b?\u0427\u0435\u043c \u0434\u0435\u043b\u0430\u044e\u0442\u041a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440, \u0441\u0435\u0442\u0442\u0435\u0440\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0440\u044f\u0434\u043e\u043c \u0441\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u043cSymfony, Laravel, bootstrap.php\u042d\u0442\u0438 \u0442\u0440\u0438 \u0432\u0435\u0449\u0438 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f \u043f\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438:\u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0436\u0434\u0451\u0442 FeedbackRepository: \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u043d\u043e \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0441\u043c\u043e\u0442\u0440\u0438\u0442 \u043d\u0430 SQL;\u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0441\u043e\u0431\u0440\u0430\u043b\u0438 \u0440\u0443\u043a\u0430\u043c\u0438 \u0432 bootstrap.php: \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u043d\u0435\u0442, \u0430 \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e.class FeedbackReportService{    public function __construct(        private FeedbackRepository $repository \/\/ \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u043d\u043e \u0442\u0438\u043f \u2014 \u043a\u043b\u0430\u0441\u0441 \u0441 SQL    ) {}}\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0441\u043e\u0437\u0434\u0430\u043b \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043b \u0435\u0433\u043e \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440, \u0438 DI \u0437\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c. \u041d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0441\u044e\u0434\u0430 \u0447\u0442\u043e-\u0442\u043e, \u043a\u0440\u043e\u043c\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f, \u043d\u0435\u043b\u044c\u0437\u044f: \u0442\u0438\u043f \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442. \u041e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043b\u0438\u0431\u043e \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a FeedbackRepository, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e save() \u043f\u0430\u0434\u0430\u0435\u0442 (\u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u043c\u044b \u0443\u0448\u043b\u0438 \u0432 LSP), \u043b\u0438\u0431\u043e \u043f\u0440\u0430\u0432\u043a\u0430 \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043e\u0442\u0447\u0451\u0442\u043e\u0432.\u0415\u0441\u043b\u0438 \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u0443\u0436\u0435 \u0440\u0430\u0437\u0432\u0451\u0440\u043d\u0443\u0442\u0430, \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0441\u0430\u043c\u0430 \u0441\u043e\u0431\u043e\u0439: \u0432 \u0441\u0431\u043e\u0440\u043a\u0435 FeedbackRepository \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0442\u0443\u0434\u0430, \u0433\u0434\u0435 \u0436\u0434\u0443\u0442 FeedbackReader. \u041e\u0431\u0440\u0430\u0442\u043d\u043e\u0435 \u043d\u0435\u0432\u0435\u0440\u043d\u043e \u2014 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0435\u0449\u0451 \u043d\u0435 \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u0440\u0430\u0437\u0432\u0451\u0440\u043d\u0443\u0442\u0430.\u0413\u0434\u0435 \u0441\u0442\u0440\u0435\u043b\u043a\u0430 \u0441\u043c\u043e\u0442\u0440\u0438\u0442 \u043d\u0435 \u0442\u0443\u0434\u0430\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0437\u043d\u0430\u0435\u0442 \u043a\u043b\u0430\u0441\u0441 \u0441 SQLnamespace App\\Feedback;use App\\Infrastructure\\FeedbackRepository;class FeedbackReportService{    public function __construct(        private FeedbackRepository $repository    ) {}}\u041e\u0431\u044a\u0435\u043a\u0442 \u043f\u0440\u0438\u0448\u0451\u043b \u0441\u043d\u0430\u0440\u0443\u0436\u0438, \u043d\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0437\u043d\u0430\u0435\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0431\u0430\u0437\u0443.\u041f\u043e\u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u044f \u0442\u0435 \u0436\u0435, \u0447\u0442\u043e \u0432 \u043f\u0440\u043e\u0448\u043b\u044b\u0445 \u0447\u0430\u0441\u0442\u044f\u0445. \u0422\u0435\u0441\u0442 \u043b\u0438\u0431\u043e \u0445\u043e\u0434\u0438\u0442 \u0432 \u0431\u0430\u0437\u0443, \u043b\u0438\u0431\u043e \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u0442 \u0432\u0435\u0441\u044c FeedbackRepository, \u0438 \u0432 \u043f\u043e\u0434\u043c\u0435\u043d\u0435 \u0432\u0438\u0434\u0435\u043d save(), \u0445\u043e\u0442\u044f \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044e \u043d\u0443\u0436\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u0438\u0441\u043e\u043a \u043e\u0442\u0437\u044b\u0432\u043e\u0432. \u0415\u0441\u043b\u0438 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u043b\u043e\u0441\u044c \u0447\u0438\u0442\u0430\u0442\u044c \u043e\u0442\u0437\u044b\u0432\u044b \u0438\u0437 \u043f\u0430\u043c\u044f\u0442\u0438, \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441. \u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u043d\u0430\u043b\u0438\u0437 \u0442\u043e\u0436\u0435 \u043d\u0435 \u043f\u043e\u043c\u043e\u0436\u0435\u0442: \u0435\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u0432\u044b\u0437\u043e\u0432\u0435\u0442 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u043e\u0442\u0447\u0451\u0442\u0430 $this-&gt;repository-&gt;save(), PHPStan \u043f\u0440\u043e\u043c\u043e\u043b\u0447\u0438\u0442, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 \u0443 \u043a\u043b\u0430\u0441\u0441\u0430 \u0435\u0441\u0442\u044c. \u0423\u0437\u043a\u0438\u0439 FeedbackReader \u0438\u0437 LSP \u0438 ISP \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u043d\u043e \u0441\u0435\u0440\u0432\u0438\u0441 \u0435\u0433\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442.\u0415\u0449\u0451 \u0445\u0443\u0436\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u043a\u043e\u0433\u0434\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 send() \u043f\u0440\u044f\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u044e\u0442 new \\PDO(&#8230;). \u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043d\u0438\u043a\u0442\u043e \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442, \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0431\u0435\u0437 \u0431\u0430\u0437\u044b \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e.\u041d\u0443\u0436\u043d\u0430 \u043e\u0431\u0440\u0430\u0442\u043d\u0430\u044f \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f: \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 FeedbackReader, \u0430 SQL \u0436\u0438\u0432\u0451\u0442 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u043c\u0435\u0441\u0442\u0435.\u00ab\u0423 \u043d\u0430\u0441 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u2014 \u0437\u043d\u0430\u0447\u0438\u0442, DIP \u0443\u0436\u0435 \u0435\u0441\u0442\u044c\u00bb\u0423 \u043d\u0430\u0441 Laravel \/ Symfony, autowire \u0432\u043a\u043b\u044e\u0447\u0451\u043d \u2014 DIP \u0441\u043e\u0431\u043b\u044e\u0434\u0451\u043d.Autowire \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u043f\u043e \u0442\u0438\u043f\u0430\u043c. \u041a\u0430\u043a\u0438\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u043e\u043d \u043d\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442.\u041f\u0435\u0440\u0432\u0430\u044f \u043b\u043e\u0432\u0443\u0448\u043a\u0430: \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043e\u0431\u044a\u044f\u0432\u0438\u043b\u0438 \u0440\u044f\u0434\u043e\u043c \u0441 SQL, \u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u0435\u0433\u043e \u043e\u0442\u0442\u0443\u0434\u0430.namespace App\\Infrastructure;use App\\Feedback\\DateRange;use App\\Feedback\\Feedback;interface FeedbackReader{    \/** @return Feedback[] *\/    public function getForPeriod(DateRange $period): array;}class FeedbackRepository implements FeedbackReader { \/* SQL *\/ }namespace App\\Feedback;use App\\Infrastructure\\FeedbackReader;class FeedbackReportService{    public function __construct(private FeedbackReader $reader) {}}\u0412 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u0441\u0442\u043e\u0438\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043d\u043e use \u0432\u0435\u0434\u0451\u0442 \u0432 \u043f\u0430\u043f\u043a\u0443 \u0441 SQL. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u044b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430: Infrastructure \u0437\u043d\u0430\u0435\u0442 \u043f\u0440\u043e DateRange \u0438 Feedback \u0438\u0437 \u043f\u0430\u043f\u043a\u0438 \u043e\u0442\u0437\u044b\u0432\u043e\u0432, \u0430 \u043f\u0430\u043f\u043a\u0430 \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u2014 \u043f\u0440\u043e Infrastructure. \u041f\u043e\u043b\u0443\u0447\u0438\u043b\u0430\u0441\u044c \u043a\u0440\u0443\u0433\u043e\u0432\u0430\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c, \u0438 \u043c\u043e\u0434\u0443\u043b\u044c \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u0431\u0435\u0437 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0443\u0436\u0435 \u043d\u0435 \u0441\u043e\u0431\u0440\u0430\u0442\u044c.\u0412\u0442\u043e\u0440\u0430\u044f \u043b\u043e\u0432\u0443\u0448\u043a\u0430: \u0444\u0430\u0439\u043b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043b\u0435\u0436\u0438\u0442 \u0433\u0434\u0435 \u043d\u0430\u0434\u043e, \u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0443 \u0432 \u043d\u0435\u0433\u043e \u043f\u0440\u043e\u0442\u0435\u043a\u043b\u0430 \u043e\u0434\u043d\u0430 \u0438\u0437 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439.interface ReportSender{    public function send(Report $report, string $slackChannelId): void;}\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u043f\u043e \u043f\u043e\u0447\u0442\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c id \u043a\u0430\u043d\u0430\u043b\u0430 Slack \u0438 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e, \u0430 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043e\u0442\u0447\u0451\u0442\u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u0437\u043d\u0430\u0435\u0442 \u043f\u0440\u043e Slack. \u0412 LSP \u044d\u0442\u043e\u0442 id \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u043b\u0438 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 SlackReportSender, \u0438 \u0442\u0430\u043c \u0435\u043c\u0443 \u0441\u0430\u043c\u043e\u0435 \u043c\u0435\u0441\u0442\u043e. \u0422\u0430 \u0436\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442, \u0435\u0441\u043b\u0438 \u043e\u0431\u0449\u0438\u0439 format() \u043d\u0430\u0447\u043d\u0451\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c Spreadsheet \u0438\u0437 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 XLSX.\u0414\u0435\u0442\u0430\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0442\u0435\u0447\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b:\u0447\u0435\u0440\u0435\u0437 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u2014 \u0435\u0441\u043b\u0438 getForPeriod() \u043e\u0442\u0434\u0430\u0451\u0442 PDOStatement, Doctrine Collection \u0438\u043b\u0438 \u043c\u0430\u0441\u0441\u0438\u0432 \u0441\u0442\u0440\u043e\u043a \u043f\u0440\u044f\u043c\u043e \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u043d\u0430\u0442\u044c, \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435;\u0447\u0435\u0440\u0435\u0437 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u2014 \u0435\u0441\u043b\u0438 \u0441\u043a\u0432\u043e\u0437\u044c FeedbackReader \u043f\u0440\u043e\u043b\u0435\u0442\u0430\u0435\u0442 PDOException, \u0430 \u0441\u0435\u0440\u0432\u0438\u0441 \u0435\u0433\u043e \u043b\u043e\u0432\u0438\u0442, \u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f use \u043d\u0430 \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0431\u0430\u0437\u044b.\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0442\u043e, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044e (getForPeriod, send), \u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u0438\u043f\u044b \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f (DateRange, Feedback, Report). PDO, S3Client \u0438 SlackWebhook \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u0432\u043d\u0443\u0442\u0440\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439.\u041f\u0430\u043f\u043a\u0438 \u0440\u0430\u0434\u0438 \u043f\u0430\u043f\u043e\u043a\u0422\u0430\u043a \u0447\u0430\u0441\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0440\u0435\u0430\u043a\u0446\u0438\u044f \u043d\u0430 \u0441\u0445\u0435\u043c\u0443 \u0438\u0437 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043f\u0440\u043e \u0447\u0438\u0441\u0442\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443:src\/  Domain\/    Entities\/    ValueObjects\/    DomainServices\/  Application\/    UseCases\/    Ports\/      In\/      Out\/  Infrastructure\/    Adapters\/      Persistence\/      Messaging\/  Bootstrap\/\u042d\u0442\u043e \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0439 \u0444\u0438\u0447\u0438, \u0438 \u0432 \u0434\u0435\u0440\u0435\u0432\u0435 \u0443\u0436\u0435 \u0447\u0435\u0442\u044b\u0440\u043d\u0430\u0434\u0446\u0430\u0442\u044c \u043f\u0430\u043f\u043e\u043a, \u0445\u043e\u0442\u044f \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 \u0435\u0449\u0451 \u043d\u0435\u0442.Ports \u0432 \u0442\u0430\u043a\u043e\u043c \u0434\u0435\u0440\u0435\u0432\u0435 \u2014 \u043f\u0430\u043f\u043a\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432, Adapters \u2014 \u043f\u0430\u043f\u043a\u0430 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0434\u044f\u0442 \u0432 \u0431\u0430\u0437\u0443, \u043f\u043e\u0447\u0442\u0443 \u0438 Slack. \u0412\u0441\u043a\u043e\u0440\u0435 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f GetFeedbackForReportUseCase, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 FeedbackReaderPort. \u0415\u0433\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 PostgresFeedbackReaderAdapter, \u0430 \u0437\u0430\u043e\u0434\u043d\u043e \u0442\u0430\u0449\u0438\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0439 \u00ab\u043d\u0430 \u0431\u0443\u0434\u0443\u0449\u0435\u0435\u00bb.\u0424\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u043a\u043e\u0434\u0443 \u043d\u0435 \u043f\u0440\u0438\u0434\u0440\u0430\u0442\u044c\u0441\u044f. \u041d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043d\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u0435 \u043e\u0442\u0437\u044b\u0432\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0432 \u0447\u0435\u0442\u044b\u0440\u0451\u0445 \u043c\u0435\u0441\u0442\u0430\u0445, getForPeriod() \u0434\u043e\u043b\u0433\u043e \u0438\u0449\u0443\u0442 \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443, \u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c \u0432\u0442\u043e\u0440\u044b\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439.\u042d\u0442\u043e \u0442\u043e\u0442 \u0436\u0435 \u043a\u043e\u0441\u043c\u043e\u043b\u0451\u0442, \u0447\u0442\u043e \u0432 SRP \u0438 OCP, \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043c\u0435\u0441\u0442\u043e \u043b\u0438\u0448\u043d\u0438\u0445 \u0444\u0430\u0431\u0440\u0438\u043a \u0437\u0434\u0435\u0441\u044c \u043f\u0430\u043f\u043a\u0430 Ports\/Out. DIP \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0451\u0432, \u0435\u043c\u0443 \u0432\u0430\u0436\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0435\u043b\u043a\u0438. \u0414\u043b\u044f \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u0445\u0432\u0430\u0442\u0438\u0442 \u0434\u0432\u0443\u0445 \u043f\u0430\u043f\u043e\u043a.\u0414\u0432\u0435 \u043f\u0430\u043f\u043a\u0438src\/  Feedback\/                         # \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043c \u043d\u0443\u0436\u043d\u044b    Feedback.php    DateRange.php    Report.php    ReportDeliveryException.php    FeedbackReader.php    FeedbackWriter.php    FeedbackReportFormatter.php    ReportSender.php    HighRatedFeedbackReader.php     # \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u00ab\u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0441\u043e\u043a\u0438\u0435 \u043e\u0446\u0435\u043d\u043a\u0438\u00bb    FeedbackReportService.php    FeedbackService.php  Infrastructure\/                   # \u0431\u0430\u0437\u0430, \u043f\u043e\u0447\u0442\u0430, Slack, \u0444\u0430\u0439\u043b\u044b    FeedbackRepository.php    CachingFeedbackReader.php       # \u0442\u0435 \u0436\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437 \u043a\u0435\u0448\u0430    EmailReportSender.php    SlackReportSender.php    CsvFeedbackReportFormatter.php    XlsxFeedbackReportFormatter.phpbootstrap.php\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b \u0438 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432 \u0434\u0435\u0440\u0435\u0432\u0435 \u043d\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u044b: \u044d\u0442\u043e \u0442\u043e\u0447\u043a\u0438 \u0432\u0445\u043e\u0434\u0430 \u043f\u043e HTTP \u0438 \u043f\u043e cron, \u0430 \u043d\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438.Feedback\/ \u2014 \u043f\u0430\u043f\u043a\u0430 \u0444\u0438\u0447\u0438. \u0412 \u043d\u0435\u0439 \u043b\u0435\u0436\u0430\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438, \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043c \u043d\u0443\u0436\u043d\u044b, \u0438 \u0442\u0438\u043f\u044b \u0432\u0440\u043e\u0434\u0435 Feedback, DateRange \u0438 Report. \u0412 Infrastructure\/ \u0443\u0445\u043e\u0434\u0438\u0442 \u0432\u0441\u0451, \u0447\u0442\u043e \u0445\u043e\u0434\u0438\u0442 \u043d\u0430\u0440\u0443\u0436\u0443.\u0424\u043e\u0440\u043c\u0430\u0442\u0442\u0435\u0440\u044b \u0441\u0430\u043c\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 CSV \u0438\u043b\u0438 XLSX, \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043d\u0430\u0434 CsvWriter \u043c\u044b \u043d\u0435 \u0437\u0430\u0432\u043e\u0434\u0438\u043c. \u0424\u043e\u0440\u043c\u0430\u0442\u0442\u0435\u0440 \u0438 \u0442\u0430\u043a \u0441\u043b\u0443\u0436\u0438\u0442 \u043e\u0431\u0451\u0440\u0442\u043a\u043e\u0439 \u043d\u0430\u0434 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439, \u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 FeedbackReportFormatter \u0443\u0436\u0435 \u0435\u0441\u0442\u044c.\u0420\u0430\u0437\u043d\u0438\u0446\u0430 \u043c\u0435\u0436\u0434\u0443 HighRatedFeedbackReader \u0438 CachingFeedbackReader \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u0430\u043f\u043a\u0443. \u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u0434\u0430\u0451\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0432\u044b\u0431\u043e\u0440\u043a\u0438, \u0442\u043e \u0435\u0441\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043b\u0435\u0436\u0438\u0442 \u0432 Feedback\/. \u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u0435 \u0436\u0435 \u0441\u0442\u0440\u043e\u043a\u0438, \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435, \u2014 \u044d\u0442\u043e \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0434\u0435\u0442\u0430\u043b\u044c, \u0438 \u043e\u043d \u043b\u0435\u0436\u0438\u0442 \u0432 Infrastructure\/. \u041e\u0431\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0442&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-503147","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/503147","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=503147"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/503147\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=503147"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=503147"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=503147"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}