Woven is built with Dart + Polymer

David Notik
10 min readApr 28, 2015

--

A few years back, I was taking some time, together with our small, distributed team, to review modern approaches to web app development. For years, Woven had been working with Drupal and the LAMP stack, building online community websites for our clients. But now we were setting out to build our own web app, and I knew we needed a new stack.

So we made a rough list of what we were looking for (things like performant, scalable, future-leaning, moderate learning curve, mature ecosystem, great docs, solid templating solution, and more). We listed many of the modern tools and frameworks we knew of (Python/Django, RoR, Scala, GWT, Go, Node.js, Dart). We set about reading and experimenting and discussing.

Not Node

Soon we were focused on Node.js. We already knew it was a serious contender and knew about its appeal: JS on the client and server, non-blocking event-driven I/O, a focus on modern, performant, realtime apps. So we explored Node frameworks like Express, Derby and Meteor. Derby in particular looked really promising. But we found the Node ecosystem to be more of a maze than felt acceptable to us: little documentation, lackluster support in the forums, half-baked frameworks each with their own lingo and syntax, the regular confusing patchwork of JS libraries. While Node was all the rage, I was not especially enthusiastic after actually digging in.

Dart

I had been tracking Dart, and so I dug in there too. First of all, it was similar to Node in concept with its events and asynchrony, and that felt right to me. I appreciated its stated aim of bringing sanity and structure to modern web app engineering. I liked that its creators had worked on the V8 engine, which powered Node. And that its corporate backer had created some of the most scalable and performant web apps in the world. Speaking of Google, I, perhaps paradoxically, appreciated how Dart appeared to live wholly on its own, unlike GWT which lost me at its very name.

I liked how much of what are widely accepted as JS’s quirks were being addressed in the brand new but grounded Dart language. I was intrigued by the promise of “batteries included”, with Dart bundling a strong set of core libraries for both client and server, a package manager, an IDE with a great analyzer, deployment tools and more. The class-based, OO language resonated with me — long before LAMP, I had started with Visual Basic. It seemed like there was a concerted effort with respect to docs, and that someone from the Dart team was always ready to answer a question.

I wish Dart would use this as their logo.

It felt like Dart was the future — a better Node.js. There was hardly an ecosystem, nary an exciting example of its real world use, and it was well short of stable. But from the beginning, we felt like we could keep moving with it, and so we did. My small team changed as we wrapped some final client projects, and I soon hired from within the Dart community — a talented Finland-based developer who had been providing excellent answers on SO, and who remains a friend and advisor even as he moved to a company in his own part of the world after a long and learning-filled year together. Our first line of code soon became 50K+ lines, client and server, leveraging the pre-cursor to Polymer called WebUI on the client. As far as I knew, we had the most comprehensive pure-Dart app, and the first in production.

There were challenges. Dart, and particularly WebUI, would often break from release to release. WebUI and its SPA approach meant our app’s size and thus initial load time grew. Our app had horrid SEO as it was all JS-rendered client side — not an issue specific to Dart, but nonetheless a painful lesson I wish I’d learned sooner as ours was not a closed Gmail-like app but one that could have really benefited from strong SEO. And the disconnect from the JS world can make life more difficult, for example when encountering a service with libraries available for JS but nothing for Dart.

Some screenshots of an older variation of Woven. It aggregated content from social media and the web related to a given community, topic, campaign or cause. Our Dart server was constantly crawling blogs, Meetup, Twitter, Instagram and more. Our datastore was MongoDB, then PostgreSQL. Here’s our pubspec.yaml at the time.

Polymer

Of course, there’s technology and then there’s building the right product. I won’t go into that side of the journey here, but at a certain point I had the chance to taper back and refactor. WebUI was being deprecated in favor of Polymer, but before I stayed that course, I surveyed the short list of Dart frameworks. AngularDart was taking shape and seemed to be the web framework for Dart. I never was attracted to its ng-ways, but I gave it a shot, reaffirmed my distaste for it and ultimately decided to stick to rolling our own with pure Polymer. It felt cleaner, and I really liked how its various parts were upcoming web standards: Web Components, HTML Imports, <template> and more. Further, we had gone that route to date, and there was a clear path to upgrading and reusing our WebUI code, which I did often at the beginning.

Polymer.

I just love being able to break my app into components (which Polymer calls elements) that are essentially encapsulated sets of HTML, JS and CSS consumed as a simple HTML tag, say <chat-list>. I have a folder for each element, and each has a .html, .dart (that gets compiled to JS in the end) and .css. One benefit of encapsulation a la Shadow DOM is that my CSS doesn’t bleed between elements unless I want it to. It may seem trivial, but that alone has been a breath of fresh air for me.

Any Polymer element can publish its properties so that it can be set via attributes like <user-photo user=“dave”>. Any property, published or not, can be @observable, so that your code can observe and reflect its changes. It’s wholly different than Facebook React’s model where components are immutable and simply react to state changes. It lends itself more to a structured OO approach, but it has its issues. For example there is a timing to things that can be tough to decipher. I won’t get into it, but the solution has often been surrounding with a <template if=“{{condition}}”> to ensure that the element is only attached when we have what we expect.

