Medium-Scale Dancer, Part 1: Modules
Or: How to Get Beyond Small-Scale in Dancer Application Design
When you generate a Dancer app with dancer2 -a App
, you get a very
nicely structured and useful starting point. As long as your app remains
fairly small, you can work with this structure as-is clear through to
completion. In that sense, the generated app is production-ready.
The problem comes when you get to thousands of lines of Perl code, dozens of templates, and dozens of routes, stretching the stock app design beyond its natural limits. This series of articles gives you one way to extend beyond this base.
I say "one way" because Dancer is a policy-free web application framework. Its designers purposely avoid making whole classes of design decisions for you. That means that Dancer not only doesn’t force you to design your app the "right way," by its creators' lights, the framework rarely even gives hints about what the right way might be. Dancer prefers to present itself as a box of tools, which you are free to use as you see fit.
The design presented here is working quite well for us, so we think it is worthy of emulation, but we do not expect that it will work for all medium-scale web applications. Think of it as a mutually consistent collection of design ideas, rather than a prescription of the One True Way to design a Dancer application.
The Generated Files
When you say dancer2 -a App
at the command line, you get a few dozen
files. Several of these files contain Perl code, but only one of those
is interesting for the purposes of this article.
We're setting bin/app.psgi
and public/dispatch.*
aside as
"uninteresting" here because we recommend that you leave these files
as-is. None of your app-specific Perl code should go in these files.
We're also ignoring Makefile.PL
since it's part of the app's build
system, not part of the app proper.
That only leaves lib/App.pm
. For small-scale apps, simply piling all
of your Dancer-related app code into this file is just fine. Up to about
one or two thousand lines of code, you can just use it as-is, for the
same reason that a thousand-line monolithic Perl script is fine in other
contexts, too.
Extending the Stock Scheme
One of the nicest things about Dancer is that the generated startup code
adds the app's lib
directory to the Perl module path. That leads to
an obvious solution to the problem of too much code in a single file:
break it out into other *.pm
files in the lib
directory. But how,
exactly?
I recommend following the example of CPAN, with a structure like the following:
lib/App.pm # your app's main module lib/App/MajorFeature.pm # implementation of a major app feature lib/App/OtherFeature.pm # as many feature impl files as you need lib/App/API.pm # if your app has a REST API, put it here lib/App/Const.pm # maybe you have some app-wide constants lib/App/Utility.pm # every big app has a "junk drawer" module
Then in lib/App.pm
, you use
all of these modules:
use App::MajorFeature; use App::OtherFeature; use App::API; use App::Const; use App::Utility;
How you design these modules is up to you. The standard rules for well-structured Perl application design apply.
There is one Dancer2-specific thing to beware of here, though: you must
set the appname
property on the sub-modules. If you
don't, each sub-module will be considered a separate Dancer2
application, which puts unwanted barriers between the modules.
(Dancer1 didn't have this requirement because it didn't have any concept of multiple apps within a single Dancer instance.)
When it comes time to break lib/App.pm
up into multiple modules, I
recommend that you move almost all Perl code into one of these modules,
leaving only application initialization code in the top-level module.
(This, by the way, is why we don't need to modify bin/app.psgi
: since
it pretty much just loads Dancer and then calls lib/App.pm
, there is
no point in splitting app initialization code across the two files. Let
bin/app.psgi
remain a tiny little glue file that exists merely to
make PSGI happy.)
Your initial refactoring of lib/App.pm
will likely leave all the
route definitions behind, which is fine because refactoring the routes
is the subject of the
next article in this series.
Author
This article has been written by Warren Young for the Perl Dancer Advent Calendar 2016.
Copyright
© 2015, 2016 by Educational Technology Resources, licensed under Creative Commons Attribution-ShareAlike 4.0