20. Rules of Jan
• A codebase needs modularization
• Modularization abstracts features away
• Teams can work on separate features without
breaking code
21. Modularization?
• Node.js has require
• Similar to ‘using’ or ‘import’ in .NET / Java
• Great for abstracting away functionality
• But not for application modularity
22.
23. Downsides of
require
• Relies on the filesystem
• Two modules, same dependencies?
Copy or symlink
• Maps to folder name
• Configuration is hard
• Multiple instances
24. No dependency
model
• Static compilation: build dependency tree on
compile time
• Dependency tree not good? Compilation
error!
• Require fails at runtime
25. Now fix it!
• Static dependency list
• Resolve at startup
• Named services
• No longer require filesystem
• Easy configuration options
26. Program
•Cloud9? 5 minute intro + what's new
• Problems growing your codebase
• Introducing: Architect!
• Lessons learned
27. Architect
•Every piece of functionality is a plugin
• Plugins can consume other plugins
• An application is a set of plugins
28.
29. • Declare entity ‘jan’ with behavior
• Use ‘jan’ to do a presentation
55. Single node.js process
IDE instance IDE instance
(FTP) (SSH)
Other code
(dashboard etc.)
IDE instance IDE instance
(Normal) (Normal)
56.
57. Architect can do
• Multiple instances of same plugin
• Run independent
• In separate contexts
• But in the same process
• Manageable
• No process overhead
73. http://c9.io
Jan Jongboom
github.com/janjongboom
@drbernhard
Editor's Notes
\n
Hi!\n
Last year also present. Today I won't be speaking about Cloud9 itself, but rather about the framework that helped us progress in the past year.\n
Last year also present. Today I won't be speaking about Cloud9 itself, but rather about the framework that helped us progress in the past year.\n
The world is all about the web, from viewing information to online banking to editing documents: you won't have to leave your browser. \n
But why do the offline developers have all the perks? Great IDEs, integrated testing suites, static compilation, language analysis? But when you write javascript, the language of the web:\n
not so much. They are just happy with syntax highlighting.\n
So that's why we decided to build the best IDE for dynamic languages.\n
But other than current IDE's like Eclipse or Visual Studio, we built our IDE on top of the web. Here are some things that you can do with it:\n
Cool IDE features like ‘debugging’\n
Context aware code completion\n
Run everything you want. Because we give you a free VM, true freedom. Java? MemSQL? Already GIT.\n
Fully functional. Real VIM, Telnet, Watch Star Wars ASCII. VM included on every project.\n
Real time code collaboration like in google docs\n
See each other typing, debug together and be more productive!\n
To build these features, our code base has grown to 400,000 lines of code, plus X external modules. All in javascript.\n
Madness! A language without static compilation or type safety. And our server side part is built on node.js, unproven technology when we started.\n
Even worse, part of our architecture is decentralized. Because you get your own VM where all code lives and all processes run. \n
Sounds tedious? The good thing is that we can help YOU grow when your application gets out of hand. Here are the ‘rules of jan’ for a large application.\n
Node.js already has a way to do modularization through the use of 'packages' that you load in via `require`. They are great for abstracting away simple pieces of functionality, but they don't fit for application modules.\n
\n
* Rely on the file system\n * If two modules use the same module, we'll need two copies or a symlink\n * Require calls map to a folder name\n - Give me the folder `database` rather than\n - Give me the service `database`\n* Impossible to pass in options, what to do with the querystring?\n* Multiple instances may be loaded, so no shared state\n
And there is no explicit dependency model. When doing a static typed language, you're creating your dependency graph on compile time. If it doesn't make sense, your app won't compile. If you mistype a dependency that you load through `require` you will only find out when it's too late. At runtime.\n
So to solve this problem we built a library on the following principles:\n\n* Static dependency list, that will be resolved at start up time (next best thing)\n* Use named services, rather than folders on a filesystem\n* Make configuration options easy, e.g. passing in a connection string\n
So here are the main principles of architect:\n
So let's start building an application via architect. We have the following piece of code in node.js, where we put this talk into code:\n
To see if this is a bad function, ask yourself the question: "is this code unit testable?" In this case no: we have two types of behavior that cannot be tested indepedently.\n
And that is what you do in Architect. \n
\n
\n
To tell architect which plugins we want to consume or provide, we use the standard package.json file that is already used in normal node modules, but with some additions.\n
\n
Now we can move the code that declared `jan` into a seperate plugin:\n
\n
There is a little boilerplate added. First is the `module.exports` line. This is the standard format of any architect plugin, and consist of a function call with three arguments:\n\n- Options, we'll get to that later\n- Imports, provides the services you want to consume\n- Register, call after initialization code, provide your service\n
This model can also be applied to the presentation part of our application. Just wrap it into the architect boiler plate code, and as you might have guessed, our `imports` [HIGHLIGHT IMPORTS] object will now contain the plugin we just required.\n
The great thing is that this module is now perfectly unit testable as well. Let's say we want to verify that the `dance` function of our presenter is called 11 times.\n
You have clean code, with managed dependencies, that are easily swapped out or injected. Also in unit tests.\n
Architect is no black magic. We will still need to tell it which plugins should be loaded. It does this via a very simple javascript application containing an array of plugins, and the `createApp` function.\n
In the `createApp` step architect will build a dependency tree, and if it finds an unmet dependency it will throw at startup. For instance, if we remove the `jan` plugin from config.\n\n
If you want to check out this application, it will be available on GitHub today.\n
\n
So let's make the number of dance moves that our presenter makes configurable.\n
These options are automatically passed into your plugin the moment that the initialization code is called. \n
Because architect ensures that the plugin is loaded at the startup of your application you can use default assertions to verify that all options are passed in.\n
When now loading this plugin without the option, the application will not start, but fail with a nice error message and a stack trace pointing to the right plugin.\n\n
\n
Architect allows you to be as specific as you like about your plugin depth. You can write your modules on a very small 'class level', or very high on a feature level. Depending on the size of your application. But the most important lesson:\n
This is great because you can swap packages the same way you would swap implementations of an interface. Because type constraints are implicit, it's also not required to expose the actual types to the outside world. \n\n
Cloud9 IDE is not one single product, we actually have some different flavours:\nDepending on where we run, we load different plugins or even *swap* complete sets of functionality. This happens for example to the filesystem. The consuming plugins will always have a standard interface that they consume:\n
\n
\n
But that's possible with any Dependency Injection framework. Within one Cloud9 process however, multiple IDE's can live, some running FTP, some running SSH; we can spawn an architect application from within another architect application.\n\n
And this has even more advantages than you would think at first glance. Let's presume we are creating a multiplayer online game, but there are multiple games running at the same time.\n\n
Your code will be cluttered with code depending on 'what is the game we're dealing with at this very moment'.\n
With architect we can create multiple instances of the same plugin, each running completely independent of eachother in a different context in the same process. So still manageble, and no process overhead.\n\nWhen someone starts a new game, we can create a new architect application.\n
The code in the plugin will **never** have to deal with context switching, checking which game it runs, etc. Because **all** code will run in a seperate per-game context.\n
\n
So here is something else that you can build, a centralized eventbus in your application, that ALL plugins can use to send / receive data without creating hard dependencies.\n
\n
\n
All plugins can now hook into this by referencing the eventbus.\n
\n
And if you want something inter-process, replace the `eventbus` plugin by something like Redis PubSub. Your plugins will never notice!\n
\n
\n
\n
\n
\n
You can find Cloud9's source on GitHub, at github.com/ajaxorg/cloud9, including all libraries like VFS under /c9 or /ajaxorg. The editor is under ajaxorg/ace.\n