Design patterns in the test of timeCommand, Redux
In my previous post about the command pattern, I gushed about how much I loved it. That doesn’t mean that the command pattern as originally envisioned is still completely in fashion.
In particular, the notion of “Undo” was one of the major features in the command pattern’s cap. Today, that is almost never the case. Sure, if you are building an application such as an editor. Something like Photoshop or a text editor would find the notion of commands with the ability to have an undo stack very compelling. Other than that, however, that is a very rare need.
In most business scenarios, there really is no good way to undo things. How would you implement SendEmailCommand.Undo(), for example? But even something like PlaceOrder.Undo() is a lot more complex and hard to do than you would think. The mere notion of undoing the operation assumes that this isn’t going to have any side affects. But cancelling an order may result in cancellation fees, require you to ship back things you got back, end. It is not “Undoing PlaceOrder”, rather that is a whole different and distinct business process, usually represented by another command: CancelOrder.
Another common issue that people have is the degeneration of the entire architecture to something like:
CommandExecuter.Execute(Command cmd);
To that I answer, more power to you! I love code that is composed of a lot of small classes all doing things about the same way. There is no easier way to look at a system, and that allows you to quite easily add additional functionality to the system easily. That said, mind how you handle routing in that scenario. I have seen people go into the “when a request comes to this URL, let us invoke the following commands” in XML. One of the reasons that people dislike this approach is how you actually call this. If just getting to the command executer is hard and involved, you lose a lot of the advantages.
This popped up in the mailing list, and I really dislike it. The notion of Composite Command. A command that can execute multiple commands. Now, from a programming point of view, I can easily see why you would want to do that. The PlaceOrderCommand operation is composed of a lot of smaller commands. But, and this is important, the notion of Composite Commands basically mean that you get the same thing as the PlaceOrderCommand, but you just lost the name. And naming is important. Almost as important, error handling is quite different between different business scenarios, and you sometimes end up with something like:
1: var placeOrderCommand = new CompositeCommand(2: new RegisterOrderCommand(),3: new ReserveStockCommand(),4: new ChargeCardCommand(),5: new ShipOrderCommand()6: )
7: {
8: Sequential = true,9: StopOnError = true10: }
And this is simple, how do you handle an error in the ShipOrderCommand after you already charged the card, for example?
More posts in "Design patterns in the test of time" series:
- (21 Jan 2013) Mediator
- (18 Jan 2013) Iterator
- (17 Jan 2013) Interpreter
- (21 Nov 2012) Command, Redux
- (19 Nov 2012) Command
- (16 Nov 2012) Chain of responsibility
- (15 Nov 2012) Proxy
- (14 Nov 2012) Flyweight
- (09 Nov 2012) Façade
- (07 Nov 2012) Decorator
- (05 Nov 2012) Composite
- (02 Nov 2012) Bridge
- (01 Nov 2012) Adapter
- (31 Oct 2012) Singleton
- (29 Oct 2012) Prototype
- (26 Oct 2012) Factory Method
- (25 Oct 2012) Builder
- (24 Oct 2012) A modern alternative to Abstract Factory–filtered dependencies
- (23 Oct 2012) Abstract Factory
Comments
Interesting. Recently I was starting to doubt usefulness of DDD pattern in common websites building - namely I don't like many architectural overkill that DDD notion force you to implement when you only need your system to display data and perform tasks like register user. And I was wondering - how about building application "core" on SOA and CQRS only principles: the application which will export only bunch of query and command classes? And both commands and queries will operate/return only simple data (like id / strings / POCO data objects / ect.). What you think?
Aynede - again I mostly agree with what you are saying, but when your entire architecture degenerates into "CommandExecuter.Execute(Command cmd);" it is indeed more power to you as the architect, but also much more power to the developer to abuse. When using this design, It is much harder to enforce your architectural constraints (e.g. what component can call which commands) and it is much harder to use static analysis tools which can help in understanding a system's composition and structure and enforce the required constraints (especially in a large and complex system). This is because much of the executed commands are likely to be determined at run-time rather than in compile-time (which is both an advantage and a disadvantage). You might be surprised at how some developers can really wreck havoc when using this kind of design. Sometimes this design is REALLY what you need, but IMHO it is more often not the case.
Composite commands are IMHO not a great idea. What one should do instead is combine event/observer with commands. So a command is executed (doing a single task, like adding something to a list) and that raises an event, or triggers in another way an observer. This observer then acts upon that event by executing more commands.
This way, you can create a very fine-grained, command driven system, which is completely decoupled and avoids having the need for composite commands: you can add a task to what should be done at any time, simply add an observer to some event.
Our designer is build this way, from top to bottom and it has proven to be very flexible and straight forward. Added to this is being able to undo everything: when command X executes and its action raises an event and command Y is then ran by the event handler, command Y is then placed in the 'command queue' of X by the command handler. This way, when you undo 'X', you simply first undo everything that's in the queue of X (which is Y), then you undo X. No knowledge of what follows X is necessary, you simply let the flow of the program through the events decide what follows what and what can be undone by what.
Michael, It would be irresponsible of me to give you architectural advice without context. I would say that the DDD book does point out the notion of separate contexts, the DDD applies at the heart of the business, not at the boundaries, there you use the simplest thing you can get away with.
Eran, Note the comment about routing, I strongly discourage runtime command routing, exactly because of those reasons. Doing static routing, my just calling Execute(new ThatCommand()), is much easier to work with.
One way to undo a send mail command is not to send it right away. I think Gmail has this feature.
Your composite command example IMHO is not a good use case but does demonstrate the problem with the pattern. This looks like a classic DDD example where each command would be sent to a separate bounded context so you would be sending a single command to a single BC and then depending on the resulting event would trigger another BC subscribing to that event would continue on its way.
Where I think composite commands make sense are within specific boundaries like a BC or transaction. They also great for just breaking something up into simple parts to make your code more expressive. This can be also be done with functional style programming or just adding additional sub routines within a command class but this can sometimes add unneeded verbosity to the code making more difficult to read.
Great post and I Hope you and your family are well with that has been going on in your part of the world.
Continued Success.
Jason Wyglendowski
@Frans, I don't think an event/observer pattern would be a good replacement for the CompositeCommand. The problem is that when you have command like RegisterOrder, ReserveStock or ChargeCredit card, you want to execute them withing a single transaction. This is hard if not impossible when using event/observer. In addition, you will lose a single point where you can look and understand the execution flow. F12 is important for understanding code.
@Daniel So what happens if the ChargeCreditCard fails (processor is offline temporarily)? Do we roll back RegisterOrder? That doesn't seem like a good transaction boundary.
"Find Usages" of Event works decently here, but yes, it is harder to immediately grok the code base, just like moving from procedural code to OO. Once someone gets the architecture though, any bit of it is dead simple.
@Abdu That works with rolling back transactions, but not with undo. Once the mail is sent you've broken your ability to undo, and you have to send that e-mail eventually :)
@james it's pseudo undo. At least u have a grace period if u ever change ur mind before the email is sent.
Probably a TransactionalCommanExecutor would work. But In this case Shipping Command should be aware of how to cancel card charge. I am seeing a graph of such command compositions would solve this issue. But it is too broad to cover in Command pattern. I would rather favor chain of responsibility instead of command pattern.
What if you treated commands like migrations?
You would have an Exeucte and an Unexecute.
Execute = Charge Credit Card UnExecute = Refund Credit Card
When you hit an error you just go backwards in your command stack and call Unexecute. This would refund a credit card, put inventory back in stock, unreserve stock, and put the order into a canceled state.
How does that look? Bad?
Khalid, That assumes only one path, and a simple one. What happen if you already prepared the order to shipping when it was cancelled, and you need re-stocking fee?
That's a good point, but I guess I would ask "Why does it have to be complex"?
Khalid, That is the business.
Processing the order is not a synchronous operation.
Place new order command will generate an event new order placed, that will start a long running process(Saga) that is responsible for tacking of compensation actions in case that one of the processing step fails (RegisterOrderCommand, ReserveStockCommand, ChargeCardCommand, ShipOrderCommand)
I have to agree on the usefulness of the command pattern and the power in making explicit closed components that can be recombined.
Instead of the CompositeCommand I just raised the breadth of the abstraction and said a command is a small unit of work or business value and an Interaction is a larger unit of work more closely tied to a use case. I wrote some code with the goal of blending your views on limiting abstractions with the more conceptual parts of DDD. Interactions become the kinetic energy of the system orchestrating the different potential components (commands, queries, tasks, etc)
You can see the example test of what that looks like here https://github.com/objectatrest/BuildingSupplies/blob/master/src/Structural/Structural.UnitTest/When_fulfilling_use_cases.cs
and the Interaction that wraps a bunch of commands/queries here
https://github.com/objectatrest/BuildingSupplies/blob/master/src/Structural/Structural.UnitTest/OrderExample/Interactors/PlaceOrder.cs
That is a simple use case just to get the concepts lined up but having used this in a complex production system really made the coding effort fly.
CompositeCommand would be the implementation, not the API. As Oren states, names matter. Personally I like an interface (or a delagate) as the API so I can play these types of implementation "shell games" behind the scenes.
I think of commands more in terms of messaging than a class with an Execute method (which could be what some commenters mean). In this case, something like CommandExecutor.Execute(Command cmd) would actually translate to MessageBus.Send(Message cmd).
I really like the latter architecturally (along with a Publish method for events), as this means I can develop components which are loosely coupled, and then intentionally create behavioral coupling by sending specific messages. Sagas are the usual answer to transactions, but not the only answer (and not an easy answer). Security/Authorization is not difficult if you receive commands on a tier boundary where authentication happens anyway (e.g. an MVC action). Simply check if the authenticated user / source address is authorized to issue the incoming command. Throw if not. That's not to say that all of these things combined are trivial to implement and right for every application. But they certainly can be useful.
Comment preview