Skip to content

[DX] ADR usage #8153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 29 commits into from
Closed

[DX] ADR usage #8153

wants to merge 29 commits into from

Conversation

Guikingone
Copy link
Contributor

@Guikingone Guikingone commented Jul 14, 2017

With the new approach used by the container, the old controller can be easily replaced by the ADR pattern, as Symfony grow, I think it should be the role of the documentation to show how to use the pattern and how to adapt the framework structure.

PS : I'm not pretty sure of the 'note' syntax.

With the new approach used by the container, the old controller can be replaced easily by the ADR pattern, as Symfony grow, I think it should be the role of the documentation to show how to use the pattern and how to adapt the framework structure.

PS : I'm not pretty sure of the 'note' syntax.
Remove the PHP 7.1 syntax.
Removed the ..note block.
Copy link
Member

@yceruto yceruto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one documents part of symfony/symfony#21723 so we may add a note for this feature @Route("/hello", name="hello") explaining when this only work? and close #7549 then ;) Thanks!

*/
final class HelloAction
{
/** @var EngineInterface */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docblock can be removed

* HelloAction constructor.
*
* @param EngineInterface $renderEngine
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docblock can be removed

* @param EngineInterface $renderEngine
*/
public function __construct(
EngineInterface $renderEngine
Copy link
Member

@yceruto yceruto Jul 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be one line?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just engine? this do a bit more than only render ;)

* @throws \RuntimeException
*
* @return Response
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docblock can be removed

in order to work, you must update the services.yml file ::

parameters:
#parameter_name: value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be removed parameters section?


/**
* Class HelloAction
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be removed

methods: ['GET']
defaults: { _controller: AppBundle\Action\HelloAction }

.. code-block:: php-annotations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

php annotations should be the first (as recommended way)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@yceruto yceruto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more... ;)

# app/config/routing.yml
hello:
path: /hello
methods: ['GET']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requirement has not been configured in the rest of formats so I guess can be removed?

{
return new Response(
$this->renderEngine->render('default/index.html.twig')
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be in one line

* @param EngineInterface $renderEngine
*/
public function __construct(
EngineInterface $renderEngine
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just engine? this do a bit more than only render ;)

@Guikingone
Copy link
Contributor Author

@yceruto For the docblocks, yes, for sure, I've updated the commits.

For the code, I'm not in the same position, if you look at the PSR (especially the 2 http://www.php-fig.org/psr/psr-2/#overview), they say clearly that a line should be under 80:

  • There MUST NOT be a hard limit on line length; the soft limit MUST be 120 characters; lines SHOULD be 80 characters or less.

If you pass the __construct in one line, yes, that works but for the Response return, that's over 85 characters long, way too much in my opinion.

For the config files, that's a personal opinion but the requirements key MUST be mandatory and in fact, Insight asks for this key if she's not defined, in my opinion, the documentation should show the right way of using the files and classes.

For the parameters key, I think she can stay for simple visibility and easier understanding of the whole file but in the "simplest" way of configuring the service, yes, she can be removed.

For the PR, I'm not sure of understanding your vision :/

public function __construct(
EngineInterface $engine
) {
$this->renderEngine = $render;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$this->engine = $engine ? and fix related use


public function __construct(
EngineInterface $engine
) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public function __construct(EngineInterface $engine) one line?

@Guikingone
Copy link
Contributor Author

@yceruto Should be better.

@yceruto
Copy link
Member

yceruto commented Jul 14, 2017

Please, I'm only address you to Coding Standards for Symfony project (i.e these aren't my personal preferences).

For the code, I'm not in the same position, if you look at the PSR (especially the 2 http://www.php-fig.org/psr/psr-2/#overview), they say clearly that a line should be under 80

Following http://symfony.com/doc/current/contributing/code/standards.html#structure:

Declare all the arguments on the same line as the method/function name, no matter how many arguments there are;

That's applicable to:

@Guikingone
Copy link
Contributor Author

Should be better, I understand your vision, I continue to use the PSR rules into my projects, that's why I recommend the multi-lines approach :).

For the annotations panels order, I've just followed the order used in the first part of the document, no more :)

Change the order of the routing files and deleted the methods key in yaml routing.
@Guikingone
Copy link
Contributor Author

@yceruto Should be better, I've removed the methods key and change the order of the recommended routing configuration.

For the parameters key, I think about using '//' in order add a reminder that some keys are missing from the example but that's just for the example, what do you think of this approach?

// ...

