Hacker Newsnew | past | comments | ask | show | jobs | submit | AndyDavis3416's commentslogin

I hope I am not sticking my head into the lion's mouth here, but here (https://gist.github.com/BiggerNoise/9334673) is a link to a gist of some code in one of our apps.

Cases have Tasks and Activities. Activities are always associated with a case, and may be associated with a task.

In the controller, when assigning a case, one would type: AssignCaseCommand.new(params).execute!

The command class implements the Command pattern (the pretty well established name for interactors) and is a Composite as well.

BTW - the same command that assigns the tasks of a case is also used to merely assign a task. And, if we're just recording an activity, we would just use the create activity command.

I think it would be insanity to try and capture the rules associated with re-assigning tasks in the controller. (I am pretty sure that you would agree.)

--Minor Edit for clarity--


I asked for specific code, you gave me specific code, so here's how I would have structured that without the (imo, needless) command pattern: https://gist.github.com/dhh/9348053

It includes a lot of more context than your example, so I was guessing liberally at the missing pieces. But hopefully this is still easily digestible.


I think it's great that you stick your head into the lion's mouth, even if he chewed it clean off it's a great contribution :)

I think the main problem with your command pattern approach is that you didn't implement the full command pattern. At least, I assume you didn't, you didn't actually show the controller. The command pattern has one extra class, the invoker. The invoker takes commands, and invokes them. A great example of this is the worker queue, it takes generic worker objects that have an 'execute' method.

It is never the class that creates the worker objects, that executes them. Yet that seems to be the way you're going to implement the controllers that use these commands.

What I think you've built instead is an adapter pattern. You have abstracted a class of problems in a way that they have a common interface, so that in your controllers you have a uniform way of invoking them.

This is where I think dhh sees the muddiness in your code. Why are you abstracting your interaction from the controller, which is supposed to be the exact thing that should be controlling the interaction.

The only reason for that would be that the same interaction is happening in different controllers in exactly the same way. And that's something that dhh claims does not happen in real applications, or at least he demands an example.


Honestly, I was disappointed in his response. With all his harrumphing about real examples, I would have expected him to make an effort to understand a real example. But, as I said elsewhere, the downside of real examples is that they require quite a bit more effort to grasp than contrived ones.

To address your comments:

While it is true that one of the great features (and frequent motivations) for using the Command pattern is the separation of the creation and invocation, it is not the only motivation. The two big payoffs that we are getting are:

- Separation of invocation from the knowledge of how it is carried out

- Composition of complex commands from smaller, simpler commands

There's no requirement that the invoker is never the same as the creator. It's just that most often it isn't, particularly (as you noted) in a worker queue system. That's really just a feature of the implementation that the command readily enables rather than a fundamental property of the pattern. For us, sometimes it is, sometimes it isn't; that's not the primary payoff that we're going for.

I can't support the characterization of this as an Adapter. This is fundamentally behavioral. I have a class that represents an action that I want to have happen. All of the places that want this action do not need to know what is involved, they only need to create the command and hand it off/invoke it.

FWIW - I have five different places in my software that create an AssignCaseCommand. - One controller

- Three commands that can create this as a sub command

- A utility job

Plus that simple concept is available to any custom implementation code that we write.


The Command pattern always reminds me of free monads. By building a new free monad you can encode the structure and logic of a computation statically and then evaluate it repeatedly. One of the things that I think is missing with the command pattern is that it's probably not as easy to decide to, for instance, execute your commands in a pure environment.

This is, of course, a primary advantage of having a static representation of the domain-level demands of your code—you can choose many ways and many environments to execute it within. It's a function I use in real code all the time.


If reassigning a case is part of the domain, it belongs in the model.

I don't know how to put this gently, but this concoction looks like a hot mess with no separation between modeling and controlling.

These commands have a ton of domain logic all tangled up with activity reporting, formatting, and parameter parsing. It looks like a big ball of mud to me :(


> It looks like a big ball of mud to me :(

That is of course the most popular of all architectural patterns. :)


Most popular by what metric?


Practiced.


Source?

Edit: Curious why the downvote, I ask a legitimate question to the parent to provide a source that the command pattern is the most utilized architecture in the industry so please don't downvote without an explanation.


Not the command pattern. The "Big Ball of Mud" architecture.


Well, that's what happens I guess when you spend all day working on one problem and stop to check HN and randomly comment...guess I was too tired to realize what the parent comment actually said!


Perhaps that's why the OP used a simple example. Complex examples are pretty hard to get your head around at first glance; even sane systems can be perceived as a 'hot mess' upon a quick read without any context.

I disagree that re-assigning a case belongs in the case model. It is a complex interaction between several models. Extracting it to a command object allows all of the rules to be easily understood.


I feel like showing off some tests would help when the example is this complex. That would give a bit more context in terms of what you're trying to accomplish here.


True, the tests do demonstrate what can be accomplished, but that kind of gets back to my point: Any example complex enough to unambiguously warrant the use of a command pattern is going to involve non-trivial effort to learn the context. Asking that effort of someone reading a blog post is a bit much.

Unfortunately, it's easy to dismiss simple examples as not being worth the complexity and demand real examples which can be dismissed because they cannot be easily understood.


It's a model, all right—but it may not be part of the “Case” model. It might be part of a CaseAssignment model that is (for lack of a better word) a virtual model. Or it's part of the controller that does the changes. It mostly depends on how your code gets used and where it gets used from.

When I did something similar[1], I implemented it as a “virtual” model and applied validations to make sure that the PlanChange was properly constructed, and then performed the change in a transaction. I was able to test the hell out of it both with and without actual database interaction. But I was able to use it in three different locations and removed lots of special case code that had built up over two years of development.

You probably don't need Commands. You probably need something to orchestrate manipulating multiple ActiveRecord objects simultaneously in a transaction. It's still a model, but it may not be the model that you thought it was.

[1] Account from Plan [a] to Plan [b] that has a different quota, affecting Device assignments, possibly including moving some devices to Plan [b], some devices to Plan [z] (a default plan), and disabling some devices entirely because there's no quota available anymore.


Although I would never call that a model, I think that we're doing similar things for similar reasons.

Command, Virtual Model, Verb Model, Interaction. If I were working on your team I am sure we could agree that one of those would do for a name.


Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: