{"id":277270,"date":"2016-03-30T12:00:05","date_gmt":"2016-03-30T08:00:05","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=277270"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=277270","title":{"rendered":"Symfony \u0438 Command Bus"},"content":{"rendered":"<p>       \u0423\u0436\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u0433\u043e\u0434\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043f\u0430\u0442\u0442\u0435\u0440\u043d <em>Command Bus<\/em> \u0432 \u0441\u0432\u043e\u0438\u0445 Symfony-\u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0438 \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u0440\u0435\u0448\u0438\u043b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043e\u043f\u044b\u0442\u043e\u043c. \u0412 \u043a\u043e\u043d\u0446\u0435\u0432 \u043a\u043e\u043d\u0446\u043e\u0432 \u043e\u0431\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u0432 Laravel \u044d\u0442\u043e \u0435\u0441\u0442\u044c \u00ab\u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438\u00bb, \u0430 \u0432 Symfony, \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e Laravel \u0432\u043e \u043c\u043d\u043e\u0433\u043e\u043c \u0432\u044b\u0440\u043e\u0441 \u2014 \u043d\u0435\u0442, \u0445\u043e\u0442\u044f \u0441\u0430\u043c\u043e\u043c\u0443 \u043f\u043e\u043d\u044f\u0442\u0438\u044e <a href=\"http:\/\/martinfowler.com\/bliki\/CommandQuerySeparation.html\">Command\/Query Separation<\/a> \u0443\u0436\u0435 \u043d\u0435 \u043c\u0435\u043d\u0435\u0435 10 \u043b\u0435\u0442. \u0418 \u0435\u0441\u043b\u0438 \u0441 \u0431\u0443\u043a\u0432\u043e\u0439 \u00abQ\u00bb \u0438\u0437 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u044b \u00abCQRS\u00bb \u0435\u0449\u0435 \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c (\u043b\u0438\u0447\u043d\u043e \u043c\u0435\u043d\u044f \u0432\u043f\u043e\u043b\u043d\u0435 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0442 custom repositories), \u0442\u043e \u043a\u0443\u0434\u0430 \u043f\u0440\u0438\u0442\u043a\u043d\u0443\u0442\u044c \u0431\u0443\u043a\u0432\u0443 \u00abC\u00bb \u2014 \u043d\u0435\u044f\u0441\u043d\u043e.<\/p>\n<p>  \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u0434\u0430\u0436\u0435 \u0432 \u0431\u0430\u043d\u0430\u043b\u044c\u043d\u044b\u0445 CRUD-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 Command Bus \u0434\u0430\u0435\u0442 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430:<\/p>\n<ul>\n<li>\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u00ab\u0445\u0443\u0434\u044b\u043c\u0438\u00bb (\u0440\u0435\u0434\u043a\u0438\u0439 \u00ab\u044d\u043a\u0448\u0435\u043d\u00bb \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 15 \u0441\u0442\u0440\u043e\u043a),<\/li>\n<li>\u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u043e\u043a\u0438\u0434\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b \u0438 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0439 \u043e\u0442 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 (\u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0435\u0435 \u043d\u0435\u0441\u043b\u043e\u0436\u043d\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u043d\u0435 \u043d\u0430 Symfony),<\/li>\n<li>\u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442\u0441\u044f unit-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438,<\/li>\n<li>\u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u0434\u0430 (\u043a\u043e\u0433\u0434\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u00ab\u0444\u0438\u0447\u0443\u00bb \u043a\u0430\u043a \u0447\u0435\u0440\u0435\u0437 Web UI, \u0442\u0430\u043a \u0438 \u0447\u0435\u0440\u0435\u0437 API).<\/li>\n<\/ul>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/a85\/803\/f0a\/a85803f0aa724e9385e799f085634f02.png\" alt=\"\u041a\u0414\u041f\u0412\"\/><br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<h1>\u0414\u0435\u043a\u043e\u0440\u0430\u0446\u0438\u0438<\/h1>\n<p>  \u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0443 \u043d\u0430\u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u043a\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b. \u041f\u0440\u043e\u0435\u043a\u0442 \u043a\u0430\u043a \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f:<\/p>\n<ul>\n<li>\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435,<\/li>\n<li>\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435.<\/li>\n<\/ul>\n<p>  \u041a\u043e\u0434, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u043e \u0440\u043e\u0434\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 Symfony, \u043c\u043e\u0433 \u0431\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043a\u0430\u043a-\u0442\u043e \u0442\u0430\u043a:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Entity<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"php\">namespace AppBundle\\Entity;  use Doctrine\\ORM\\Mapping as ORM; use Symfony\\Bridge\\Doctrine\\Validator\\Constraints;  \/**  * @ORM\\Table(name=&quot;projects&quot;)  * @ORM\\Entity  * @Constraints\\UniqueEntity(fields={&quot;name&quot;}, message=&quot;\u041f\u0440\u043e\u0435\u043a\u0442 \u0441 \u0442\u0430\u043a\u0438\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.&quot;)  *\/ class Project {     const MAX_NAME        = 25;     const MAX_DESCRIPTION = 100;      \/**      * @var int ID.      *      * @ORM\\Column(name=&quot;id&quot;, type=&quot;integer&quot;)      * @ORM\\Id      * @ORM\\GeneratedValue(strategy=&quot;AUTO&quot;)      *\/     private $id;      \/**      * @var string \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.      *      * @ORM\\Column(name=&quot;name&quot;, type=&quot;string&quot;, length=25)      *\/     private $name;      \/**      * @var string \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.      *      * @ORM\\Column(name=&quot;description&quot;, type=&quot;string&quot;, length=100, nullable=true)      *\/     private $description; } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0424\u043e\u0440\u043c\u0430<\/b><\/p>\n<div class=\"spoiler_text\">namespace AppBundle\\Form;<\/p>\n<p>  use AppBundle\\Entity\\Project;<br \/>  use Symfony\\Component\\Form\\AbstractType;<br \/>  use Symfony\\Component\\Form\\Extension\\Core\\Type\\TextType;<br \/>  use Symfony\\Component\\Form\\FormBuilderInterface;<br \/>  use Symfony\\Component\\Validator\\Constraints;<\/p>\n<pre><code class=\"php\">class ProjectForm extends AbstractType {     \/**      * {@inheritdoc}      *\/     public function buildForm(FormBuilderInterface $builder, array $options)     {         $builder-&gt;add('name', TextType::class, [             'label'       =&gt; '\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430',             'required'    =&gt; true,             'attr'        =&gt; ['maxlength' =&gt; Project::MAX_NAME],             'constraints' =&gt; [                 new Constraints\\NotBlank(),                 new Constraints\\Length(['max' =&gt; Project::MAX_NAME]),             ],         ]);          $builder-&gt;add('description', TextType::class, [             'label'       =&gt; '\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430',             'required'    =&gt; false,             'attr'        =&gt; ['maxlength' =&gt; Project::MAX_DESCRIPTION],             'constraints' =&gt; [                 new Constraints\\Length(['max' =&gt; Project::MAX_DESCRIPTION]),             ],         ]);     }      \/**      * {@inheritdoc}      *\/     public function getBlockPrefix()     {         return 'project';     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 (\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430)<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"php\">namespace AppBundle\\Controller;  use AppBundle\\Entity\\Project; use AppBundle\\Form\\ProjectForm; use Sensio\\Bundle\\FrameworkExtraBundle\\Configuration; use Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller; use Symfony\\Component\\HttpFoundation\\Request;  class ProjectController extends Controller {     \/**      * \u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441 \u0444\u043e\u0440\u043c\u043e\u0439, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0435\u0435 &quot;\u0441\u0430\u0431\u043c\u0438\u0442&quot;.      *      * @Configuration\\Route(&quot;\/new&quot;)      * @Configuration\\Method({&quot;GET&quot;, &quot;POST&quot;})      *\/     public function newAction(Request $request)     {         $project = new Project();          $form = $this-&gt;createForm(ProjectForm::class, $project);         $form-&gt;handleRequest($request);          if ($form-&gt;isValid()) {             $this-&gt;getDoctrine()-&gt;getManager()-&gt;persist($project);             $this-&gt;getDoctrine()-&gt;getManager()-&gt;flush();              return $this-&gt;redirectToRoute('projects');         }          return $this-&gt;render('project\/form.html.twig', [             'form' =&gt; $form-&gt;createView(),         ]);     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u042f \u043f\u0440\u0438\u0432\u0435\u043b \u044d\u0442\u043e\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0431\u043e\u043b\u044c\u0448\u0435 \u0434\u043b\u044f \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u2014 \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a \u043e\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f Symfony-\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438. \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0436\u0435 \u0434\u0435\u043b\u0435 \u00ab\u0432\u0435\u0431 2.0\u00bb \u0434\u0430\u0432\u043d\u043e \u043f\u043e\u0431\u0435\u0434\u0438\u043b, \u0441\u043e\u0441\u0435\u0434 \u043f\u043e \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0432\u0430\u044f\u0435\u0442 \u00ab\u0444\u0440\u043e\u043d\u0442\u044d\u043d\u0434\u00bb \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 Angular, \u0430 \u0444\u043e\u0440\u043c\u044b \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u043f\u0440\u0438\u043b\u0435\u0442\u0430\u044e\u0442 \u0432 AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0438\u043d\u0430\u0447\u0435.<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"php\">namespace AppBundle\\Controller;  use AppBundle\\Entity\\Project; use AppBundle\\Form\\ProjectForm; use Sensio\\Bundle\\FrameworkExtraBundle\\Configuration; use Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller; use Symfony\\Component\\HttpFoundation\\JsonResponse; use Symfony\\Component\\HttpFoundation\\Request;  class ProjectController extends Controller {     \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 HTML-\u043a\u043e\u0434 \u0444\u043e\u0440\u043c\u044b.      *      * @Configuration\\Route(&quot;\/new&quot;, condition=&quot;request.isXmlHttpRequest()&quot;)      * @Configuration\\Method(&quot;GET&quot;)      *\/     public function showNewFormAction()     {         $form = $this-&gt;createForm(ProjectForm::class, null, [             'action' =&gt; $this-&gt;generateUrl('new_project'),         ]);          return $this-&gt;render('project\/form.html.twig', [             'form' =&gt; $form-&gt;createView(),         ]);     }      \/**      * \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 &quot;\u0441\u0430\u0431\u043c\u0438\u0442&quot; \u0444\u043e\u0440\u043c\u044b.      *      * @Configuration\\Route(&quot;\/new&quot;, name=&quot;new_project&quot;, condition=&quot;request.isXmlHttpRequest()&quot;)      * @Configuration\\Method(&quot;POST&quot;)      *\/     public function newAction(Request $request)     {         $project = new Project();          $form = $this-&gt;createForm(ProjectForm::class, $project);         $form-&gt;handleRequest($request);          if ($form-&gt;isValid()) {             $this-&gt;getDoctrine()-&gt;getManager()-&gt;persist($project);             $this-&gt;getDoctrine()-&gt;getManager()-&gt;flush();              return new JsonResponse();         }         else {             $error = $form-&gt;getErrors(true)-&gt;current();              return new JsonResponse($error-&gt;getMessage(), JsonResponse::HTTP_BAD_REQUEST);         }     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u00ab\u0412\u044c\u044e\u00bb \u0444\u043e\u0440\u043c\u044b<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>{{ form_start(form) }} {{ form_row(form.name) }} {{ form_row(form.description) }} {{ form_end(form) }} <\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<h1>Simple Bus<\/h1>\n<p>  \u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 <em>Command Bus<\/em> \u0434\u043b\u044f PHP \u2014 \u043e\u0442 \u00abthephpleague\u00bb \u0434\u043e \u043e\u0442\u043a\u0440\u043e\u0432\u0435\u043d\u043d\u044b\u0445 NIH-\u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u043e\u0432. \u041b\u0438\u0447\u043d\u043e \u043c\u043d\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0430\u0441\u044c \u0432\u0435\u0440\u0441\u0438\u044f \u043e\u0442 Matthias Noback (\u0443 \u043d\u0435\u0433\u043e \u0432 \u0431\u043b\u043e\u0433\u0435 \u0435\u0441\u0442\u044c <a href=\"http:\/\/php-and-symfony.matthiasnoback.nl\/tags\/SimpleBus\/\">\u0441\u0435\u0440\u0438\u044f \u0441\u0442\u0430\u0442\u0435\u0439<\/a>, \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u043d\u044b\u0445 <em>Command Bus<\/em>) \u2014 <a href=\"https:\/\/packagist.org\/packages\/simple-bus\/message-bus\">SimpleBus<\/a>. \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0438 \u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043b\u044e\u0431\u043e\u043c PHP-\u043f\u0440\u043e\u0435\u043a\u0442\u0435. \u0414\u043b\u044f \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441 Symfony \u0435\u0441\u0442\u044c <a href=\"https:\/\/packagist.org\/packages\/simple-bus\/symfony-bridge\">\u0433\u043e\u0442\u043e\u0432\u044b\u0439 bundle<\/a> \u043e\u0442 \u0442\u043e\u0433\u043e \u0436\u0435 \u0430\u0432\u0442\u043e\u0440\u0430, \u0435\u0433\u043e \u0438 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c:<\/p>\n<pre><code>composer require simple-bus\/symfony-bridge <\/code><\/pre>\n<p>  \u041b\u044e\u0431\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u2014 \u043d\u0435 \u0431\u043e\u043b\u0435\u0435, \u0447\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435. \u0411\u0430\u043d\u0434\u043b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 <code>command_bus<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438.<\/p>\n<p>  \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u00ab\u043e\u0442\u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0442\u044c\u00bb \u043d\u0430\u0448 \u00ab\u044d\u043a\u0448\u0435\u043d\u00bb \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. HTML-\u0444\u043e\u0440\u043c\u0430 \u2014 \u043d\u0435 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043f\u0440\u043e\u0435\u043a\u0442 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 API, \u0438\u043b\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c \u0432 SOA-\u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u0438\u043b\u0438\u2026 \u0434\u0430 \u043c\u0430\u043b\u043e \u043b\u0438 \u043a\u0430\u043a \u0435\u0449\u0435), \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u043e\u0448\u0443 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0431\u043b\u0438\u0436\u0435 \u043a \u0441\u0430\u043c\u043e\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0435 (\u0447\u0430\u0441\u0442\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f), \u0442.\u0435. \u0438\u0437 \u0444\u043e\u0440\u043c\u044b \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a\u043e\u043c\u0430\u043d\u0434\u044b. \u0412 \u043e\u0431\u0449\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0438 \u043b\u044e\u0431\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u043e\u0447\u0435\u043a \u0432\u0445\u043e\u0434\u0430 \u043c\u044b \u0438\u0434\u0435\u043c \u0432 \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043e\u043a \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 (\u0434\u0430 \u0438 \u043b\u044e\u0431\u044b\u0445 \u0434\u0440\u0443\u0433\u0438\u0445) \u043c\u044b \u044d\u0441\u043a\u0430\u043b\u0438\u0440\u0443\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 \u0432\u0438\u0434\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439. \u0412 \u0438\u0442\u043e\u0433\u0435 \u043b\u044e\u0431\u043e\u0439 \u00ab\u044d\u043a\u0448\u0435\u043d\u00bb \u2014 \u044d\u0442\u043e \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 try-catch, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u044b \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u00ab200 OK\u00bb; \u0441\u0435\u043a\u0446\u0438\u044f <code>catch<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 HTTP-\u043a\u043e\u0434 \u00ab4xx\u00bb \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0432 \u0434\u0435\u043b\u0435:<\/p>\n<h4>\u0424\u043e\u0440\u043c\u0430<\/h4>\n<p>  \u0422\u0443\u0442 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e, \u0432 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u043c \u0444\u043e\u0440\u043c\u0430 \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0430\u0441\u044c.<\/p>\n<pre><code class=\"php\">class ProjectForm extends AbstractType {     public function buildForm(FormBuilderInterface $builder, array $options)     {         $builder-&gt;add('name', TextType::class, [             'label'    =&gt; 'Project name',             'required' =&gt; true,             'attr'     =&gt; ['maxlength' =&gt; Project::MAX_NAME],         ]);          $builder-&gt;add('description', TextType::class, [             'label'    =&gt; 'Project description',             'required' =&gt; false,             'attr'     =&gt; ['maxlength' =&gt; Project::MAX_DESCRIPTION],         ]);     }      public function getBlockPrefix()     {         return 'project';     } } <\/code><\/pre>\n<p>  <\/p>\n<h4>\u041a\u043e\u043c\u0430\u043d\u0434\u0430<\/h4>\n<p>  \u0410 \u0432\u043e\u0442 \u0442\u0443\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f.<\/p>\n<pre><code class=\"php\">namespace AppBundle\\SimpleBus\\Project;  use Symfony\\Component\\Validator\\Constraints;  \/**  * Create new project.  *  * @property string $name        Project name.  * @property string $description Description.  *\/ class CreateProjectCommand {     \/**      * @Constraints\\NotBlank()      * @Constraints\\Length(max = &quot;25&quot;)      *\/     public $name;      \/**      * @Constraints\\Length(max = &quot;100&quot;)      *\/     public $description; } <\/code><\/pre>\n<p>  <\/p>\n<h4>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a\u043e\u043c\u0430\u043d\u0434\u044b<\/h4>\n<p>  <\/p>\n<pre><code class=\"php\">namespace AppBundle\\SimpleBus\\Project\\Handler;  use AppBundle\\Entity\\Project; use AppBundle\\SimpleBus\\Project\\CreateProjectCommand; use Symfony\\Bridge\\Doctrine\\RegistryInterface; use Symfony\\Component\\HttpKernel\\Exception\\BadRequestHttpException; use Symfony\\Component\\Validator\\Validator\\ValidatorInterface;  class CreateProjectCommandHandler {     protected $validator;     protected $doctrine;      \/**      * Dependency Injection constructor.      *      * @param ValidatorInterface $validator      * @param RegistryInterface  $doctrine      *\/     public function __construct(ValidatorInterface $validator, RegistryInterface $doctrine)     {         $this-&gt;validator = $validator;         $this-&gt;doctrine  = $doctrine;     }      \/**      * Creates new project.      *      * @param  CreateProjectCommand $command      * @throws BadRequestHttpException      *\/     public function handle(CreateProjectCommand $command)     {         $violations = $this-&gt;validator-&gt;validate($command);          if (count($violations) != 0) {             $error = $violations-&gt;get(0)-&gt;getMessage();             throw new BadRequestHttpException($error);         }          $entity = new Project();          $entity             -&gt;setName($command-&gt;name)             -&gt;setDescription($command-&gt;description);          $this-&gt;doctrine-&gt;getManager()-&gt;persist($entity);         $this-&gt;doctrine-&gt;getManager()-&gt;flush();     } } <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b<\/h4>\n<p>  \u0427\u0442\u043e\u0431\u044b <code>command_bus<\/code> \u043d\u0430\u0448\u0435\u043b \u043d\u0430\u0448 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u0435\u0433\u043e \u043d\u0430\u0434\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u0441\u0435\u0440\u0432\u0438\u0441, \u043f\u043e\u043c\u0435\u0442\u0438\u0432 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u043c \u0442\u044d\u0433\u043e\u043c.<\/p>\n<pre><code class=\"ruby\">services:     command.project.create:         class: AppBundle\\SimpleBus\\Project\\Handler\\CreateProjectCommandHandler         tags: [{ name: command_handler, handles: AppBundle\\SimpleBus\\Projects\\CreateProjectCommand }]         arguments: [ &quot;@validator&quot;, &quot;@doctrine&quot; ] <\/code><\/pre>\n<p>  <\/p>\n<h4>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440<\/h4>\n<p>  \u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>showNewFormAction<\/code> \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0430\u0441\u044c (\u0434\u043b\u044f \u043a\u0440\u0430\u0442\u043a\u043e\u0441\u0442\u0438 \u043e\u043f\u0443\u0441\u0442\u0438\u043c \u0435\u0435), \u043f\u043e\u043c\u0435\u043d\u044f\u043b\u0441\u044f \u043b\u0438\u0448\u044c <code>newAction<\/code>.<\/p>\n<pre><code class=\"php\">class ProjectController extends Controller {     public function newAction(Request $request)     {         try {             \/\/ \u041d\u0430\u0448\u0430 \u0444\u043e\u0440\u043c\u0430 \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 &quot;project&quot;. \u0418\u043d\u0430\u0447\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e &quot;$request-&gt;request-&gt;all()&quot;.             $data = $request-&gt;request-&gt;get('project');              $command = new CreateProjectCommand();             $command-&gt;name        = $data['name'];             $command-&gt;description = $data['description'];              $this-&gt;container-&gt;get('command_bus')-&gt;handle($command);              return new JsonResponse();         }         catch (\\Exception $e) {             return new JsonResponse($e-&gt;getMessage(), $e-&gt;getStatusCode());         }     } } <\/code><\/pre>\n<p>  \u0415\u0441\u043b\u0438 \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c, \u0442\u043e \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043f\u0440\u0435\u0436\u043d\u044f\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u00ab\u044d\u043a\u0448\u0435\u043d\u0430\u00bb \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043b\u0430 12 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043d\u043e\u0432\u0430\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 11 \u0441\u0442\u0440\u043e\u043a. \u041d\u043e \u0432\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0430\u043b\u0438 (\u0434\u0430\u043b\u044c\u0448\u0435 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u0440\u043e\u0447\u0435 \u0438 \u0438\u0437\u044f\u0449\u043d\u0435\u0435), \u0430 \u0432\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, \u0443 \u043d\u0430\u0441 \u0441\u0444\u0435\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0432 \u0432\u0430\u043a\u0443\u0443\u0435\u043c\u0435. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0436\u0438\u0437\u043d\u0438 \u0443\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u00ab\u0440\u0430\u0437\u0434\u0443\u0432\u0430\u0442\u044c\u00bb \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0432 \u043f\u0435\u0440\u0432\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0438 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u043d\u0438\u043a\u0430\u043a \u0435\u0433\u043e \u043d\u0435 \u0437\u0430\u0442\u0440\u043e\u043d\u0435\u0442 \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c.<\/p>\n<p>  \u0415\u0441\u0442\u044c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043d\u044c\u044e\u0430\u043d\u0441. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0432\u0435\u043b \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0412 entity-\u043a\u043b\u0430\u0441\u0441\u0435 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f, \u043d\u043e \u0444\u043e\u0440\u043c\u0430-\u0442\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439. \u0418\u0437-\u0437\u0430 \u044d\u0442\u043e\u0433\u043e \u0432 \u043f\u0435\u0440\u0432\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u0440\u0435\u0434\u043a\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0433\u043e\u0440\u043e\u0434\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043e\u0448\u0438\u0431\u043e\u043a.<\/p>\n<p>  \u0412 \u043d\u0430\u0448\u0435\u0439 \u0436\u0435 command-\u0432\u0435\u0440\u0441\u0438\u0438 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 <code>persist($entity)<\/code> \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 exception \u2014 \u0435\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0441\u0430\u043c\u0430 ORM, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0432 \u043d\u0435\u0433\u043e \u0442\u043e \u0441\u0430\u043c\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0432 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u043a\u043b\u0430\u0441\u0441\u0430 <code>Project<\/code> (<em>\u00ab\u041f\u0440\u043e\u0435\u043a\u0442 \u0441 \u0442\u0430\u043a\u0438\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442\u00bb<\/em>). \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0441\u0430\u043c \u00ab\u044d\u043a\u0448\u0435\u043d\u00bb \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0441\u044f \u2014 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043b\u043e\u0432\u0438\u043c \u043b\u044e\u0431\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u0431\u044b \u0443\u0440\u043e\u0432\u043d\u0435 \u043e\u043d\u043e \u043d\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u043e, \u0438 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e \u0432 \u00abHTTP 400\u00bb.<\/p>\n<p>  \u041a\u0441\u0442\u0430\u0442\u0438, \u043d\u0430 \u0445\u0430\u0431\u0440\u0435 (\u0438 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e) \u0443\u0436\u0435 \u0431\u044b\u043b\u043e \u0441\u043b\u043e\u043c\u0430\u043d\u043e \u043d\u0435\u043c\u0430\u043b\u043e \u043a\u043e\u043f\u0438\u0439 \u043d\u0430 \u0442\u0435\u043c\u0443 \u00ab\u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0442\u0438\u0432 \u043e\u0448\u0438\u0431\u043e\u043a\u00bb. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 <a href=\"https:\/\/habrahabr.ru\/post\/279501\/\">\u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445<\/a> \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0445 \u0441\u0442\u0430\u0442\u0435\u0439  <a href=\"https:\/\/habrahabr.ru\/users\/alexleonov\/\" class=\"user_link\">AlexLeonov<\/a> \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u043b \u043d\u0435\u0447\u0442\u043e \u0431\u043b\u0438\u0437\u043a\u043e\u0435 \u043a \u043c\u043e\u0435\u043c\u0443 \u043f\u043e\u0434\u0445\u043e\u0434\u0443 (\u043e\u0448\u0438\u0431\u043a\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f), \u0438, \u0441\u0443\u0434\u044f \u043f\u043e \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c \u043a \u0435\u0433\u043e \u0441\u0442\u0430\u0442\u044c\u0435, \u043c\u043d\u0435 \u0442\u043e\u0436\u0435 \u0434\u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f. \u042f \u043f\u0440\u0438\u0437\u044b\u0432\u0430\u044e \u043d\u0430 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u043d\u0435 \u0445\u043e\u043b\u0438\u0432\u0430\u0440\u0438\u0442\u044c, \u0430 \u043f\u0440\u0438\u043d\u044f\u0442\u044c \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u043e\u0441\u0442\u044c \u043c\u043e\u044e \u0441\u043b\u0430\u0431\u043e\u0441\u0442\u044c \u043a \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0435 \u043a\u043e\u0434\u0430, \u0438 \u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u043c\u043d\u0435 \u0435\u0435, \u0435\u0441\u043b\u0438 \u0441\u043c\u043e\u0436\u0435\u0442\u0435 (\u0442\u0443\u0442 \u0431\u044b\u043b \u0441\u043c\u0430\u0439\u043b\u0438\u043a, \u043d\u043e \u043e\u043d \u0438\u0441\u043f\u0443\u0433\u0430\u043b\u0441\u044f \u043c\u043e\u0434\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 \u0438\u0441\u0447\u0435\u0437).<\/p>\n<h1>\u0410\u0432\u0442\u043e\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434<\/h1>\n<p>  \u0415\u0441\u043b\u0438 \u043f\u0440\u0438\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c\u0441\u044f \u043a \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>handle<\/code> \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0435\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430:<\/p>\n<ul>\n<li>\u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0443 \u043a\u043e\u0434\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438,<\/li>\n<li>\u044f\u0432\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c\u0441\u044f \u0438\u0437 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443,<\/li>\n<li>\u043b\u0435\u0433\u043a\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u0431\u044b\u0442\u0430 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u043e\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0435.<\/li>\n<\/ul>\n<p>  \u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e, <strong>SimpleBus<\/strong> \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u00abmiddlewares\u00bb \u2014 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043b\u044e\u0431\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b. Middleware-\u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0433\u043e\u0434\u043d\u043e, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043e\u0434\u043d\u0438 \u0438\u0437 \u043d\u0438\u0445 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043e \u043a\u043e\u043c\u0430\u043d\u0434, \u0430 \u0434\u0440\u0443\u0433\u0438\u0435 \u2014 \u043f\u043e\u0441\u043b\u0435, \u0432\u044b \u0434\u0430\u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0442\u044c \u0438\u043c \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u044b, \u0435\u0441\u043b\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u0430\u043a\u0438\u0445-\u0442\u043e middleware-\u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0432\u0430\u0436\u043d\u0430. \u041e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u043a\u043e\u043c\u0430\u043d\u0434 \u0432 middleware-\u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0438 \u0437\u0430\u0431\u044b\u0442\u044c \u043e \u043d\u0435\u0439 \u0432\u043e\u0432\u0441\u0435.<\/p>\n<pre><code class=\"php\">namespace AppBundle\\SimpleBus\\Middleware;  use Psr\\Log\\LoggerInterface; use SimpleBus\\Message\\Bus\\Middleware\\MessageBusMiddleware; use Symfony\\Component\\Validator\\Validator\\ValidatorInterface;  class ValidationMiddleware implements MessageBusMiddleware {     protected $logger;     protected $validator;      \/**      * Dependency Injection constructor.      *      * @param LoggerInterface    $logger      * @param ValidatorInterface $validator      *\/     public function __construct(LoggerInterface $logger, ValidatorInterface $validator)     {         $this-&gt;logger    = $logger;         $this-&gt;validator = $validator;     }      \/**      * {@inheritdoc}      *\/     public function handle($message, callable $next)     {         $violations = $this-&gt;validator-&gt;validate($message);          if (count($violations) != 0) {             $error = $violations-&gt;get(0)-&gt;getMessage();             $this-&gt;logger-&gt;error('Validation exception', [$error]);             throw new BadRequestHttpException($error);         }          $next($message);     } } <\/code><\/pre>\n<p>  \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0448 middleware:<\/p>\n<pre><code class=\"ruby\">services:     middleware.validation:         class: AppBundle\\SimpleBus\\Middleware\\ValidationMiddleware         public: false         tags: [{ name: command_bus_middleware }]         arguments: [ &quot;@logger&quot;, &quot;@validator&quot; ] <\/code><\/pre>\n<p>  \u0423\u043f\u0440\u043e\u0449\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a\u043e\u043c\u0430\u043d\u0434\u044b (\u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u0443\u0431\u0440\u0430\u0442\u044c \u043d\u0435\u043d\u0443\u0436\u043d\u0443\u044e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u0430):<\/p>\n<pre><code class=\"php\">class CreateProjectCommandHandler {     protected $doctrine;      \/**      * Dependency Injection constructor.      *      * @param RegistryInterface $doctrine      *\/     public function __construct(RegistryInterface $doctrine)     {         $this-&gt;doctrine = $doctrine;     }      \/**      * Creates new project.      *      * @param CreateProjectCommand $command      *\/     public function handle(CreateProjectCommand $command)     {         $entity = new Project();          $entity             -&gt;setName($command-&gt;name)             -&gt;setDescription($command-&gt;description);          $this-&gt;doctrine-&gt;getManager()-&gt;persist($entity);         $this-&gt;doctrine-&gt;getManager()-&gt;flush();     } } <\/code><\/pre>\n<p>  <\/p>\n<h1>\u041c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438<\/h1>\n<p>  \u041c\u043d\u043e\u0433\u0438\u0435 \u0438\u0437 \u0432\u0430\u0441 \u0443\u0436\u0435 \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435 \u0437\u0430\u0434\u0430\u043b\u0438\u0441\u044c \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u043c, \u043a\u0430\u043a \u0436\u0435 \u0431\u044b\u0442\u044c, \u0435\u0441\u043b\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0435 \u043e\u0434\u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0430, \u0430 \u0446\u0435\u043b\u044b\u0439 \u043d\u0430\u0431\u043e\u0440. \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u0435 \u0441\u0430\u043c\u0430\u044f \u0443\u0434\u0430\u0447\u043d\u0430\u044f \u0438\u0434\u0435\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u2014 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u0432\u0441\u0435 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0435 \u043f\u043e\u043b\u044f \u0444\u043e\u0440\u043c\u044b \u0437\u0430 \u043e\u0434\u0438\u043d \u0440\u0430\u0437.<\/p>\n<p>  \u042d\u0442\u043e, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u00ab\u0443\u0437\u043a\u043e\u0435\u00bb \u043c\u0435\u0441\u0442\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430. \u042f \u043d\u0435 \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u043b \u043d\u0438\u0447\u0435\u0433\u043e \u043b\u0443\u0447\u0448\u0435, \u043a\u0440\u043e\u043c\u0435 \u043a\u0430\u043a \u043a\u0438\u0434\u0430\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441 \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u043c \u043e\u0448\u0438\u0431\u043e\u043a. \u041c\u043e\u0439 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u043f\u0435\u0440\u0444\u0435\u043a\u0446\u0438\u043e\u043d\u0438\u0441\u0442 \u043e\u0447\u0435\u043d\u044c \u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442 \u043e\u0442 \u044d\u0442\u043e\u0433\u043e, \u043d\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u043d \u043d\u0435 \u043f\u0440\u0430\u0432, \u0431\u0443\u0434\u0443 \u0440\u0430\u0434 \u0443\u0441\u043f\u043e\u043a\u043e\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c. \u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442\u0441\u044f, \u0435\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442 \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u0430\u0447\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435.<\/p>\n<p>  \u0410 \u043f\u043e\u043a\u0430 \u2014 \u043d\u0430\u0448\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438:<\/p>\n<pre><code class=\"php\">class ValidationException extends BadRequestHttpException {     protected $messages = [];      \/**      * {@inheritdoc}      *\/     public function __construct(array $messages, $code = 0, \\Exception $previous = null)     {         $this-&gt;messages = $messages;          parent::__construct(count($messages) ? reset($this-&gt;messages) : '', $previous, $code);     }      \/**      * @return array      *\/     public function getMessages()     {         return $this-&gt;messages;     } } <\/code><\/pre>\n<p>  \u0421\u043b\u0435\u0433\u043a\u0430 \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u043c \u043d\u0430\u0448 \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u044e\u0449\u0438\u0439 middleware:<\/p>\n<pre><code class=\"php\">class ValidationMiddleware implements MessageBusMiddleware {     public function handle($message, callable $next)     {         $violations = $this-&gt;validator-&gt;validate($message);          if (count($violations) != 0) {             $errors = [];              foreach ($violations as $violation) {                 $errors[$violation-&gt;getPropertyPath()] = $violation-&gt;getMessage();             }              $this-&gt;logger-&gt;error('Validation exception', $errors);             throw new ValidationException($errors);         }          $next($message);     } } <\/code><\/pre>\n<p>  \u041d\u0443 \u0438 \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u0441\u0430\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 (\u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0441\u0435\u043a\u0446\u0438\u044f <code>catch<\/code>):<\/p>\n<pre><code class=\"php\">class ProjectController extends Controller {     public function newAction(Request $request)     {         try {             $data = $request-&gt;request-&gt;get('project');              $command = new CreateProjectCommand();             $command-&gt;name        = $data['name'];             $command-&gt;description = $data['description'];              $this-&gt;container-&gt;get('command_bus')-&gt;handle($command);              return new JsonResponse();         }         catch (ValidationException $e) {             return new JsonResponse($e-&gt;getMessages(), $e-&gt;getStatusCode());         }         catch (\\Exception $e) {             return new JsonResponse($e-&gt;getMessage(), $e-&gt;getStatusCode());         }     } } <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u00ab\u044d\u043a\u0448\u0435\u043d\u00bb \u0432\u0435\u0440\u043d\u0435\u0442 JSON-\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0433\u0434\u0435 \u043a\u043b\u044e\u0447\u0430\u043c\u0438 \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u043d\u0430 HTML-\u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u2014 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u0434\u043b\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u043e\u043b\u0435\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0432\u0432\u0435\u0441\u0442\u0438 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043b\u0438\u043d\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435:<\/p>\n<pre><code class=\"javascript\">{     &quot;name&quot;: &quot;\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c.&quot;,     &quot;description&quot;: &quot;\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u0440\u0435\u0432\u044b\u0448\u0430\u0442\u044c 100 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432.&quot; } <\/code><\/pre>\n<p>  \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043a\u043b\u044e\u0447\u0430\u043c\u0438 \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u043d\u0430 \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043d\u043e \u043c\u044b \u0436\u0435 \u043d\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043d\u0430\u0437\u0432\u0430\u043b\u0438 \u0438\u0445 \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u043e \u043f\u043e\u043b\u044f\u043c \u0444\u043e\u0440\u043c\u044b. \u0412\u043f\u0440\u043e\u0447\u0435\u043c, \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u0432\u044f\u0437\u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u043a\u043b\u0430\u0441\u0441\u0430 \u0441 \u043f\u043e\u043b\u044f\u043c\u0438 \u0444\u043e\u0440\u043c\u044b \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0439 \u2014 \u044d\u0442\u043e \u0432\u0430\u043c \u0440\u0435\u0448\u0430\u0442\u044c, \u043a\u0430\u043a \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u0435\u0442\u0435\u0432\u0448\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043a \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c \u00ab\u0444\u0440\u043e\u043d\u0442\u044d\u043d\u0434\u0430\u00bb. \u0414\u043b\u044f \u00ab\u0437\u0430\u0442\u0440\u0430\u0432\u043a\u0438\u00bb \u0432\u043e\u0442 \u0432\u0430\u043c \u043f\u0440\u0438\u043c\u0435\u0440 \u043c\u043e\u0435\u0433\u043e error-\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u0430:<\/p>\n<pre><code class=\"javascript\">$.ajax({     \/\/ ...     error: function(xhr) {         var response = xhr.responseJSON ? xhr.responseJSON : xhr.responseText;          if (typeof response === 'object') {             $.each(response, function(id, message) {                 var name = $('form').prop('name');                 var $control = $('#' + name + '_' + id);                  if ($control.length === 0) {                     alert(message);                 }                 else {                     $control.after('&lt;p class=&quot;form-error&quot;&gt;' + message + '&lt;\/p&gt;');                 }             });         }         else {             alert(response);         }     },     beforeSend: function() {         $('.form-error').remove();     } }); <\/code><\/pre>\n<p>  <\/p>\n<h1>\u0410\u0432\u0442\u043e\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b<\/h1>\n<p>  \u041a\u0430\u0436\u0434\u044b\u0439 \u043d\u0430\u0448 \u00ab\u044d\u043a\u0448\u0435\u043d\u00bb \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0435\u0435 \u043d\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443. \u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u044b\u0445 \u043f\u044f\u0442\u0438 \u00ab\u044d\u043a\u0448\u0435\u043d\u043e\u0432\u00bb \u044d\u0442\u043e \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0440\u0430\u0437\u0434\u0440\u0430\u0436\u0430\u0442\u044c \u0438 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438. \u041d\u0430\u043f\u0438\u0448\u0435\u043c trait, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u043d\u0430\u0448\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440-\u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440:<\/p>\n<pre><code class=\"php\">trait MessageTrait {     \/**      * \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0438\u0437 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u0430.      *      * @param array $values \u041c\u0430\u0441\u0441\u0438\u0432 \u0441 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.      *\/     public function __construct(array $values = [])     {         foreach ($values as $property =&gt; $value) {             if (property_exists($this, $property)) {                 $this-&gt;$property = $value;             }         }     } } <\/code><\/pre>\n<p>  \u0413\u043e\u0442\u043e\u0432\u043e. \u00ab\u041b\u0438\u0448\u043d\u0438\u0435\u00bb \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u044e\u0449\u0438\u0435 \u2014 \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 NULL-\u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438.<\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u00ab\u044d\u043a\u0448\u0435\u043d\u00bb \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"php\">class ProjectController extends Controller {     public function newAction(Request $request)     {         try {             $data = $request-&gt;request-&gt;get('project');              $command = new CreateProjectCommand($data);             $this-&gt;container-&gt;get('command_bus')-&gt;handle($command);              return new JsonResponse();         }         catch (ValidationException $e) {             return new JsonResponse($e-&gt;getMessages(), $e-&gt;getStatusCode());         }         catch (\\Exception $e) {             return new JsonResponse($e-&gt;getMessage(), $e-&gt;getStatusCode());         }     } } <\/code><\/pre>\n<p>  \u0410 \u0447\u0442\u043e, \u0435\u0441\u043b\u0438 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043c\u0438\u043c\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438\u0437 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430? \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 <code>editAction<\/code> \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u2014 ID \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0418 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430 \u043e\u0434\u043d\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u0431\u043e\u043b\u044c\u0448\u0435:<\/p>\n<pre><code class=\"php\">\/**  * Update specified project.  *  * @property int    $id          Project ID.  * @property string $name        New name.  * @property string $description New description.  *\/ class UpdateProjectCommand {     \/**      * @Constraints\\NotBlank()      *\/     public $id;      \/**      * @Constraints\\NotBlank()      * @Constraints\\Length(max = &quot;25&quot;)      *\/     public $name;      \/**      * @Constraints\\Length(max = &quot;100&quot;)      *\/     public $description; } <\/code><\/pre>\n<p>  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 \u0441 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438:<\/p>\n<pre><code class=\"php\">trait MessageTrait {     \/**      * \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0438\u0437 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u0430.      *      * @param array $values \u041c\u0430\u0441\u0441\u0438\u0432 \u0441 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.      * @param array $extra  \u041c\u0430\u0441\u0441\u0438\u0432 \u0441 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.      *                      \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430 \u043a\u043b\u044e\u0447\u0435\u0439 \u044d\u0442\u043e\u0442 \u043c\u0430\u0441\u0441\u0438\u0432 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e.      *\/     public function __construct(array $values = [], array $extra = [])     {         $data = $extra + $values;          foreach ($data as $property =&gt; $value) {             if (property_exists($this, $property)) {                 $this-&gt;$property = $value;             }         }     } } <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448 \u0433\u0438\u043f\u043e\u0442\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 <code>editAction<\/code> \u043c\u043e\u0433 \u0431\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"php\">class ProjectController extends Controller {     \/**      * \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 HTML-\u043a\u043e\u0434 \u0444\u043e\u0440\u043c\u044b.      *      * @Configuration\\Route(&quot;\/edit\/{id}&quot;, requirements={&quot;id&quot;=&quot;\\d+&quot;}, condition=&quot;request.isXmlHttpRequest()&quot;)      * @Configuration\\Method(&quot;GET&quot;)      *\/     public function showEditFormAction($id)     {         $project = $this-&gt;getDoctrine()-&gt;getRepository(Project::class)-&gt;find($id);          if (!$project) {             throw $this-&gt;createNotFoundException();         }          $form = $this-&gt;createForm(ProjectForm::class, $project, [             'action' =&gt; $this-&gt;generateUrl('edit_project'),         ]);          return $this-&gt;render('project\/form.html.twig', [             'form' =&gt; $form-&gt;createView(),         ]);     }      \/**      * \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 &quot;\u0441\u0430\u0431\u043c\u0438\u0442&quot; \u0444\u043e\u0440\u043c\u044b.      *      * @Configuration\\Route(&quot;\/edit\/{id}&quot;, name=&quot;edit_project&quot;, requirements={&quot;id&quot;=&quot;\\d+&quot;}, condition=&quot;request.isXmlHttpRequest()&quot;)      * @Configuration\\Method(&quot;POST&quot;)      *\/     public function editAction(Request $request, $id)     {         try {             $project = $this-&gt;getDoctrine()-&gt;getRepository(Project::class)-&gt;find($id);              if (!$project) {                 throw $this-&gt;createNotFoundException();             }              $data = $request-&gt;request-&gt;get('project');              $command = new UpdateProjectCommand($data, ['id' =&gt; $id]);             $this-&gt;container-&gt;get('command_bus')-&gt;handle($command);              return new JsonResponse();         }         catch (ValidationException $e) {             return new JsonResponse($e-&gt;getMessages(), $e-&gt;getStatusCode());         }         catch (\\Exception $e) {             return new JsonResponse($e-&gt;getMessage(), $e-&gt;getStatusCode());         }     } } <\/code><\/pre>\n<p>  \u041f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435 \u0445\u043e\u0440\u043e\u0448\u043e, \u043d\u043e \u0435\u0441\u0442\u044c \u043d\u044c\u044e\u0430\u043d\u0441 \u2014 \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u0443\u0441\u0442\u044b\u043c, \u043a \u043d\u0430\u043c \u043f\u0440\u0438\u043b\u0435\u0442\u0438\u0442 \u043f\u0443\u0441\u0442\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432 \u0438\u0442\u043e\u0433\u0435 \u0438 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0430 \u0432 \u0431\u0430\u0437\u0443, \u0445\u043e\u0442\u044f \u0432 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043f\u0438\u0441\u0430\u0442\u044c \u0432 \u0431\u0430\u0437\u0443 <code>NULL<\/code>. \u0420\u0430\u0441\u0448\u0438\u0440\u0438\u043c \u043d\u0430\u0448 trait \u0435\u0449\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e:<\/p>\n<pre><code class=\"php\">trait MessageTrait {     public function __construct(array $values = [], array $extra = [])     {         $empty2null = function ($value) use (&$empty2null) {              if (is_array($value)) {                 foreach ($value as &$v) {                     $v = $empty2null($v);                 }                  return $value;             }              return is_string($value) && strlen($value) === 0 ? null : $value;         };          $data = $empty2null($extra + $values);          foreach ($data as $property =&gt; $value) {             if (property_exists($this, $property)) {                 $this-&gt;$property = $value;             }         }     } } <\/code><\/pre>\n<p>  \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e (\u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u043b\u043e\u0434\u0438\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439), \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e (\u043c\u0430\u0441\u0441\u0438\u0432 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u043c) \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u043f\u043e \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c \u0438 \u043c\u0435\u043d\u044f\u0435\u0442 \u043f\u0443\u0441\u0442\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u043d\u0430 <code>NULL<\/code>.<\/p>\n<h1>\u0421\u043e\u0431\u044b\u0442\u0438\u044f<\/h1>\n<p>  \u041f\u043e\u043c\u0438\u043c\u043e \u043a\u043e\u043c\u0430\u043d\u0434 <strong>SimpleBus<\/strong> \u0443\u043c\u0435\u0435\u0442 \u0442\u0430\u043a\u0436\u0435 \u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f. \u0421\u0442\u0440\u043e\u0433\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u043d\u0435\u0432\u0435\u043b\u0438\u043a\u0430. \u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u043e\u043d\u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u043e \u2014 \u0432\u044b \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0435 \u043a\u043b\u0430\u0441\u0441-\u0441\u043e\u0431\u044b\u0442\u0438\u0435, \u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 (\u0442\u043e\u0447\u043d\u0435\u0435 \u2014 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u043e\u0432) \u0443 \u043d\u0435\u0433\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043c\u043d\u043e\u0433\u043e (\u0438\u043b\u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e). \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0438 \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c (\u043b\u0438\u0448\u044c \u0441 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0434\u0440\u0443\u0433\u0438\u043c \u0442\u044d\u0433\u043e\u043c), \u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u043c\u0438 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432 <strong>SimpleBus<\/strong> \u2014 <code>event_bus<\/code>.<\/p>\n<p>  \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0438 <code>simple_bus<\/code>, \u0438 <code>event_bus<\/code> \u2014 \u043e\u0431\u044b\u0447\u043d\u044b\u0435 Symfony-\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u043d\u0435\u0434\u0440\u044f\u0442\u044c \u0438\u0445 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043a\u0443\u0434\u0430 \u0443\u0433\u043e\u0434\u043d\u043e, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0438 \u0432 \u0432\u0430\u0448\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u043e\u0441\u043b\u0430\u043b\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0431\u044b\u043b \u0441\u043e\u0437\u0434\u0430\u043d \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n<h1>\u0412\u043c\u0435\u0441\u0442\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f<\/h1>\n<p>  \u041f\u043e\u043c\u0438\u043c\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u00ab\u0442\u043e\u0449\u0438\u0435\u00bb \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b, \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u043b\u0438 \u0441\u0435\u0431\u0435 unit-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043f\u0440\u043e\u0449\u0435 \u043e\u0442\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0432\u0437\u044f\u0442\u044b\u0439 \u043a\u043b\u0430\u0441\u0441-\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u043f\u0440\u0438\u0447\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u00ab\u0437\u0430\u043c\u043e\u043a\u0430\u0442\u044c\u00bb \u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0430 \u043c\u043e\u0436\u043d\u043e \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u0430\u0448 unit-\u0442\u0435\u0441\u0442 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043e\u0442 <code>Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase<\/code>. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0432 \u043b\u044e\u0431\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Symfony crawler (\u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u043a \u0441\u043b\u043e\u0432\u0443, \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0437\u0430\u043c\u0435\u0434\u043b\u044f\u0435\u0442 \u0442\u0435\u0441\u0442\u044b), \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0442\u043e\u0442 \u0438\u043b\u0438 \u0438\u043d\u043e\u0439 \u00ab\u044d\u043a\u0448\u0435\u043d\u00bb. \u0427\u0435\u0441\u0442\u043d\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u0442\u0435\u043f\u0435\u0440\u044c \u044f \u043f\u043e\u0440\u043e\u0439 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u044e \u00ab\u044d\u043a\u0448\u0435\u043d\u044b\u00bb \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u0440\u0430\u0437\u0432\u0435 \u0447\u0442\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e \u0438\u0445 \u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c, \u043a\u0430\u043a \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f Symfony.<\/p>\n<p>  \u0415\u0449\u0435 \u043e\u0434\u043d\u0438\u043c \u043d\u0435\u043e\u0441\u043f\u043e\u0440\u0438\u043c\u044b\u043c \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u043f\u043e \u0441\u0443\u0442\u0438 \u043e\u0442\u043e\u0440\u0432\u0430\u043b\u0438 \u043d\u0430\u0448\u0443 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443 \u043e\u0442 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 (\u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043a\u043e\u043d\u0435\u0447\u043d\u043e). \u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u043d\u0435\u0434\u0440\u044f\u044e\u0442\u0441\u044f \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438, \u0430 \u0438\u0437 \u043a\u0430\u043a\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u043e\u043d\u0438 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u2014 \u0443\u0436\u0435 \u043d\u0435 \u0432\u0430\u0436\u043d\u043e. \u041e\u0434\u043d\u0430\u0436\u0434\u044b <a href=\"http:\/\/www.php-fig.org\/\">FIG<\/a> \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0438\u0437\u0430\u0446\u0438\u044e \u0432\u0441\u0435\u0445 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432, \u0438 \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0431\u0440\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u0442\u044c \u0438\u0445 \u0438\u0437-\u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u0430 \u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442 \u0434\u0440\u0443\u0433\u043e\u0433\u043e. \u0414\u0430\u0436\u0435 \u0440\u0430\u0437\u0434\u0440\u043e\u0431\u043b\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 \u043f\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043f\u043b\u044e\u0441\u043e\u043c, \u0435\u0441\u043b\u0438 \u043e\u0434\u043d\u0430\u0436\u0434\u044b \u0432\u0430\u0441 \u0438\u043b\u0438 \u0432\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u0443\u043a\u0443\u0441\u0438\u0442 SOA.<\/p>\n<p>  \u041a\u0441\u0442\u0430\u0442\u0438, \u0435\u0441\u043b\u0438 \u0432\u044b (\u043a\u0430\u043a \u0438 \u044f) \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043f\u0438\u0441\u0430\u043b\u0438 \u043d\u0430 Java, \u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u043d\u0435 \u0430\u0441\u0441\u043e\u0446\u0438\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u0430\u0441 \u0441\u043e \u0441\u043b\u043e\u0432\u043e\u043c \u00ab\u0433\u0430\u0440\u043c\u043e\u043d\u0438\u044f\u00bb, \u0442\u043e \u0432\u044b \u0434\u0430\u0436\u0435 \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u043d\u044b \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043a\u043b\u0430\u0441\u0441\u0435 (\u0445\u043e\u0442\u044f \u043b\u0438\u0447\u043d\u043e \u043c\u043d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f). <strong>SimpleBus<\/strong> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0432 \u043e\u0434\u0438\u043d \u043a\u043b\u0430\u0441\u0441, \u0442\u0430\u043a \u0447\u0442\u043e \u0432\u044b \u0432\u043f\u043e\u043b\u043d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u043c\u0435\u0442\u044c \u043f\u043e \u043a\u043b\u0430\u0441\u0441\u0443 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 entity, \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0443\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439.       <\/p>\n<div class=\"clear\"><\/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:\/\/habrahabr.ru\/post\/280512\/\"> https:\/\/habrahabr.ru\/post\/280512\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>       \u0423\u0436\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u0433\u043e\u0434\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043f\u0430\u0442\u0442\u0435\u0440\u043d <em>Command Bus<\/em> \u0432 \u0441\u0432\u043e\u0438\u0445 Symfony-\u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0438 \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u0440\u0435\u0448\u0438\u043b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043e\u043f\u044b\u0442\u043e\u043c. \u0412 \u043a\u043e\u043d\u0446\u0435\u0432 \u043a\u043e\u043d\u0446\u043e\u0432 \u043e\u0431\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u0432 Laravel \u044d\u0442\u043e \u0435\u0441\u0442\u044c \u00ab\u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438\u00bb, \u0430 \u0432 Symfony, \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e Laravel \u0432\u043e \u043c\u043d\u043e\u0433\u043e\u043c \u0432\u044b\u0440\u043e\u0441 \u2014 \u043d\u0435\u0442, \u0445\u043e\u0442\u044f \u0441\u0430\u043c\u043e\u043c\u0443 \u043f\u043e\u043d\u044f\u0442\u0438\u044e <a href=\"http:\/\/martinfowler.com\/bliki\/CommandQuerySeparation.html\">Command\/Query Separation<\/a> \u0443\u0436\u0435 \u043d\u0435 \u043c\u0435\u043d\u0435\u0435 10 \u043b\u0435\u0442. \u0418 \u0435\u0441\u043b\u0438 \u0441 \u0431\u0443\u043a\u0432\u043e\u0439 \u00abQ\u00bb \u0438\u0437 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u044b \u00abCQRS\u00bb \u0435\u0449\u0435 \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c (\u043b\u0438\u0447\u043d\u043e \u043c\u0435\u043d\u044f \u0432\u043f\u043e\u043b\u043d\u0435 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0442 custom repositories), \u0442\u043e \u043a\u0443\u0434\u0430 \u043f\u0440\u0438\u0442\u043a\u043d\u0443\u0442\u044c \u0431\u0443\u043a\u0432\u0443 \u00abC\u00bb \u2014 \u043d\u0435\u044f\u0441\u043d\u043e.<\/p>\n<p>  \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u0434\u0430\u0436\u0435 \u0432 \u0431\u0430\u043d\u0430\u043b\u044c\u043d\u044b\u0445 CRUD-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 Command Bus \u0434\u0430\u0435\u0442 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430:<\/p>\n<ul>\n<li>\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u00ab\u0445\u0443\u0434\u044b\u043c\u0438\u00bb (\u0440\u0435\u0434\u043a\u0438\u0439 \u00ab\u044d\u043a\u0448\u0435\u043d\u00bb \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 15 \u0441\u0442\u0440\u043e\u043a),<\/li>\n<li>\u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u043e\u043a\u0438\u0434\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b \u0438 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0439 \u043e\u0442 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 (\u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0435\u0435 \u043d\u0435\u0441\u043b\u043e\u0436\u043d\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u043d\u0435 \u043d\u0430 Symfony),<\/li>\n<li>\u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442\u0441\u044f unit-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438,<\/li>\n<li>\u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u0434\u0430 (\u043a\u043e\u0433\u0434\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u00ab\u0444\u0438\u0447\u0443\u00bb \u043a\u0430\u043a \u0447\u0435\u0440\u0435\u0437 Web UI, \u0442\u0430\u043a \u0438 \u0447\u0435\u0440\u0435\u0437 API).<\/li>\n<\/ul>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/a85\/803\/f0a\/a85803f0aa724e9385e799f085634f02.png\" alt=\"\u041a\u0414\u041f\u0412\"\/>  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-277270","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/277270","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=277270"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/277270\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=277270"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=277270"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=277270"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}