    services:
         _defaults:
             autowire: true
             autoconfigure: true
             public: false
             
         # Allow to load every actions
         AppBundle\Action\:
             resource: '../../src/AppBundle/Action/'
             public: true

@Guikingone
Copy link
Contributor Author

Guikingone commented Jul 17, 2017

@yceruto Services.yml corrected, I've use the # ... syntax, Yaml doesn't support // ..., I've thought about this after my comments :(

EDIT : Corrected the indentation bug.

@Guikingone
Copy link
Contributor Author

@yceruto Hi, small question about this PR, should I update the usage after the release of Flex (in Alpha mode, I know but that's a major release) via the blog ?

Flex change some configuration added via this PR and it's probably a good idea to show via the doc how to inject the ADR logic using Flex.

Probably @javiereguiluz can help us to decide ?

Copy link
Contributor

@HeahDude HeahDude left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for proposing this! Not sure this is the best place to do it though.

@@ -80,6 +80,98 @@ If your controller implements the ``__invoke()`` method - popular with the
Action-Domain-Response (ADR) pattern, you can simply refer to the service id
(``AppBundle\Controller\HelloController`` or ``app.hello_controller`` for example).

If you want to use the ADR pattern rather than the default controller approach, you can use
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this deserves its own article to be referenced here.

resource: '../../src/AppBundle/Action/'
public: true

Once the file is updated, delete your Controller folder and create an Action one then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Once the file is updated, delete your Controller folder and create an Action class using the ADR principles, i.e ::"

namespace AppBundle\Action;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Templating\EngineInterface;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component is to be deprecated, a Twig Environment should be used instead.


final class HelloAction
{
private $render;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be named $twig or $renderer.


// app/config/routing.php
$collection->add('hello', new Route('/hello', array(
'_controller' => 'AppBundle\Action\HelloAction',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HelloAction::class

@HeahDude HeahDude added this to the 3.3 milestone Jul 29, 2017
@Guikingone
Copy link
Contributor Author

@HeahDude Hi, thanks for the review, in fact, after reading your messages, I was thinking about a different approach, we all know that the doc should reflect the current usage of the framework, in this approach, I was thinking of proposing a new chapter in the "Guides" part, I don't have a specific name in mind but something like "Logic" or "Pattern" could be useful, in this chapter, I was thinking about linking to specific chapter about ADR, API or even MVC (using the Sf approach and actual code), we can build a specific API chapter like @javiereguiluz was talking about in the dedicated Slack then adding the ADR chapter, this way, we can allow the visitor to find and easily see how the pattern/logic can be adapted into Sf via example and dedicated tips, any thought about this ?

For the corrections, I correct all your recommendations as soon as I'm back on my computer.

Updating the classes call and prepare for dedicated article.
@Guikingone
Copy link
Contributor Author

@HeahDude Just corrected the classes call and prepare for the dedicated article, gonna clone the repo on my computer and add the new file as soon as possible (probably before Sunday).

@Guikingone
Copy link
Contributor Author

@HeahDude Hi, I've just added the dedicated file and the link from service.rst.

}
}

Final though
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought?

@Guikingone
Copy link
Contributor Author

Updated the Request call then the Twig render() call.

public function __construct(Environment $twig)
{
$this->twig = $twig;
}
Copy link
Member

@yceruto yceruto Sep 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking again about this approach, if we have just one method/controller, shouldn't be simpler inject the required dependencies into __invoke() directly? Thus, we are avoiding define one property per service argument, if so the action service needs to be tagged with controller.service_arguments.

After

final class HelloAction
{
    public function __invoke(Environment $twig): Response
    {
         return new Response($twig->render('default/index.html.twig'));
    }
}

@Guikingone
Copy link
Contributor Author

@yceruto It can work but this make appear the "controller" word inside a "pattern" who don't use it, for me, it can be strange for newcomer (and even for experimented developer) to use the word controller when your class became an Action.

This says, @dunglas can probably say more than me about this pattern.

Corrected the service injection and services configuration.
@Guikingone
Copy link
Contributor Author

@yceruto Just made the corrections on service injection via the tags, sorry for the delay.

AppBundle\Action\:
resource: '../../src/AppBundle/Action/'
tags: ['controller.service_arguments']
public: true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior has been modified for 3.3, now all the services tagged with controller.service_arguments are public by definition and according to #8415 we can remove it so.

As the framework evolve, you must update the services.yml file in order to
use the latest features of the DependencyInjection component, this way, here's the updates::

# ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be removed?

@Guikingone
Copy link
Contributor Author

@yceruto Should be better.

@Guikingone
Copy link
Contributor Author

@HeahDude Your requested changes stay blocking while they were accepted and corrected, bug?

Added a Responder call and the configuration linked.
@Guikingone
Copy link
Contributor Author

Just added a new block for Responder injection and configuration.

Fix the yaml call and the extra line in the responder configuration block.

In Symfony, you're used to implement the MVC pattern and extending the default :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller`
class.
Since the 3.3 update, Symfony is capable of using natively the ADR approach.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is worth it to add a link to https://github.com/pmjones/adr

Just added the link to the official ADR repository.
@Guikingone
Copy link
Contributor Author

@dunglas Should be better.

How to implement the ADR pattern
================================

In Symfony, you're used to implement the MVC pattern and extending the default :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symfony has never promoted the MVC pattern (at least, not since version 2). Instead, we are presenting Symfony as being a Request/Response framework where the controller converts a Request to a Response via a Controller.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, my apologies for this words, the meaning of my "text" was to clearly say that a lot of developers use Symfony with MVC and that using this pattern is way more used than just transforming a Request into Response (which is handled by the framework in a certain way, the actual transformation occurs in the controller).

tags:
- 'controller.service_arguments'

Now that the container knows about our actions, time to build a simple Action !
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra whitespace before the !

Updating your classes
---------------------

As the framework evolve, you must update your classes, first, delete your Controller folder and create an Actions one then a new class using the ADR principles, for this example, call it ``HelloAction.php``:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: evolves

Keep in mind that this approach can be completely different from what you're used to use, in order to
keep your code clean and easy to maintain, we recommend to use this approach only if your code is
decoupled from the internal framework logic (like with Clean Architecture approach) or if you start a new
project and need to keep the logic linked to your business rules.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this last paragraph and the supposed benefits. I don't see how this approach or what you refer to the old one is cleaner or easier. I don't understand why this approach is more decoupled than the other one. I'm not saying this is good or bad, just that from a beginner's perspective, the benefits are not very clear.

@@ -80,6 +80,11 @@ If your controller implements the ``__invoke()`` method - popular with the
Action-Domain-Response (ADR) pattern, you can simply refer to the service id
(``AppBundle\Controller\HelloController`` or ``app.hello_controller`` for example).

As this approach is evolving faster and can be easily transposed into Symfony, we recommend
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is it evolving?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this stage, not so much, like before, there's was an error in the real sense of my words, ADR still young and the "logic" keep evolving with time and usage by the developers, the true meaning was to say that using ADR still a "WIP" in Symfony and as the "logic" of ADR still evolving by the feedbacks of the developers, his implementation inside Symfony can be modified with future evolution of the framework or pattern.

In fact, it was possible since Symfony 3.1 when recompiling the container (or like @dunglas do it, by injecting the classes as controllers and mapping the return of each one of them) but using it in production is way easier since 3.3 and 3.4 (and even easier in 4.0)

javiereguiluz added a commit that referenced this pull request Feb 18, 2018
This PR was merged into the 4.0 branch.

Discussion
----------

Controller naming habits

As Symfony isn't an `MVC` framework but a `Request/Response` one (see #8153 (comment)), the name of the class which handles the Request isn't forced to contain `Controller`, it's probably more a common usage than a need to make Symfony works.

Commits
-------

5b29f5b Minor reword
2b61e3b suggest(Controller): suggestion about naming habits
@Guikingone Guikingone changed the title Adding the ADR approach. [DX] ADR usage Feb 11, 2019
@wouterj wouterj modified the milestones: 3.3, 3.4 Apr 14, 2019
@javiereguiluz
Copy link
Member

What should we do with this proposal? I don't see people talking or promoting ADR on Symfony Slack or blogs or web sites or aggregators like Hacker News. Should we close without merging?

@Guikingone
Copy link
Contributor Author

For me, as ADR is only an evolution of MVC (which is mainly used in today's applications), I think that we can close this PR, it's not the role of Symfony to promote this type of architecture as everyone can have a different point of view when it comes to how to implement it 🙂

@fabpot
Copy link
Member

fabpot commented Sep 27, 2019

I agree that this is opinionated and should not be part of the official docs.

@javiereguiluz
Copy link
Member

OK, let's close then. Guillaume thanks for being so understanding. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.