I love the Polymer core-elements (there are some useful paper-elements too) — these are a great set of components that are easily consumed, extended and styled (if they expose a UI, which many do not). Elements like core-drawer-panel for an animated sidebar panel, core-a11y-keys for easily binding keyboard events to functions, or core-animation for declaratively leveraging the Web Animations standard.

A note about React

If I were starting fresh today there’s high chance I would go with React. There’s its vibrant community versus Dart’s or even Polymer’s. But more important (and specific to Polymer) is that components can be rendered server or client side (big for initial load and SEO), its Virtual DOM is highly performant (which really makes a difference on mobile), and its simple state changes versus observables can make it easier to reason about. It’s all very attractive indeed.

Each has its view of the world, and each has its merits as I see it. I’m not versed enough nor do I care to deliver a full-on tit for tat, and I think options are great. As an aside, there is in fact a Dart wrapper for React.js, a comprehensive example of which was recently shared. As another aside, React’s used by Facebook and its own Instagram, and Dart’s used by… Google Ads? Not exciting, but I digress.

It’s only getting better

With respect to performance, Polymer 0.8 is a re-write set to deliver big performance gains and a leaner payload. Dart is working on a way to build performant native apps with its Sky and Fletch projects, a la React Native. The debugging story across all browsers is set to become much easier. Dart the language recently got async/await, and the new test package looks great. I’m riding a lot of momentum using Dart + Polymer.

Woot, woot.

Firebase

Then there’s the datastore. I’ve got so much love for Firebase. After MongoDB and then pgsql failed to cure our performance woes, it became abundantly clear it was largely to do with a fundamental approach to saving and retrieving data. I was searching for something that would take the headache away so I could focus on the important stuff, and I looked into Parse, Firebase and Google Datastore, as all claimed to offer a more linear, and thus highly efficient, approach to saving and retrieving data.

I believe all enforce the same basic concept in that you architect your data such that you are really just pulling down one-dimensional streams of data, as opposed to having to do any complex, inefficient joins or the like. Firebase was to the point, had great docs, let me get started saving and retrieving data without any server-side, and I was happy to find a Dart wrapper for Firebase’s JS API. There was a point where that library wasn’t keeping up with its JS equivalent, and it caused me some frustration and made me feel isolated in Dart-land, but soon someone on the Dart team graciously updated it and more recently someone in the community has brought it in line with Firebase’s latest goodies.

Realtime data goodness.

So Firebase has this concept of denormalize all the data: you repeat data because disk space is cheap and time is not, and you literally update an item in multiple places once you’ve gotten to that point. I save to items/<item> but also items_by_community/community/<item> because the latter gives me a direct and efficient way to get at items for a given community while the former gives the same for an individual item even if I don’t know its community. Firebase has some updates that abstract that away even further, with orderBy* functions that can efficiently sort based on a child’s value, say item/<item>/community, which I think eliminates the need for many cases where you might otherwise duplicate your data.

These days I have a lot of Firebase communication happening on my server-side with its REST API, especially saving data. Retrieval is still client side, as Firebase has these awesome event callbacks like onChildAdded, onChildChanged, onChildMoved, and there are a lot of connection and caching optimizations — Firebase saved me from having to create all of that myself, and basically eliminated database-related DevOps giving me more time to focus on building something that matters to people.

Put it all together

Here’s our pubspec at the time of this writing.

Dart is an awesome language for client and server. It’s a strong, well-documented set of client and server libraries. It’s a set of tools including a package manager, compiler, analyzer and more.

Polymer is a way to leverage Web Components and the idea of encapsulated elements today. It makes it easier to build single page web apps with clean separation of model (.dart) and view (.html/.css). It is also a set of powerful custom elements for the kinds of things web apps need often.

Firebase is a realtime datastore. Data flying to and fro, and it rocks.

I use WebStorm as my IDE, which has excellent Dart support, and I suggest you do too.

WebStorm from JetBrains… built on the IntelliJ IDEA platform. It has a lot of names.

I host on Google Compute Engine (GCE). Spin up a new Debian instance, install the latest SDK, start a new screen and run your Dart server in the background there with dart your_server.dart &. If you Ctrl+A and Ctrl+D out of the screen it’ll stay alive in the background, which is plenty reliable to start, then screen -rx to re-attach to it. git push and pull and you’re going. There’s also Dart support on App Engine when you want sheer auto-scalability though that is a much more involved proposition.

What we’re building

Companies have great communication and collaboration tools, but what about communities? Woven channels combine chat with crowdsourced feeds of content, so we can talk in the same place we share and discover the best events, news and other resources related to our favorite communities.

Woven’s mission is to empower people everywhere with the tools to coordinate our actions to improve the world.

I will leave it there for our purposes, but I invite you to be among the first to check it out at http://woven.co. When you sign up using invitation code MEDIUM, you’ll have instant access. Perhaps we’ll meet in the new dartlang channel, and I invite you to help shape Woven itself in the woven channel. See you around!

I’m David Notik, founder of Woven and co-founder of a little man. I’ll have more to share soon at @davenotik and @wovenco on Twitter. And you can always find me at @dave on Woven. If you dug this, please hit Recommend below.

--

--