>> NICHOLAS ZAKAS: I'm not sure how long ago
it was, now; maybe a couple of months ago.
Whenever Velocity was, Gonzalo and I were
hanging out, and he said: you know what I
really want you to do a talk on? JavaScript
architecture, and how you make stuff so that
it keeps working, and doesn't break. This
is where that talk came from.
I'm going to apologize up front; this one
is a little bit more of theory than the talks
that I usually give. I've tried to intermix
some code, but I don't have a lot of code
to look at, so to make up for that I've put
in a lot of pictures. I hope you guys don't
mind that.
A little bit of background on me. As Gonzalo
said, I work on the homepage. I'm also a contributor
to YUI, testing library profiler cookie I
wrote, and I've done a few books, and wrote
a chapter for Steve Sauder's Even Faster websites
-- all great stuff that you should go out
and buy.
[laughter]
Why is that funny?
What I really want to talk to you about is
JavaScript application architecture. When
people think about architecture, they usually
start by thinking about a whiteboard with
a bunch of boxes on it, so I thought it would
be remiss if I didn't start out with that
as well. But in most cases the boxes represent
things that you actually end up building,
so what I want to talk to you about is that
building is actually an application framework
for your JavaScript.
I like to think of an application framework
as a lot like a playground. There's a bunch
of different stuff that's going on in a playground:
you have your swings, and your slide, all
kinds of different things that are somewhat
related, but not necessarily related to each
other. They're related in their final purpose,
right, you go there to relax and hang out,
and have fun. But the slide and the swings
don't necessarily know what the other is doing.
I think that's a really good metaphor to talk
about good architecture in general.
A lot of people stop at this point and say
wait a minute, isn't that what JavaScript
libraries do? Like, there's a bunch of stuff
out there and you just get to use it? But
JavaScript libraries are more like a toolbox.
They provide a bunch of stuff for you to build
things with. You could build a playground,
or maybe you could build a car, or maybe you
could build a building. Those tools are there
for you to use, to continue building -- that's
not where you should stop.
What we're really talking about as we're beginning
this architecture is, the JavaScript base
library is the very bottom, lowest level.
Then on top of it you have the application
core, that's actually going to run the app.
I'm going to get a little bit more into that.
But first I want to talk about a little thing
I like to call Module Theory. And Module Theory
basically says everything is a module -- anything
that's out there, anything that can possibly
be defined, can be defined as module. If we're
talking about module definition, this is actually
the dictionary definition of 'module'. Some
of these don't really fit. The one that I
find most interesting is number four: 'an
independently operable unit which is part
of the total structure of a space vehicle.'
[laughter]
I think that's great. I think that's exactly
what we're doing. We're building this space
age stuff, and you want to get it into a bunch
of little pieces. We have the international
space station out there in orbit -- I told
you there were pictures. It's really, really
interesting; it's this massive thing that's
just floating around in space. How do they
get it up there, anyway? It's huge, and clearly
not very aerodynamic, so if you were just
going to shoot that thing up there, it would
blow up pretty quickly. What they did was
they separated it into a bunch of smaller
modules, and each of the modules was built
separately, built so that it would interlock
with other ones, and each module is pretty
self-contained. If you chop off any one of
these, everything continues to work. So that
was a pretty interesting metaphor for how
to build things. If you can take this modular
approach with something as complex as an international
space station, it might just have a chance
to work on your web application. Not sure,
but we'll see.
So this is the question: how does it apply
to web applications? I like to think of this
definition of a web application module as
very similar: we're talking about an independent
unit of functionality on the page. Sometimes
that's easy to locate, and sometimes it's
not, so I'm going to start with a place where
it's easy to locate, and that's on My Yahoo!
How many My Yahoo! users do we have? Nice,
excellent. So you're probably used to this
concept of having all these little modules
on your page that are each doing something
different. They're pretty easy to identify
because they have boxes around them. There
they are. Very easy. They're all doing different
things.
But you can actually go one step further and
take a look at the header and say: you know
what? I bet that I could break that down into
a bunch of smaller modules as well. Maybe
it looks something like this. I don't know,
it could. Part of the debate is exactly what
part is an independent module, and what parts
are interrelated, so I don't want to get too
deep into why I think this is how it should
be separated. But as you're going through
and thinking about various web applications
that you're working on or use, try and think:
OK, what exactly is the functionality of this
piece of the page? And how much of the rest
of the page is related?
Web application modules in general consist
of HTML, CSS, and JavaScript, and all within
one part of the page. When you think of it
that way, if you're just working on the HTML
and the CSS and the JavaScript of that small
part of the page, massive problems become
much simpler.
One of the key concepts is that any of these
modules should be able to live on its own.
So if I take 'weather' and I take it out,
I should be able to just put it onto an empty
page and have it work. The reason why that
should work is because it's all self-contained;
there's nothing about weather that knows that
it's on My Yahoo!, it doesn't matter. It should
just continue to work so long as it has its
HTML, its CSS, and its JavaScript. That's
one of the goals of creating scalable JavaScript
application architecture.
Loose coupling is really, really important
in this. You want to make sure there aren't
artificial dependencies between your modules,
which tends to happen a lot in web development.
You really have to keep an eye out for them,
because tight coupling is your enemy. Loose
coupling is your friend. In order to enforce
this, you have each module with its own sandbox.
I'm not talking about sandbox in the strict
security sense that we usually think of it,
so it's more of a convention of a sandbox,
where people just say OK, I know that I'm
supposed to stay within the bounds. Kind of
like a little kid -- there's nothing preventing
the little kid from getting up out of the
sandbox and leaving, except for the fear of
getting caught by the parent that's standing
by.
Again, take a look at the architecture that
we have thus far. The sandbox actually sits
on top of the application core, because you
don't want modules interacting directly with
the core. So the modules kind of exist on
the periphery. What they interface with is
the sandbox, and that is their view into the
world.
These are the four basic parts of the architecture
I'm going to talk about. Let's start out with
the modules. Modules have limited knowledge
of what's going on on the page. Each module
knows about the sandbox, but it doesn't know
about any of the other modules that are on
the page, and that's really important.
This is just some sample code presuming how
you might register a module. I have nothing
that actually runs behind this, so if anybody's
going to ask me for sample code afterwards,
it doesn't exist, it's just on this slide.
But the basic idea is that you want to register
your module somehow, with the core, to let
it know that it's there. As part of that registration,
you make available this sandbox object. In
this case, I'm saying I want to pass in a
function, and then the sandbox option is passed
in as an argument. Then from that an object
can be created, using that sandbox. When it's
returned that can be used by the core, in
order to run that module.
So which parts actually know about the web
application being built out of that whole
stack? None of them do. Each of those parts
is like a puzzle piece, and it really doesn't
matter if each puzzle piece knows the whole
picture -- it just has to do its job, and
that's all. Exactly what is a module's job?
It depends on the module, of course. A weather
module's job is to tell you about the weather.
Stock modules tell you about the stock market.
So each module's job, really, is to create
a meaningful user experience within its boundaries.
That's what it's supposed to do. Then the
web application is created when everything
is doing its job. The entirety of My Yahoo!
is created when all the modules on its page
are doing their job. And the entirety of the
homepage is created when all of its modules
are doing their job. In this case, Hello Kitty
is created when the puzzle is put together.
Really, there's no laughs for that at all?
OK.
This doesn't mean that modules can do whatever
they want in order to do their job. Definitely
not. I like to think of modules as kids, because
you need to give them guidelines so that they
don't get into trouble. Since most web developers
are like kids, that goes double for them.
Some of the rules that modules need to abide
by are actually similar to little kids' rules.
First: hands to yourself. Only call your own
methods, or those on the sandbox, and you
shouldn't be touching anything you don't have
permission to touch. Don't access DOM elements
outside of your box. Same thing. You have
no need to do that -- everything you need
should be contained within one box, and you
just touch everything that's in there. Don't
access non-native global objects. Hopefully
there's none floating around, but you should
definitely not be worrying about global objects,
because creates a really strict dependency
on where that module can live - you have to
make sure that all those global objects are
always available wherever the module can live,
and that's really fragile.
Next: ask, don't take. If you need anything
that you can't get yourself, you need to ask
for it. That's the sandbox's job. Its job
is to stand there and wait for you to say
hey, you know what I really need? I need an
ice cream. And the sandbox will say, well,
let me see if you actually have permission
to have this ice cream or not. If it's right
before dinner, then you probably don't.
Don't let your toys lying around. So that's:
don't create global objects. You're just polluting
it for everyone else, and that increases the
likelihood that you're going create some fragile
structures. And don't talk to strangers. You
shouldn't be interfacing with any other modules
on the page. You don't know them, they don't
know you, regardless of how much candy they
offer, just stay away.
Modules really need to stay within their own
sandboxes. Sometimes as a module developer
you'll start to feel cramped, and that you
don't have enough access, but really, the
level of access that you have is enough for
you to do your job, and it's also enough to
ensure the overall stability of the application.
Next part is the sandbox itself. There it
is, sitting there in the middle. The sandbox's
job is to ensure a consistent interface to
the modules. That's the only thing that the
modules know about, so that really has to
stay the same. You don't have a lot of flexibility.
The sandbox also acts like a security guard,
as I said earlier. It knows what the modules
are allowed to access and what they're not.
So there's a bit of marshalling going on there.
Here's a very simple example. Let's say in
the admit method you want to check if you
can haz Cheezburger. You need to check that
first before you can try to do something.
So call it on sandbox, and if you can, you
say thank you. Told you, really light on code
today. So the sandbox's jobs. I would say
consistency: the sandbox has to be very dependable.
Security. Determine which parts of a framework
a module can actually access. Communication,
which turns out to be very important. Translating
requests that the modules make into core actions.
I know that the module wants to do this -- what
does that mean at the core level? It's very
important to take the time to design the correct
sandbox interface, because this just has to
stay the same. It has to remain consistent.
You can add new functionality, but you can't
remove it, and you can't change existing functionality.
This is actually where you should spend the
most amount of architecture design time, in
figuring out this interface. Eventually you're
going to have a ton of modules, and you don't
want to have to go back and touch a ton of
modules when things change.
The next part is the application core. This
is where we start to get into fun stuff. The
core manages modules -- that's its only job.
What does that mean? The first thing is it
tells a module when to start and when to stop.
A lot of times we fall into this habit of,
OK, I know on page load I need to do stuff.
Or on DOM content ready, I need to do stuff.
And really, if you have everything on the
page hooking into those events, you end up
with chaos, especially because you're not
sure exactly what else is going on, or in
what order things need to happen in order
for them to work properly. So it shouldn't
be up to a module to start itself -- it's
up to the core to say OK, you now have permission
to start. That may be on load, it may be before,
it may be after, but the core is really the
only thing that understands when it's safe
for a module to start itself. On the other
end of it, it's also the only one that knows
when it's safe for a module to stop itself,
whether that be on page unload, or before.
So the core's main job is really managing
this module life cycle; when is it OK to start,
and when is it OK to stop.
I just have some sample code to walk through
about this, to show you what a possible implementation
of the core might look like. This is really,
really simple: not enough error handling.
Just want to throw it out there as a prototype
to try and solidify some of these things.
First thing I did was have the register method,
which I showed you earlier -- I register a
module ID, and create a function. I'm just
going to take those and store them for later.
When start is called, the first thing it has
to do is create an instance -- so it calls
the creator method, and passes in a new instance
of the sandbox object that's going to be the
module's view into the world. That instance
is stored, and then this init method is called.
Clearly we're expecting that there's going
to be an init method on the object that's
created. There has to be some sort of interface
so that the core can work with it. And then
stop, we're just going to do the opposite.
If there's an instance we're going to call
destroy, and we're assuming that it's there,
and then just get rid of the instance all
together.
Little bit more. It's probably useful to have
'start all' and 'stop all' methods that will
go through and start everything that's in
the system, or stop everything that's in the
system. Just a very simple example of how
to do that: go through, pull out each module
ID, and go ahead and start it. It's the same
with stop. So on your web application, what
actually ends up happening is you create a
series of lines of module registration -- registering
weather module, registering stock module,
and whatever else happens to be on the page,
you end up registering them all -- then at
the end, you 'start all'. 'Start all' is really
like calling 'application.init'. I'm starting
everything, getting everything ready, and
the application is ready to be used.
Application core also manages communication
between modules. This is the latest iPhone,
in case anybody hasn't seen it. A little bit
more complex example is Twitter. But it's
a good example of why this inter-module communication
is necessary. You can go through Twitter and
you can do the same sort of thingâ€¦ Are
people noticing themselves up on my Twitter
feed? I actually had to filter this before
I came in; there were some inappropriate tweets
that were showing up. Do a little photoshop.
But you can go through Twitter and go through
the exact same process of just finding modules
that are on the page. Some of them are really
obvious, and some of them are not. Again,
you can kind of debate whether my breakdown
is the right breakdown or not, but just conceptually,
you start to look at the page and you start
to identify different parts as OK, this could
be its own module. As you go through, you
start find, OK, so you can break this down
pretty logically. But the interesting thing
here, as opposed to My Yahoo!, is that there's
actually interaction between these modules.
You go and post a status and that ends up
showing in your timeline almost immediately.
Then you start to click on the filters over
on the right, and those end up affecting the
timeline too. Everything is just going back
to the server and saying OK, display some
new information in the timeline. But you really
don't want these things to have any knowledge
of what else is on the page, so this is where
this intermediary communication becomes really
important.
If you were going to look at a traditional
web application, it probably would look something
like this, where you have a bunch of different
objects that represent different parts of
the page. Hopefully, at least, you've got
this far. Then each of those refers to others.
So you have timeline filter that calls timeline,
and then you have status poster which calls
timeline, and then you have timeline that
has all this stuff. The issue here is tight
coupling. If you were to go through and remove
timeline, then those other two objects break,
and they can't work anymore. It may just be
because you're creating a new timeline that
does something different, or a different visualization,
but how do you know how many other parts of
the code are referencing timeline, that you
then need to go back and fix? And how confident
are you that you'll be able to catch all of
those before you push everything into production?
It's very fragile.
So what you're really looking at is something
a little bit more like this, where each module,
again, doesn't know anything about other modules,
but it knows that it's doing something interesting
to the web application. Because it's interesting,
it wants to let people know, hey, I'm doing
something. In this theoretical implementation
of the framework, I have a notify method on
sandbox object. That basically says, OK, tell
me what this information is, what's going
on, and then give me the data that's associated
with it. So when the timeline filter changes
a filter, it doesn't access the timeline object
at all, it just says hey look, I'm changing
the filter of the timeline. If anybody is
interested in the fact that I am changing
the filter on the timeline, I'm just letting
you know. The same with the status poster.
It's like hey, I'm just letting you guys know
I'm posting the status.
If any of you happen to be interested in it,
I suggest you do something about it. So this
is much looser coupling. Then if you look
at the timeline, it just goes in and says
OK, I know a certain number of things that
I'm going to be interested in knowing about
- timeline filter change, and post status.
So in my make up implementation here, I have
a method called listen which allows you to
set up a notification function whenever one
of those things happens.
So now nobody is referring to anybody else,
but they're still communicating, and they're
communicating in such a way that I can move
things, change things, and remove things,
and everything else continues to work. So,
loose coupling? Yay. If I actually go back
in later and remove the timeline for any reason,
my status poster and my filters never throw
JavaScript errors, because they're doing their
job. They're letting the rest of the application
know that something interesting has happened.
The fact that nothing on the application is
listening for that, and doing something, is
besides the point. They've already fulfilled
their job, they've told people that something
is happening.
Another important job of the application core
is to handle errors, and to figure out what
the best course of action is. The cool thing
about the core is that it pretty much knows
what's going on at any particular point in
time, so it can also figure out exactly what
type of error happened, and then what the
appropriate resolution for that error is.
It knows, hey, this really wasn't all that
important, and an error happened, so I'm just
going to ignore it. Or, oh my God, my status
didn't get posted because for some reason
I couldn't reach the server. That's really
bad, I need to let people know about that.
That's the only part that really understands
the context of the errors.
This is a function that basically goes through,
and when you create an instance of a module,
it instruments each of the methods on that
object, so that any errors that are coming
through are being trapped and then funneled
into a log function. It's a little bit complicated,
but the basic algorithm is: go over the instance
object that was created, look for functions,
and for each of those functions create a new
function that has a try catch in it. Call
the original function, and then if any error
happens it goes into the catch, and that goes
into your log.
Now, log can be anything. It could also just
be some generic error handler that you have.
But the basic idea is that all of the errors
end up getting funneled into the same entry
point, so that you know what to do. The modules
themselves often don't need to know that an
error occurred, because it's usually their
fault that the error happened. Most of the
time, errors like this should be caught before
going out, but every once in a while that
doesn't happen. So it's really helpful to
be able to recognize them, and definitely
log them back to the server.
I don't want to go too much into error handling
because I actually did an entire talk on this
last year. If you want to learn more, this
is the talk: it's called Enterprise JavaScript
Error Handling, that I gave at Ajax Experience
last year. It's up on SlideShare, so you can
go and poke through that. But basically, everything
that I say to do in that presentation is the
job of the application core. That's where
all of your error information should go, and
that's where a lot of your errors should be
handled.
So just to review the application core jobs.
Manage the module life cycle, so tell it when
to start, and tell it when to stop. You don't
want modules trying to figure that out on
their own. Enable the inter-module communication.
It's really, really important for a loose
coupling to make sure that you can add and
remove modules whenever you want, and not
break stuff. General error handling, to detect,
trap, and report the errors that are in the
system. And also to be extensible, because
the three things that it's doing, that I have
listed here, are actually not enough for it
to be doing.
Why not? Good question. Web applications change.
A lot of times, you have no idea the way that
they're going to evolve. Something that starts
out very simple can grow to be something very
complex. My favorite example, of course, because
I work here, is the Yahoo! homepage. It starts
out very simple, and ends up very complex.
Of course, nobody was writing JavaScript was
Yahoo! first was launched, but the ideal 
is that you should be able to continue growing
and changing your web application without
changing the underlying infrastructure. If
you make it correctly, it should be able to
continue on for years and years. So you need
to plan for extension right from the beginning.
In your architecture, there needs to be extension
points, places you know you can add additional
functionality going forward, because you don't
have all of it right now.
The really cool thing is that when you build
stuff for extension it can just last forever.
I take the digital camera explosion as a really
good example of that. The smart manufacturers,
when they made their digital cameras, made
them so that all of the lenses for their non-digital
cameras could still work. How brilliant is
that? Consistent interface. You get to upgrade
a piece of the architecture, and everything
else continues to work. You don't need to
buy a whole bunch of new lenses, you don't
need to buy a whole bunch of new cameras.
You can mix and match the stuff that you already
have. Just absolutely brilliant architecture.
So when you look at the architecture we've
had thus far, what we really need is extensions
to the application core as well.
What extensions could you possibly want? Error
handling, for one. Maybe you want to have
an extension that decides how to deal with
all of your errors in a consistent way. That
can be swapped out later if you decide to
change your mind. Ajax communication. This
stuff changes all the time; we're definitely
not doing it the same way that we were two
years ago. New module capability. So anything
you think that a module should be able to
do, and isn't able to do, you plug it into
the core, make it available through the sandbox,
and there you go: instant module upgrade.
Any sort of general utilities that you want
to make available - could be for anything.
I want to focus on Ajax communication for
a minute, because I think this is a really,
really important part of the extension of
the application core. It's really important
because Ajax communication comes in a bunch
of different flavors, and whatever flavor
you're using right now might not be what you
want to use later. There's usually three parts
to be concerned about when you're dealing
with Ajax communication. There's the request
format - which is basically everything that
goes over the wire, whether it's a get, whether
it's a post, and what the format is - the
entry point that you're hitting on the server,
and then the response format that you get
back. These three things, any one of them
is likely to change at any given point in
time, so you really want to have your Ajax
extension.
I'm going to start out with an Ajax XML extension,
just to show you the natural progression that
I think most of the web applications have
gone through in the past couple of years.
A lot of people started out with Ajax and
XML, going back and forth. Let's just say,
for example, that my request format that I'm
starting out with is a get request, and I'm
passing in just queries string arguments,
and I'm hitting this /ajax entry point because
it's really descriptive. And the response
format comes back as some XML structure, and
that has a status node in it that should be
either 'OK' or 'error', and there's a data
node that has additional data in it.
So, basic implementation using low level Ajax,
basic XHR object. You have a lot of code in
there. You create a new object, and you open,
and then you assign your own ready state change,
and a bunch of stuff that happens in there.
You check the ready state, and you check the
status code, and then you try to parse the
response -- hopefully there's no parse error.
You go and you pull in the status node, and
you want to make sure that the status node
indicates that it's also OK. Then you process
the data that's in the data node into some
format that everything can understand, because
you don't want it in XML, and then you pass
that into handle success. If any of that doesn't
work, then you want to call handle failure.
So there's a lot of code written there, and
this is definitely something that you don't
want to see in a lot of places in your JavaScript,
because any time any of this changes, all
of that has to change. Very bad.
So you say: 'hey, JavaScript libraries really
help us with that, right? It abstracts away
a lot of this stuff!' Oh hey, wait a second,
that was the wrong segue. So let's go through
the rest of these pointers first. Entry point,
request format, response format. OK.
'Hey, JavaScript libraries really help us
with that, right? It abstracts away a lot
of the stuff that we worried about!' Yeah,
OK. It looks a little bit better. This is
using YUI 3, so we call y.io, that's cool.
We've still got the entry point in there,
that's alright. We have success and failure,
which is helpful because we don't have to
worry about checking for Ready State 4, and
we don't have to worry about checking for
Status 200 or 304, so that helps, remove that
step. But we still have some redundancy there,
because both success and failure have to call
handle failure in case something goes wrong,
so if the response format is incorrect, or
the server just says I did something wrong,
like oh crap, OK. So this is still a little
bit messy.
We still have the entry point, we still have
knowledge of the request format, and we still
have knowledge of the response format. As
an added bonus, we have a reference to the
library that you've been using. So this is
actually more tightly coupled than the other
one, because now you're relying on a library
to be there.
So what do you actually want to do? Well,
what I actually want to do is, I want for
my module code to be like this. I just want
to say: I want to make a request, here's the
data that I'm sending, my names and my values,
and then when this succeeds let me know, and
if it fails then let me know. In this case,
the data that's coming in is already formatted
in the format you expect. Success is only
called if Ready State 4 was hit, Status 200
or 304 was hit, the response format is parsed
correctly, and the status node says OK. Anything
other than that, failure is called - because
a module doesn't really care why something
failed. It just knows I asked for something,
and I was expecting something back, and I
didn't get it. That's all it cares about.
This is very loosely coupled; you'll notice
that we had none of those three things -- we're
not worrying about request format, we're not
worrying about entry point, and we're not
worrying about response format. The Ajax extension
encapsulates all three of these, and that
means any of these can change without forcing
modules to change.
Now I'm going to change, because it's 2009,
to a JSON response, but I also need to change
my entry point, because somebody said they
didn't like /ajax, and that's OK, because
I can just plop in the Ajax JSON extension
into my framework and everything continues
to work. It works because the modules had
no idea about any of those three parts. It
still is using the exact same interface that
it was before, we've just swapped stuff out
from underneath.
So the jobs of the Ajax extension hide all
of those details. Modules don't need to know
any of it; it's really irrelevant to what
they're trying to accomplish. Provide a common
request interface -- that basically means
name value pairs that I'm going to send in,
and it doesn't need to know anything about
the actual format of that. Common response
interface -- so you don't really want to take
exactly what was passed back by the server
and hand it to the module, because that might
change. So you want to have some good intermediary
format that you know handles all of your capabilities.
And managed server failures -- again, modules
don't care why they didn't get what they were
asking for, just that they didn't get it.
Let's now move on to the base library, which
is the part that most people seem to be most
familiar with. Base library, the very bottom
of the stack, provides basic functionality.
Pretty simple. There's a bunch of things that
you can use as a base library out there. For
this architecture, it really doesn't matter
all that much; just pick one and go with it.
But the problem that you need to try to avoid
is that most web applications end up being
too tightly coupled to those base libraries.
I think that's partly because we, as developers,
get really emotionally attached to our base
libraries. If I love YUI, I love YUI, and
if you're telling me I can't use YUI, I don't
like you very much. Hence this picture. I'm
here to tell you to release that emotional
attachment to your base library. It's not
doing all of the job for you, and you actually
have enough knowledge to do a lot of stuff
without it.
There was an interesting presentation a couple
of years ago at OSCON, done by Joseph Smarr
from Plaxo. He later came to Yahoo! to give
the same talk, and he had this one slide that,
at the time, I remember people getting upset
about. Now I find it really, really funny,
so I thought that I would drag it back out.
Basically, what he said towards the bottom,
his second big bullet point was: "to minimize
your dependency on third-party library code,"
so your base libraries. "Lots of extra code
comes along that you don't need" -- he was
primarily worried about performance in this
case. "Libraries solve more general problems.
Use it like scaffolding." I thought that was
a really great metaphor, because that's exactly
what it should do. You should use it like
scaffolding to get the rest of your architecture
up and running. If anybody's interested in
seeing the rest of his presentation, you can
go to check out his website; he has that up
there. Very good talk.
Basically, really the only thing that needs
to know about the base library is the application
core, because it needs to interface directly
with that base library. The reason why that's
important is because you don't want to have
that dependency on third-party code. I could
start out with my application built on Dojo
and decide later on that that's not doing
it for me, and what I really want to do is
switch it to YUI. Now, if the application
core is the only thing that knows about the
base library, this is completely possible,
because you don't need to change anything
else in the architecture. As soon as other
parts know about the base library, trying
to do this swap becomes a lot more painful
and a lot more dangerous.
The jobs of the base library, which are very
important. Browser normalization. We really
want any browser specific logic to be in that
base library layer. The rest of the application
should never have to worry about it. General
purpose utilize s- you know those, add class,
remove class, all of that fun stuff. Of course,
CSS query selectors. Parsers and serializers.
Object manipulation. DOM manipulation. Ajax
communication. All the stuff that makes your
life easy in general. And then provide low
level extensibility; when you're choosing
your base library, you need to make sure that
you can write extensions for it, as well.
So really, your architecture ends up looking
like this. We have a core in the middle of
things that you rely on, and then a bunch
of extension points around the outside so
that you can continue to grow your application
in whatever way that you want to.
Let's talk a little bit about knowledge within
the architecture, because I've thrown this
around a lot and I just want to give a summation
of this to make sure that everybody understands
what parts know about what other parts. So
in the total architecture, only the base library
knows which browser is being used, and it
needs to know that because it needs to do
the browser normalization. So if you come
across a time when you need something else
to know about the browser being used, the
correct point to extend is actually the base
library. You don't want anything above that
layer knowing about the browser.
Only the application core knows which base
library is being used. That's really important.
These two end up being tied together. The
application core's job is really to abstract
that base library away. Only the sandbox,
on top of the application core, knows which
application core is being used. You actually
can swap out an application core pretty easily;
you may need to recode the sandbox a little
bit, but the sandbox interface always remains
the same, and it doesn't matter to the modules
that are using it. The modules know nothing
except the sandbox exists. They also don't
know about each other. And no part of this
architecture knows about the web application
as a whole. Remember, they're just individual
pieces that are each doing their own little
job.
The advantage of this is you can create multiple
different applications using the same architecture.
I take it nobody's picking up on the metaphor
on this one.
[laughter]
You use the same base, you can use the same
core, you use the same sandbox. But to create
a different application, you just provide
different extensions, and you provide different
modules. So you can create My Yahoo! and the
Yahoo! homepage on the exact same architecture,
because the only difference is the extensions,
pretty much. Everything else remains the same.
A really big bonus is that when you separate
these into independent units of functionality,
you can also test them separately, because
you know they don't have a whole bunch of
side effects on different things, and you
know that they're not dependent on a bunch
of things in the environment that you couldn't
possibly predict. So you can actually set
up unit testing a lot easier this way than
you can traditionally -- you just take each
individual component, stick it into an area
where you start poking it and telling it to
start and stop and do its various jobs, and
then verify that that's working. Then if you
can verify that each part is doing its job,
you eventually verify that the web application
is doing its job. Much like an assembly line
at a pastry shop.
Basically, if you think of it like Jenga,
scalable JavaScript architecture allows you
to remove any block, and the tower still stays.
It doesn't matter if it's at the top, it doesn't
matter if it's at the bottom. Everything still
continues to work. That means you spend less
time worrying about maintaining a bunch of
code, and more time really being able to be
effective creating new features, testing things,
and even creating entirely new web applications.
So that is that. Are there any questions?
The question was the naming of a module, because
coming from a Python background, he thinks
of it more as a namespace. Does anyone want
to make fun of him for that?
>> AUDIENCE MEMBER 1: In Ruby it's different
too.
>> NICHOLAS: Yeah, so this is the general
problem that I think we've run into because
a widget, to me, means something that is reusable
in a number of different modules. I look at
a menu component as a widget, and a module
as the weather module that uses that widget.
I think we run into this a lot in computer
science, whether it's a widget, or a module,
or a component, or a part, or a stub, or an
extension, or a plug in. I think whatever
makes it make the most sense to you, feel
free to call it.
I've found that module has worked pretty well,
and that is coming from my working on Yahoo!
and later on the homepage. Everybody was already
calling it modules, so that's what we adopted,
and everyone understands. People in the room
that I work with, does anybody find 'module'
confusing in the context that we use it? Besides
Oliver, because that doesn't count. OK.
Interesting question. So the question was,
you've talked a lot about functionality, but
what about data? Who owns it, and how does
it get passed through?
For that, I guess I'll turn it around with
another question. Who needs to know about
weather data besides the weather module? I
don't know.
>> AUDIENCE MEMBER 2: It depends on whatever
the module's are doing.
>> NICHOLAS: Right. I tend to believe that
data has some specific owner on the page.
As in the weather example, I think the weather
module owns the weather data for the page,
and the way that it can make it available
to other places on the page is through that
inter-module communication mechanism. But
then you get into interesting quandaries of:
what if someone else also thinks they own
weather data, and they start passing that
around, and then where's the source of truth?
To answer, I guess, more succinctly, I think
that there typically is a module that's responsible
for some specific data. If there's something
that is more general, you can end up having
that as, like, application core data, and
then expose it through the sandbox in some
way. Does that make sense? Potentially? Maybe
after another beer it'll make more sense.
The question was, is it intentional to create
a new instance of sandbox per module. Yes,
that was intention in this example. But as
I said earlier, this was just a very basic,
non optimized sample example. If you find
that there is a better way to do it, then
by all means do it. I thought that this might
be the easier way for people to grasp the
concept. I think there are some bonuses to
having an individual sandbox for each, because
you don't have to worry about anything bleeding
through, or modules accidentally doing something
they weren't supposed to because some other
module is actually allowed to. That makes
me feel a little bit more safe. But I think
ultimately, there are patterns that you could
do either whether you have shared one that
you end up passing around, or one unique one
for each module that's on the page.
That's actually right -- I am moving the problem
around. I'm moving it down, and away from
where most of the code lives. Because most
of the code for your application lives in
the modules, and you'd never want to touch
those once they're working; you want to ensure
that they continue to work. So you're right
-- that application core does end up needing
to abstract what the base library is doing,
not necessarily implement. That's a bonus,
because that's what allows you to not be tied
to a particular base library.
If you are comfortable saying: I am going
to use this base library forever, then I say
go for it, and let stuff bleed through as
much as you want. If you're not sure then
it's actually worth your time to cover up
the base library functionality that you think
you need. I would venture to say interacting
with the DOM, a lot of times we think of it
as really complicated, but usually you're
doing relatively simple things; I'm adding
something, I'm removing something, I'm inserting
something. That's more or less it. Everything
else is like add class, remove class, replace
class; that stuff is useful too. So what you're
really talking about at its core is a very
small amount of functionality that you're
going to provide an extra layer on, so that
then you can provide it to the modules. Because
remember, Ajax is completely abstracted away,
anyway, so you definitely don't have to worry
about that.
Yeah, it's definitely a concern with third-party
modules, but you can provide infrastructure
around that such that it works. There are
ways to do it. I probably can't get into it
tonight, but the architecture that's here
really is the starting point, and you can
pretty much make most stuff work. I mean,
I've worked with this on enough different
types of applications that I'm pretty convinced
that no matter what the requirement is, you
can figure out a way to make it work. Sometimes
you just have to be a bit more clever than
with others.
Yeah, the idea is that it always goes down
through the layers and always comes back up,
so that the interface is consistent. There's
not enough information in that individual
module such that it can break if stuff underneath
swaps out.
>> AUDIENCE MEMBER 3: So it is true that you're
playing with base code navigation core all
the time for performance reasons?
>> NICHOLAS: Absolutely.
>> AUDIENCE MEMBER 3: Because they have a
lot of versions you're dealing with here.
>> NICHOLAS: Yeah. So, the question was, you're
always messing around with base and core in
order to speed things up. Absolutely. That's
pretty much my job at this point.
Yeah, so to sum up the question, it seems
like some things are more easy to abstract
away than other things, and the example was
the YUI data table, which is really complex.
Could you possibly abstract that away in such
a way that you could actually swap in Dojo
or jQuery underneath? Very good question.
Yes, definitely, there are some things that
are more difficult to abstract away. When
there are things that are more complicated,
usually what I do is look at them and figure
out: what is my use case for that thing? What
you'll usually find is that you're using only
a very small percentage of the functionality
of that particular widget. You're using it
in a very specific way. If you start from
that, you can usually work backwards into
some sort of easy abstraction, where you say:
it's supporting everything that I need to
support. So you don't actually abstract everything
- every event, even method -- you actually
end up abstracting just the stuff that you
need, and that's a lot easier.
I'm not going to say that it's completely
easy, because data table, I know, is very,
very complex. And it's also one of those things
where, if you're comfortable saying: I will
hitch my wagon to YUI and I will never turn
my back on it, then maybe that's something
that you're not going to be that concerned
about. If you think that there's any possibility
in the future that you're going to change
your mind, then I think it's worth putting
in a little bit more thought, and figuring
out what the proper abstractions for you are.
The question was: what happens if you have
six modules that are all trying to get data?
Wouldn't it be better to have them all go
separately because of CPU considerations?
I think, actually, that's an argument for
why it should go through the core. Because
when it goes through the core to the Ajax
extension, the Ajax extension has the ability
to say hey, I already have three things queued
up -- maybe it's not the best idea for me
to go off to another one. It maybe puts a
stop to it until the other ones come back,
and then start again, but if you have everything
going off and making their own requests, then
you have no way of stopping it, or throttling
it, or filtering it in any way.
Good questions. So I guess I'm done. That's
me, if you want to contact me after this,
if you're not catching me during pizza.
That's all.
[applause]
