Skip to content

Instantly share code, notes, and snippets.

@davidhemphill
Last active January 28, 2018 17:36
Show Gist options
  • Save davidhemphill/3254b3954be5f01bba35 to your computer and use it in GitHub Desktop.
Save davidhemphill/3254b3954be5f01bba35 to your computer and use it in GitHub Desktop.
Command Buses and Simple Redirects

I've been busy developing a large application on Laravel. In the middle of this, I've been trying to also be a good PHP developer (oxymoron?) and look into using the Command Bus pattern, specifically using Laravel's implementation. I think I get the idea in theory: Fire a Command, the Handler deals with it, the Command is immutable, and all that. I've looked up tons of tutorials on the subject, and even watched Ross Tuck's talk Models and Service Layers; Hemoglobin and Hobgoblins which mentions the subject.

The one thing these tutorials do not mention is how one might use the data generated in the Handler. For example, say I have a controller method that fires off a command:

// Register the User
public function register(RegisterRequest $request)
{
  $this->dispatch(new CreateRegistrationCommand($request));
}

Now, let's assume this method needs to charge a credit card with Stripe, created the Order in our database, generate some tickets, email the user, notify the admins, and log the whole thing. After the Command is successful, I want to redirect the user to a confirmation page (using a reference number or ID from the Order we created). So our usage of the Command Bus gets a little more hard to figure out.

// Register the User
public function register(RegisterRequest $request)
{
  $this->dispatch(new CreateRegistrationCommand($request));
  
  $this->dispatch(new CreateOrderCommand($charge));
  
  // Redirect the user here
}

If I want to use the data from the newly-created charge, and use it to create the Order in our system, how would I do that? Using the Command Pattern means a Handler is not allowed to return anything. It just goes off to do its thing and we can handle any Exception that's thrown, but that's it. Redirecting from there becomes tricky. Here are some of the suggestions I've seen.

1. Return the Data Needed from the Handler Class

Handler classes aren't supposed to return anything and you're not supposed to rely on any of the data they generate. In theory, this is because a Command could take several minutes, hours, or even days to complete. Some people say, "Screw this!" and do it anyways. I see this as creating a lot of extra unneeded Command and Handler classes when I could create a simple RegistrationService class that handles all of this and is not bound to the rules of a Handler.

2. Create a UUID

Using my example, one could reason that my reference_number could be used as a makeshift UUID and be generated by the Controller for use in the creation of the Order. A Controller, in my mind should not need to worry about doing such things. At that point the Controller will know too much about the application and be veering away from its primary duty of handling HTTP requests. But, assuming I'm okay with making that concession, this could be a viable way to handle it, but you lose the purity of the pattern, and by that point it's no better than using a Service class for the same goal.

3. Redirect to a Verifier Method and Redirect After That

This one is the most complex and makes me go, "Dafuq?". This approach suggests something like this:

// Register the User
public function register(RegisterRequest $request)
{
  // Create UUID
  $uuid = uniqid();

  // Dispatch the Commands to the Queue
  $this->dispatch(new CreateRegistrationCommand($request, $uuid));

  $this->dispatch(new CreateOrderCommand($charge, $uuid));
  
  // Redirect the user to the verify method
  return redirect()->route('register.verify', $uuid);
}

// Show the verification page
public function verify($uuid)
{
  // Have your view listen via AJAX call and redirect when the order is ready
  return view('register.verify');
}

// The AJAX verification route
public function verifyOrder($uuid)
{
  // Verify the Charge and Order
  
  // Return the Order when all is well
  return $order;
}

Look at all this set up so we can use the "pure" approach of the Command Bus! This is total madness. Essentially, we fire the Commands off to the queue, redirect the user to a holding page that checks via an AJAX route whether the order with a UUID you generate is ready. This is no more simple than using a Service class that shoots these actions off to the queue and does the same actions.

Disclaimer

