Match All the Verbs
Phoenix is not your App. Is it even MVC?
It turns out that Elixir's popular MVC web framework doesn't need Models, Views, or even Controllers.
I've never been an adherent of the MVC pattern in web applications, even though it has been a productive concept for full-featured server-side software. I might use Webmachine or an equivalent program in Erlang, Ruby, or Clojure; or I might not require an HTML template engine or a database wrapper. I often prefer building up a web app piece by piece, instead of committing up-front to an opinionated framework.
Although I can generate a Phoenix project without Models, or else encapsulate them in a separate OTP application from my web app; and although I can avoid Views by sending responses directly using Plug; I didn't think I could escape from Phoenix's Controllers (without completely replacing the Router). Soon I would find a way.
Spelling W-E-B with M-V-C
Model–View–Controller (MVC) is a software design pattern that developed out of early (desktop) graphical user interfaces but was later adapted to describe web application frameworks, such as Ruby on Rails and, more recently, Phoenix.
For server-side web software, MVC denotes the components that manage data (Models), media representations (Views), and HTTP request/response behavior (Controllers). Nevertheless, the MVC pattern does not name its central component, the Router, which dispatches each request to the code that will handle the response.
Take a basic request line such as:
GET /some HTTP/1.1
In Phoenix, a
GET request for
/some resource is matched using a router
get "/some", SomeController, :index
get macro call is equivalent to:
match :get, "/some", SomeController, :index
Here, the arguments comprise an HTTP verb, a URL path, a Controller, and an action function. These four terms together determine a "route" in Phoenix. But do they have to?
The Controller Pattern
match :get, "/plug", SomePlug, 
So, if a Phoenix route does not require a Controller, does that mean we can drop the last letter of MVC?
Because the role of the Router is often elided when we talk about Web MVC, it's
easy to miss its significance.
The Controller part of MVC is not just a
Controller module that includes one
or more action functions.
Rather, it is a particular contract, enforced by the Router, for matching a
request with the code that will handle the response.
We have already seen this contract in the API for Phoenix's
# Generates a route match based on an arbitrary HTTP method match(verb, path, plug, plug_opts, options \\ )
I argue that the real Controller pattern in Web MVC matches a verb and path, and executes a module/function in the context of a request.
For most CRUD apps, the Controller pattern suffices. But it does not work for resource designs such as:
- An echo server that replies to all requests for a path (for any HTTP method)
- A REST-based framework where the HTTP methods supported by each resource are encoded in the connected Plug, and not the Router; e.g., PlugRest
Breaking the Pattern
I brought up method-agnostic Phoenix routes again in a closed issue on the GitHub project page. After some discussion, Chris McCord offered that Phoenix already supports this behavior by way of a catch-all verb:
match :*, "/any", SomePlug, 
The feature exists for use with the
forward macro, but had not been publicly
endorsed for use with
Fortunately, it seems this feature may be documented in the next release of
This finally allows us to drop the Controllers from our "MVC" web app.
Doing It Wrong?
I was curious about the initial reluctance to fully support a catch-all
macro in Phoenix Router, and how it could be "abused" or "introduce bad
I have a hunch it stems from a decision in Rails to deprecate "match"
without an explicit method (resolved in this commit).
Rails warns about the danger of unintentionally putting "state-changing" code
that manipulates resources on
The documentation advises that "You should not use the match
method in your router without specifying an HTTP method."
Egor Homakov is even more blunt, writing:
You always know which HTTP Verb is used for specific "controller#action"! If you don't - you are doing it wrong.
This assumes, of course, that we are using the Controller pattern. This may be a fair assumption for Rails, but it shouldn't be for Phoenix.
It's still possible to match all HTTP methods in a Rails route using an explicit
via: :all option.
Fortunately, Phoenix offers us the same escape hatch.
Game, Set, Match
Phoenix's flexibility and openness to multiple, co-existing patterns for structuring web applications is just one more thing to recommend the framework.
Phoenix is only MVC if you want it to be.