In recent days I saw several examples of "actions" in #Laravel, but one especially got my attention and I wanted to clarify it - so we had little discussion with @wendell_adriel and I would like to share my opinion about this particular approach 🙂
Before we start, one important preface: I'm big fan of explicitness and testability, so I'm using dependency injection everywhere (where's applicable). Examples in this thread will use it too by default, along with #PHP 8.0+ syntaxes.
What caught my attention was instantiating action inside controller's method 🤔 That looked suspicious, because I'm used to different nomenclature.
My assumption after looking at the example was that `CreateUserAction` is more like a #CQRS command rather than action.
What I personally see as "action" is a single-purpose request handler. While multi-method controllers contain multiple actions and somehow break SRP principle, single invokable controllers are crafted to handle one particular kind of requests (actions).
It's also part of ADR (action–domain–responder) pattern 🙂
The difference between those two approaches is that multi-action controllers require all dependencies from every method, while single-action controllers require only dependencies used in this one particular action.
The latter makes instantiating and testing easier.
Let's go back to Wendell's example. What baffled me, was that "action" was instantiated but wasn't actually called inside of `UserController::store()` method (which is... an action itself). That doesn't look good to me not only from semantics point of view.
The thing is `UserController::store()` looks pretty magic. Assuming this action should store user somewhere, it probably requires persistence service (database?). Also we can see it throws some validation exception. But there's only DTO passed to action's constructor... 🤔
So we can safely assume that `CreateUserAction` contains hardcoded instantiation of objects or is using Laravel facades/helpers inside. Either way, I don't like it. As I said, I prefer explicit dependencies while this approach hides those relations inside the action class.
I don't know implementation details, wether it's custom stuff or some Laravel off-the-shelf package, but it looks like it has logic in constructor or maybe in jsonSerialize() method. It's just created in controller and has to be transformed to JSON response somehow 🤷♂️
There's always possibility that example was oversimplified for tweet purposes and in real application it's done slightly different 😅 Anyway, that doesn't stop us from discussing it, right?
Wendell said it's done like this because he would like to have multi-method controllers calling separate actions, instead of service methods. In general it makes sense, but IMHO not with this particular implementation 😉
I agree that controller should stay as thin as possible. Get data from the request, encapsulate it somehow and pass deeper. But personally I would use #CQRS, so controller would only prepare command/query and pass it to the proper bus (for queries it also should return result).
This way we also would achieve logic segregation, but without introducing new custom concept or tight-coupling with particular package. Yes, having CQRS buses we also make some coupling, but it can be done by introducing app-level contracts & interchangeable implementation.
In the end we could have command/queries, DTOs and handlers - all universal and framework agnostic. The only infrastructure part would be bus implementation and its usage (passing command/queries). Everything testable, maintainable and extendable (through middlewares).
The great advantage of this approach is that command bus offers universal contract for your app. You can have multiple different entry points sharing the same logic (command handler). For example you can have CLI command that uses the same command, but gathers data differently.
Yes, I'm aware of laravelactions.com and while it looks like it solves the original problem (reusing action's logic in different contexts), it introduces highly-coupling conventions that make our code pretty much Laravelish. It's the opposite of what I like.
I believe Wendell's approach could be modified slightly to achieve something similar to command bus described above, without actually introducing bus. If `CreateUserAction` was injected as invokable service to the controller, it could take advantage of DI, without magic.
💡Command Bus can be easily implemented within your app using @symfony Messenger component. It's really good, flexible & extendable solution for dispatching synchronous and asynchronous messages 😉 For more info, just read the docs:
So let's sum it up. I see such issues in his example:
- misleading "action" concept (probably subjective, vide Laravel Actions package)
- there's too much magic behind probably, I would prefer this action-service to be injected by DI (so it could have its own dependencies)
Some may find my thoughts as clinginess and/or #overengineering - I'm fine with it. Others doesn't need to like what I like 🙂 But we're here to discuss, aren't we?
Let me know what you think and how you deal with reusing logic in your apps 😎
Thank you for reading! 🍻
• • •
Missing some Tweet in this thread? You can try to
force a refresh
While I don't care much about numbers, I check #TwitterAnalytics from time to time mostly because of monthly summaries. Let me tell you that last 28 days were pretty intense comparing to previous period 😁
Let's look what happened! 👇
First of all @davorminchorov mentioned me in his thread about enums. We initially had discussion about them under his PR (github.com/learnhubdev/la…), but this thread extended it even more 🙂
Dictionary is set of values that are supported in your application. But how they should be defined? Well, as always, it depends 😉 #PHP#architecture
Let me tell you what I think about it 👇🧵
In general, I believe there are two types of dictionaries:
1) Internal, related to your app's logic 2) UI-managable, related to app instance's data
Let's look about difference between them 🙂
Internal dictionaries are represented in the code. With #PHP 8.1+ it's pretty straightforward - you can use enums 🙂 When you need to represent some state, string-backed enum is the best choice, because it's much better for debugging purposes.
Basically, it's a simple object representation of some kind of reusable data structure. Unlike entity, it does not have identity, so value objects are considered as equal if underlying data is equal.
Value Objects encapsulate data and ensure its validity - constructor can, and should, check if provided primitive values are correct. Thanks to that, when you pass VO to other methods, you can be sure it carries proper information 🙂
Value Object's reusability doesn't mean you reuse data it carries. You reuse its structure across other Value Objects or Entities. Name can be used for modelling Employee, Client and other.
So you can have several John Doe employees with the same Name, but different Id 🙂
There were many discussions if #Laravel's facades implement GoF's Facade Pattern, but I think it does not matter at this point - the team won't change naming convention anyway. Naming is not a problem, I see other issues with facades - a #PHP thread 🧵
1) They're basically magic 🪄 Some may see it as advantage, but I consider it as drawback. You don't execute exact code you're calling, but your call is proxied to some underlying service. It strictly couples your code with the framework, which handles it.
Facades' API has to be added as comments in PHPDoc (with `@Method`) which is error prone because it's easy to forget to update facade's phpdoc when underlying service (accessor) is changed. But even if autocompletion in IDE works, you just can't simply navigate to method's code.