I'm don't profess to be the best developer in the world. I'm very pragmatic and don't buy into a lot of the overly complex patterns and I don't tend to prematurely optimize. But I'm just not seeing the benefit in the Command Bus pattern for use cases like these. What am I missing?

@fideloper
Copy link

In general, no need for complexity if you don't need the behaviors they enable!. Use commands or don't, it's all good! Here's just an example I find useful that you may not may not need or want:

Re-use

I re-use the same commands for CLI, API and regular web calls. Same code, difference context, so the only difference becomes (essentially) how you gather your user input and how you respond to validation issues/errors.

Logging / Extra functionality centralized:

My favorite use has been logging actions so users have a history of what they've done. I wrap the Dispatcher with another class (Decorator Pattern) which logs the action. Then users have a nice audit log for themselves (and I do for myself) - stuff like this:

  • User registered
  • User subscribed to paid plan
  • User changed email

Only commands with also implement "Loggable" in my application are logged, so I can selectively decide which commands are logged.

To generalize that: Everything going through a central bus makes it a useful place to add extra functionality around some or all commands.

Exceptions

I use exceptions to catch validation errors and any other errors while the command is running, which is how i get information back to respond to a web request client.

Otherwise I have handlers return data, which are then returned through teh dispatcher, so the code calling the command can then use it:

$data = $this->dispatch(new CreateOrderCommand($charge, $uuid));

return Redirect::to('/account/'.$data['uuid']);

Note sure if any of this helps clarify, just my 2 cents on how I use it and why I like it.

@davidhemphill
Copy link
Author

I think your example use-case is a great example. I use my service classes in a similar way, to enable reuse across request types. And your method of returning data is how I would handle it, and is an example of Suggestion #1. But from what I've read, it seems like people poo-poo on it as though it were an anti-pattern.

Here's an example where I've seen it said that a Command shouldn't return anything: but then we have a Suggestion #2 situation. What do you think?

By the way, your Hexagonal Architecture talk at Laracon US last year got this whole thing started for me.

@fideloper
Copy link

Awesome, thanks (I hope :P )

So it turns out so far for me that 99% of the time I don't return anything from command handlers (It's also my preference, I guess...). I still leave the option open though, because you gotta make shit work at the end of the day :D

Pragmatism rules!

@JesseObrien
Copy link

If you check out this discussion, Ross Tuck, Shawn McCool and I have a good discussion on what avenues you can take into using the Command pattern.

First, I wouldn't worry too much about the "purist" CQRS view of the command pattern unless it's something you really want to internally strive for. Take what fits for you (and your team) and work it in. Don't go overboard for the sake of pleasing outside parties, they don't have to maintain your code, you do (that is, unless you hire them). In the same vein, don't ignore advice. Take some from everyone, with some grains of salt and then make decisions on what you should be doing. Also, experiment like crazy. Write tons of throwaway code, you'll get much closer to what you like that way.

Second, on what you might be missing, I can give you my view of it. I use commands in my projects for a lot of the reason's @fideloper pointed out. The bus is centralized, easy to decorate, and straight forward. The commands are easier to name (most of the time), nicely encapsulated units to test and give a nice overview of the app without having to even open a file. We can type tree in the project directory and get a good sense of what the app does.

Lastly, it's not the patterns that make a Good Developer™. It's doing things like this, talking to other people and getting opinions. You begin and end with a disclaimer on not being the best, but you don't need to. It's evident by the post that you're striving for self betterment, so don't sweat it.

@davidhemphill
Copy link
Author

Appreciate the responses! I can get where you guys are coming from. It's hard to implement a purist approach across any codebase as each is different. What's also difficult is when trying out these concepts on small, disposable projects, I've found it hard to find resources that show examples of the "ideal" usage or even real-world use-cases. It's nice to have a good community though.

@fideloper: Pragmatism and #cashmoney

@JesseObrien: Great discussion. Not sure about changing my database to have a UUID so I can implement a Bus. That seems weird?

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