Controllers, the right way!
What you’re probably doing wrong in your first MVC steps.
Controllers, the right way!
What you’re probably doing wrong in your first MVC steps
Ok, so you’ve joined the train of good code architecture and you’re writing your applications following the well known Model-View-Controller design pattern. That’s great!
As you probably know, many popular frameworks in different languages provide a very good starting point to implement MVC in your project. You’ve picked one and it gave you a great boilerplate to code your MVC software. You’ve followed its basic steps and framework conventions and everything is working.
A problem arises with this, the definition and examples many framework docs and tutorials give, tend to be ‘wrong’ or misleading.
A controller can send commands to the model to update the model’s state (e.g., editing a document). It can also send commands to its associated view to change the view’s presentation of the model (e.g., by scrolling through a document).
Wikipedia
Following this definition, let’s pseudo code a user controller update method, this article is language agnostic.
# UserControllerfunction update(request)
# 1. get the input
# 2. set rules to the input
# 3. if the user also want to change the pw, set rules for it
# 4. create the validator and test the input
# 6. if input is valid, update model. email if pw changed
# 7. return response
input = request.post
rules['email'] = 'required|email'
if input['password']
rules['password'] = 'min:6'
endif
validator = new Validator(rules, input)
if validator.fails()
return Response(RenderView.errors())
else
UserModel.update(input) # use some model to update
if input['password']
email = new Email()
email.to = 'the@superstar.com'
email.body = 'password changed'
email.send()
endif
return Response(RenderView.success())
endifendfunction
For some of you, this code isn’t wrong, and actually, following the definition that we got from Wikipedia, it isn’t. We have a controller that tells some model to do an update and tells the corresponding view to render accordingly. So what?
I’ve done this myself millions of times, but for those of you who are making this, let’s do the breakthrough!
“Controllers are like transporters, they should get a request, ask the core application code to do the logic, and return a response. They shouldn’t have domain logic. Code them thinking in the end result.”
As we can see, just for the simple task of updating a user, we have about 20 lines of code. We have some logic going on in the method and we’ve followed a procedural way of coding the control.
Update some user info its what we want to accomplish, to ASK is all the controller method should need to care about.
Let’s now code with intent!
# UserController the right way function update(request)
# 1. get the input
# 2. tries to update the info using some core application
domain object and return success response
# 3. if it fails return response with errors
input = request.post
try
UserService.update(input)
return Response(RenderView.success)
catch ValidationException e
return Response(e.errors())endfunction
See, we now have a very clean controller method with 6 lines, and we’ve coded our controller with intent.
But what about all that logic we had earlier ? This is the good part and what made me code better. After I stopped making procedural controllers I felt I had total control. Since my application logic was out of the controller I could then start creating my own application logic following good OOP principles and the design patterns I wished to follow.
So let’s give it a shot, let’s create our application core.
# UserServiceClass UserService() function create()
# some code
endfunction function delete()
# some code
endfunction function update(input)
rules['email'] = 'required|email' if input['password']
rules['password'] = 'min:6'
endif
validator = new Validator(rules, input) if validator.fails()
Throw ValidationException(validator.errors)
else
UserModel.update(input)
return
endif
endfunctionendclass
And here’s our core logic.
And what about the emails? Well, once again, that’s not the UserService responsibility to send emails, it should be some MailerService. There are several approaches you can take. One I like to follow for example, in case your development stack provides it, is to trigger some event in the cases you need to, and the listener will take care of calling the service to send the email.
Hope this was useful!