Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 224 lines (207 sloc) 13.139 kB
915520e article
Zbigniew Lukasiak authored
1 <html>
2 <head>
3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
4 </head>
5 <body>
6 <h1>Why WebNano</h1>
7
8 There are some common parts of many web applications, things like a login page,
9 a comment box, an email confirmation mechanism, a generic
10 CRUD page - all of them are quite well defined and one can think that
11 it should not be too
12 difficult to abstract them away into libraries. And yet, every time you start a
13 web application you need to write that boring comment box again and
14 again. Why there are so few of such libraries? Why writing them is so hard?
15 There are more and less important reasons - but there are many of them and I
16 guess that eventually most of such application components projects die the
17 death of a thousand cuts. Some of those problems can be solved and I believe
18 that solving them would make a difference. WebNano is my attempt at doing that.
19 <p>
7aed9ac article
Zbigniew Lukasiak authored
20 Probably the most popular Perl web framework is Catalyst now, it is also the
21 web framework I know the best - this is why I choose it as the point of reference
22 for the analysis below.
915520e article
Zbigniew Lukasiak authored
23
24 <h2>Controllers in request scope</h2>
25
26 Five years ago I was staring at the first Catalyst examples and I had this
27 foggy intuition that there is something not quite optimal there. The essence
28 of object oriented programming is that the most accessed data is made available
29 to all methods in a class without
30 explicitly passing them via parameters. But the examples always started with
31 <code>my ( $self, $c, ... ) = @_</code>, not very DRY. The <code>$c</code>
32 parameter was dragged everywhere as if it was plain old procedural programming.
33 <p>
34 At some point I <a
35 href="http://perlalchemy.blogspot.com/2009/10/catalystcomponentinstancepercontext.html">
36 counted how often methods from the manual use the controller object versus
37 how often they use the context object which contains the request data</a> -
38 the result was 117 and 38 respectively. Many people commented that their code
39 often uses the controller object. It's hard to comment on this until the code
40 is made public, my own experience was very close to that show by the manual,
41 but this disproportion is only an illustration. The question is not
42 about switching $c with $self. The question is why the
43 request data, which undeniably is in the centre of the computation carried on
44 in controllers, has to be passed around via parameters just like in plain old
45 procedural code instead of being made object data freely available to all
46 methods?
47 <p>
48 My curiosity about this was never answered in any informative way until about
49 year ago I finally got a reply in private communication from some members of
50 the core team - they want the controller object to be mostly immutable, and
51 that of course would not be possible if one of it's attributed contained data
52 changing with each new request. Immutable objects are a good design choice and
53 I accepted this answer but later I though: Wait a minute - what if we recreated
54 the controller object anew with each request? Then it could hold the request
55 data and still be immutable for his whole life time. This was the moment when
56 WebNano was born.
57 <p>
7aed9ac article
Zbigniew Lukasiak authored
58 I have tried many Perl web frameworks but I
915520e article
Zbigniew Lukasiak authored
59 found only one more that uses controllers in request scope - it is a very
60 fundamental distinguishing feature of WebNano. It's not only about reducing
61 the clutter of repeatable parameter passing - I believe that putting object into
62 their natural scope will fix a lot of the widely recognized <a
63 href="http://jjnapiorkowski.vox.com/library/post/does-anyone-else-hate-the-stash.html">problems
64 with the Catalyst stash and data passed through it</a>. In procedural
65 programming it is not a controversial rule to put your variable declarations
66 into the narrowest block that encompasses it's usage - the same should be true
67 for objects. And if you think that creating the controller object with each
68 request would be too slow - then think again: <a
69 href="http://www.cpantesters.org/cpan/report/7c61aa08-d810-11df-8503-f91d06264d1f">Object::Tiny
70 on one of the tester machines could create 714286 object per second</a> -
71 and even Moose - the slowest OO framework tested there could create 13459
72 objects, a few of such operations should not make much difference for web
73 applications. By the way I also tested these theoretical estimations with more
74 down to earth ab benchmarks for a trivial application serving just one page -
75 WebNano came out as the fastest, by a wide margin, framework tested.
76
77
78 <h2>Decoupling</h2>
79
80 The reason why WebNano is the fastest framework is probably that it just does
81 not do much. WebNano is really small, in it's 'lib' directory it currently has
82 just 232 lines of code (as reported by sloccount) and <a
83 href="http://deps.cpantesters.org/?module=WebNano;perl=latest">minimal
84 dependencies</a>. There are CPAN libraries covering all corners of web
85 application programming - what the framework needs to do is provide a basic
86 structure and get out of the way. <p> Catalyst started the process of
87 decoupling the web framework from the other parts of the application, with
88 Catalyst you can use any persistence layer for the model and any templating
89 library for the view. The problem is that the <code>model</code> and
90 <code>view</code> methods in Catalyst become rather empty - there is no common
91 behaviour among the various libraries - so all these methods do is finding the
92 model or view by it's name. For WebNano I decided that
93 <code>->Something</code> is shorter and not less informative then
94 <code>->model('Something')</code> - and I got rid of these methods.
95 <p>
96 The other thing that I decided not to add to WebNano is initialization of
97 components. The Catalyst application builds all it's components and later
98 serves as some kind of a Service Locator for them. Because of that the
99 framework needs to know how to create every component of the application and we
100 have the endless list of Catalyst adapters for practically any other popular
101 CPAN library. In WebNano I decided to follow the usual advice and decouple
102 these two distinct parts of the application into separate code units - or
103 rather to limit WebNano to web part and let the programmer use whatever she
104 wishes for the initialization part. I've heard that <a
105 href="http://search.cpan.org/dist/Bread-Board/">Bread::Board</a> is a very
106 sophisticated tool for that, for my more simple experiments <a
107 href="http://search.cpan.org/dist/MooseX-SimpleConfig/">MooseX::SimpleConfig</a>
108 was very convenient.
109
110 <h2>Localized dispatching</h2>
111
112 Out of the box WebNano supports only very simple
113 dispatching model - this can be enough because this default dispatching is
114 so easy to extend and override on per controller basis. Dispatching is about
115 what subroutine is called and with what arguments - this is strongly tied to
116 the subroutines themselves. This is why I don't believe in external
117 dispatchers where you configure all the dispatching for the application in one
118 place. The dispatching might be in one place - but all practical changes need
119 to be done in two.
120 <p>
121 Writing a dispatcher is not hard - it becomes hard and
122 complex when you try to write a dispatching model that would work for every
123 possible application. I prefer to write a simple dispatcher covering only the
124 most popular dispatching scenarios - and let the users of my framework write
125 their own specialized dispatching code for ther specialized controllers. With
126 WebNano this is possible because these specialized dispatchers don't interfere
127 with each other.
128 <p>
129 I also believe that this will make the controller classes more encapsulated
130 - thus facilitating building libraries of application controllers.
131
132 <h2>Granularity</h2>
133
134 I like the way Catalyst structures your web related code into controller
135 classes - this is a step forward from the <a
136 href="http://search.cpan.org/~markstos/CGI-Application-4.31/lib/CGI/Application.pm">CGI::Application</a>
137 way of packing everything into one class. I don't have any hard data to
138 support that - but the granularity of packing a few related pages into one
139 controller class feels just about right. It gives room for expansion by adding
140 new classes and dividing existing ones - and it also does not clutter the
141 application code with too many nearly empty classes. This is a very important
142 feature and in WebNano I copied this.
143
144 <h2>Experiments with inheritance and overriding</h2>
145
146 One of the WebNano tests is an application that a subclass of the main test
147 app. It passes all the original tests and could be completely empty if I did
148 not want to test the overriding of the inherited parts. I have seen many times
149 a need for such behaviour, from 'branding' websites, through SAAS to reusable
150 intrenet tools - most of the time this was solved by copying the code.
151 Inheritance has it's problems but it would fit well this ad-hoc reuse and it is
152 still better then 'cut and paste' programming. The key point here is that you
153 need to override much more than just methods. The experiment I am doing with
154 WebNano is about overriding application parts: controllers, templates and
155 configuration, and independently at the level of individual controllers once
156 again overriding it's templates, plus of course the standard OO way of method
157 overriding.
158
159 <p>
160 I think with this type of inheritance it would be more
161 natural to publish applications to CPAN, because they could be operational
162 without any installation. The user could run them directly from @INC and only
163 later override the configuration or templates as needed.
164
165 <h2>Universality</h2>
166 In the most common case a web application serving a request needs to: fetch
167 data from the database, do some computation over this data and render a
168 template with the computed values. Doing those tasks typically takes over 99%
169 of the overall time spent serving a request. The 1% of time spent in the
170 application framework processing does not matter much and reducing it further
171 would not result in any noticeable speed increase of the whole application.
172 Yet there might be some rare cases where the application needs to serve for
173 example small JASON data chunks from a memory cache - even when this is a small
174 and simple part of the application - if it generates enough volume - then the
175 speed of the framework suddenly becomes important. Would you code that part in
176 PHP?
177 <p>
178 It is well known that using Moose generates some significant startup overhead.
179 For web applications running in persistent environments - this does not matter
180 much because that overhead is amortized over many requests, but if you run your
181 application as CGI - this suddenly becomes important. I was very tempted to
182 use Moose in WebNano - but even if CGI is perceived so passe now - it is still
183 the easiest way to deploy web applications and also one that has the most
184 widespread support from hosting companies. Fortunately using MooseX::NonMoose
185 it is very easy to treat any hash based object classes as base classes for
186 Moose based code, so using WebNano does not mean that you need to stick to the
187 simplistic Object Oriented Framework it uses.
188 <p>
189 The plan is to make WebNano small, but universal, then make extensions that
190 will be more powerful and more restricted. I think it is important that the
191 base platform can be used in all kinds of circumstances.
192
193 <h2>Conclusions</h2>
194 It is early to say if WebNano will live to the promise of facilitating the
195 development of web application components. There is a first component at CPAN:
196 <a
197 href="http://search.cpan.org/dist/WebNano-Controller-CRUD/">WebNano::Controller::CRUD</a>.
198 It still bears the label 'experimental' - but I used it in <a
199 href="https://github.com/zby/Nblog">Nblog</a>. When I compare it to my
200 first similar product <a
201 href="http://search.cpan.org/~wreis/Catalyst-Example-InstantCRUD-0.037/lib/Catalyst/Example/Controller/InstantCRUD.pm">Catalyst::Example::Controller::InstantCRUD</a>,
202 it was simpler to write and deploy, and I think it's inheritable templates are
203 especially nice - you don't need to write your own templates to see it working,
204 later you can easily override the defaults. The surprising thing when
205 converting Nblog from Catalyst to WebNano was how little I missed the rich
206 Catalyst features, even though WebNano has still so little code. I think it is
207 a promising start.
208
209 <p>
210 Recently I discovered that many of my design choices are echoed in the
211 publications by the Google testing guru <a
212 href="http://misko.hevery.com/">Miško Hevery</a>. My point of departure
213 for the considerations above were general rules - like decoupling,
214 encapsulation etc - his concern is testability - but the <a
215 href="http://misko.hevery.com/2008/08/21/where-have-all-the-singletons-gone/">resulting
216 design</a> is remarkably similar. There is a lot of good articles at his
217 blog, some were similar to <a
218 href="http://misko.hevery.com/2009/04/15/managing-object-lifetimes/">what I
219 already had thought over</a>, <a
220 href="http://misko.hevery.com/2009/04/08/how-to-do-everything-wrong-with-servlets/">others</a>
221 were completely new to me. I recommend them all.
222 </body>
223 </html>
Something went wrong with that request. Please try again.