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.
When working in Elixir, Plug suffices for the piecemeal approach.
Nevertheless, it is tempting to use Phoenix Framework, which adds
many features without sacrificing flexibility or performance.
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
Nevertheless, the MVC pattern does not name its central component, the
Router, which dispatches each request to the code that will handle the
Take a basic request line such as:
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
These four terms together determine a “route” in Phoenix.
But do they have to?
The Controller Pattern
match macros in Phoenix Router are not limited to using a Phoenix
Instead, the routes will accept any module that follows the Plug
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.