-
Notifications
You must be signed in to change notification settings - Fork 0
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
Feature/create article #20
Conversation
af5cd4a
to
0467f39
Compare
|
||
public static function write(string $title, string $content, string $slug): self | ||
{ | ||
return new self($title, $content, $slug); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read somewhere that it was more readable, in a business logic, to have Article::write()
rather than 'new Article()' because "creating" and article has little meaning, its part of the abstraction glossary we created with the RAD paradigm.
Do you agree ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I totally agree, as long as it makes sense, semantically speaking. BTW, this is called a named constructor, and it’s an important part of DDD. I would not use this systematically though, but only when it’s appropriate, as it’s the case here.
It’s very true that every time you write some part of your domain model, you should think more about the semantics of your domain rather than the semantics of the programming language being used. Sticking to the semantics of your domain, in DDD, basically means: «How would the user describe that action, in their own words?», and indeed, in this context it makes more sense to «write an article» rather than «create a new article».
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The major concern I have about the implementation is more about the folder structure: everything is at the same level, whereas we should probably vace clear layers identified by the project structure
Absolutely 😉 And there are no «official» rules for that matter, you have to figure out what makes the most sense to you 🙂 You can take a look at WeShop’s codebase to have my point of view on the subject, and you can take a look here to have Pedro’s point of view. Personally, I like to have clear Application
and Domain
layers (we’re on the same page with Pedro about this), but for the rest, I just scatter infrastructure-related stuff and vendor-cutomization-related stuff over specific, meaningful folders. I don’t say I’m right about this, but it’s what makes sense to me 🙂
|
||
public static function write(string $title, string $content, string $slug): self | ||
{ | ||
return new self($title, $content, $slug); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I totally agree, as long as it makes sense, semantically speaking. BTW, this is called a named constructor, and it’s an important part of DDD. I would not use this systematically though, but only when it’s appropriate, as it’s the case here.
It’s very true that every time you write some part of your domain model, you should think more about the semantics of your domain rather than the semantics of the programming language being used. Sticking to the semantics of your domain, in DDD, basically means: «How would the user describe that action, in their own words?», and indeed, in this context it makes more sense to «write an article» rather than «create a new article».
return new JsonResponse( | ||
["error" => $exception->getMessage()], | ||
Response::HTTP_INTERNAL_SERVER_ERROR | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Je sais pas ce que tu en penses, mais personnellement, j’aime bien que mes actions soient view-agnostic, ce qui veut dire concrètement que je préfère leur faire renvoyer un format de données générique, par exemple un array
, et ensuite je me démerde avec Symfony (avec un kernel listener, ou bien SensioFrameworkExtraBundle
par exemple) pour qu’il transforme cette array
en une réponse HTTP à posteriori.
Autre possibilité si on veut être davantage explicite et ne pas utiliser la «Symfony magic»: créer un service, qu’on injecterait dans chaque action, à qui on passerait l’array
de sortie, et qui lui se démerderait pour formater ça pour la vue. L’important, à mon avis, étant de s’abstraire quoi qu’il arrive du format de sortie, et de ne pas générer les réponses directement dans le body des actions, mais de déléguer cette tâche à un service, afin de réduire le couplage.
À ce sujet, je sais pas si tu connais le pattern ADR (Action-Domain-Responder), qui est un genre de réinterprétation de MVC mais davantage adapté au web. Je trouve ça à la fois simple et complètement censé. En l’occurrence, ce dont je parlais ci-dessus correspond donc au Responder de ce modèle. Ce mini-site résume très bien le truc: http://pmjones.io/adr/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be done in another pr ;)
$createArticle = new Command\CreateArticle(); | ||
|
||
$form = $this->formFactory->create(ArticleType::class, $createArticle); | ||
$form->submit($request->request->all()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ça me semble un peu cavalier comme manière de submit un form 😄 Généralement, je crois qu’il est plutôt conseillé d’utiliser $form->handleRequest($request)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmmh je le fait comme ca parce que handleRequest()
aime pas trop qu'on lui envoit un payload en json, même si il a été décomposé auparavant.
->add('content', TextareaType::class, [ | ||
'constraints' => [ | ||
new Asserts\NotBlank(), | ||
new Asserts\NotNull(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Je crois que NotNull
est redondant avec NotBlank
😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, il me semble pas 🤔 IIRC NotBlank verifie que le string n'est pas vide, alors que NotNull verifie que la valeur n'est pas null.
{ | ||
return $this->repository->findAllPublished(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Je sais plus si je t’en avais parlé, et c’est évidemment à toi de voir si tu veux t’emmerder à ce point-là, mais personnellement, je crois que dans mes prochains projets, je ne vais plus utiliser des commandes et un command bus pour les lectures. Si on respecte un minimum CQRS, c’est de toute façons illogique par définition. J’imaginais par exemple utiliser des «Presenters», qui seraient de bêtes services, qui la plupart du temps encapsuleraient juste un repository Doctrine certainement, et feraient le passe-plat pour les lectures. On pourrait permettre aux contrôleurs d’utiliser les repositories directement, mais c’est un peu touchy, dans la mesure où un repository peut être utilisé pour lire ET modifier le state du modèle. Je sais pas trop. En fait je me dis que ça peut vite devenir over-engineered aussi, donc bon… Bon, au pire, oublie ce que j’ai dit, mais clairement j’ai l’intuition qu’utiliser un command bus pour les lectures, ce n’est pas fait pour ça.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Je suis entièrement d'accord, c'est overkill. La ca relève davantage d'une volonté d'avoir un traitement similaire pour tous les endpoints, mais en effet ca n'a pas de sens d'avoir des commandes pour ca. Je pense que dans les prochaines PRs, le controlleur aura directement accès au repository, je n'utiliserais le commande bus que pour les ecritures.
a2c6e5d
to
f81d0ac
Compare
The command bus logic has been introduced here #16
This PR is an attempt to decouple domain related components from Symfony ones using the command pattern in a very simple use case: writing an article.
In this scenario, the controller creates and validate the form, map its content on a command (DTO), then throw it to the bus. The bus resolves the command handler, which instanciates an entity and persist it.
The major concern I have about the implementation is more about the folder structure: everything is at the same level:
whereas we should probably vace clear layers identified by the project structure, such as
What do you think ?