-
-
Save gmoeck/3088352 to your computer and use it in GitHub Desktop.
class CommentsController < ApplicationController | |
def create | |
translator.translate params | |
end | |
private | |
def translator | |
CommentRequestTranslator.new(processor) | |
end | |
def processor | |
comment_processor = CommentProcessor.new | |
comment_processor.add_listener twitter_poster | |
comment_processor.add_listener facebook_poster | |
comment_processor.add_listener comment_persister | |
comment_processor | |
end | |
def twitter_poster | |
TwitterPoster.new | |
end | |
def facebook_poster | |
FacebookPoster.new | |
end | |
def comment_persister | |
SQLCommentPersister.new | |
end | |
end | |
#example class | |
class CommentProcessor | |
def initialize | |
@announcer = Announcer.new | |
end | |
def add_listener(listener) | |
@announcer.add_listener listener | |
end | |
def new_comment(comment) | |
new_comment = comment.with_language(language_for(comment.body)) | |
.with_spam(is_spam(comment.body)) | |
@announcer.announce('new_comment', new_comment) | |
end | |
end |
@gmoeck I can definitely see that, the actual domain definitely changes things - but in the case where persistence is async, where would you put validation, and make sure posts to fb/twitter only happen with valid comments? Would you just let them all call .valid? on the comment, or would you place that logic elsewhere and only announce new_comment when valid?
@therabidbanana I would put a service that was responsible for comment validation in front of the processor. The processor would never receive a comment that wasn't valid, and it wouldn't need to worry about that, since validation seems to be a separate responsibility to me.
@gmoeck That gets to my second question: where would you put those services that should live in front of the processor and keep it from firing? Leave them in the controller, or is there another object living somewhere in there that runs validation/authorization services before calling the processor?
@therabidbanana I would do that by composition. I would make the validator implement the processor contract, and have it proxy the request to the processor when it is valid, and do something else when it isn't.
Right now this is the controller calling the processor. After the change, proposed above by Gregory, controller would call the validator which would call the processor, so at the end, the controller would:
- create a processor (as it is now)
- create a validator, passing in the processor (so validator would delegate when everything is OK)
- pass a comment to validator.
But that would introduce a processing logic into controller (or would it not?). What if there are two more controllers using a processor right now? They would still be passing a comment directly to processor instead of validator... :-/
Is that not a problem that the controller is actually creating the processor instance? Making the validator implementing a processor contract won't help here. Shouldn't controllers ask someone (a comment processor factory?) for an instance of processor, so your solution with adding a validator into the processing chain would "kick in" everywhere?
I would agree that it can be somewhat of an issue to have the controller creating the instances of the objects. I'd generally move the creation of the objects into an infrastructure layer and then just have the controllers call into the domain. I would often however still create a "translator" or "adapter" within the controller, and pass the domain into its constructor when translation from the network request into several messages to the domain is necessary.
@therabidbanana I think that depends on the actual domain. In my mind all of the validation of the values and such should already have taken place, and the persistance layer should only be responsible for mapping the comment into the database. If that's the case, for something like comments which are strictly additive I see no reason why the actual persistance should block the rerendering of the page. The actual persistance itself can be totally async, as should the "persisting" to Facebook and Twitter. If we were worried about someone clicking the Twitter/Facebook link and not seeing the comment, we could always have the persister announce like your saying, and have FB/Twitter listen to that.