Using PostgreSQL in modern enterprise web applications
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Rogelstad, John
Using HTML5, JavaScript, NodeJS with PostgreSQL PostgreSQL's object relational heritage makes it an outstanding choice for developing web applications that have a rich object model domain. See how PostgreSQL's object relational features allow for building database models to support a NodeJS data service feeding a 100% JavaScript web client. PostgreSQL's object relational heritage makes it an outstanding choice for developing web applications that have a rich object model domain. See how PostgreSQL's object relational features allow for building database models to support a NodeJS data service feeding a 100% JavaScript web client. Topics include: The benefits of a rich domain model in enterprise software, Comparing different architecture approaches: scripted model, table model and domain model. The dissonance between relational databases and the object model. Why NoSQL database's seem attractive Why relational is still the best choice for enterprise applications What is an "object relational" database Using Postgres' object-relational features to reduce the friction Compound Types Querying an object hierarchy Object relational views Exploring an example Define a rich domain in a JavaScript MVC web client Build a data source using NodeJS using PostgreSQL JavaScript drivers Defining models in Postgres Extending models in Postgres Yes, it's got a Hemi Use Google's plV8js language in PostresSQL to run JavaScript directly in the database Parse and process JSON payloads
 I hope you're here to attend the modern enterprise web application. We have an open source ERP company. We're about 10 years old. We're a profitable growing company. We have about 300 commercial users. We've got an open source project called Post Books. We think there's about 30,000 folks using that. Right now, our product is two tier. We've used Postgres exclusively since the inception of the company. That's the back end. And then we've got kind of your classic desktop client front end built with QT. So users have to install a client to get that to work. You can do some extension with it currently with JavaScript. So that's just a real brief about who we are. We're working on basically accounting systems and things like that. So what I wanted to do is describe to you kind of this journey we've been through, looking at new technologies as they've been coming out. I wanted to describe the current architectural processes that we're at. And just pass along experiences we've had with new JavaScript tools. These could give you ideas for your own framework. And if you're interested in an ERP style platform, you'd be interested in getting involved in our community. I'm not here to say that this is the right way or the only way to use these tools. It's just solutions that have worked for or seem to be working for us. And we don't think our problems are unique. So hopefully you'll find some value in them. So as I said at the beginning, we make a C++ based client. And we've resisted going to a web architecture for a long time because if you've ever worked with accounting ERP type software, it's a struggle to do it on the web because these are transaction intensive applications. People expect them to be very snappy and have a traditional desktop experience. So we've waited a long time to look at this. But with the advent of HTML5, we thought that web technology is finally getting to a place where we can provide a user experience that's equal to or better than a traditional, rich desktop client. So that means that we kind of get to start from a clean slate. And starting from a clean slate, we ask ourselves first, well, what can we do better than what we're doing now? So on the left is a diagram with what we're going to call our classic client. It's the C++ one built with QT talks directly to Postgres. With the web client, we're having to basically force to go to a three tier architecture where we've got the client and then we need an application and data service that talks to Postgres. Since we're an open source company, the tools all have to be open source. And of course, the whole point of this is the web client has to perform as well or better than the native application that we have today. So just did a little rereading on this topic. Martin Fowler has a great book on this called, Patterns of Enterprise Application Architecture. And I'm pulling out a little bit of it that I thought was useful for us in sort of framing our thoughts about this change. He describes three different approaches to building large enterprise style applications. The first is a scripted model. Another one called actually it's table module. I have table model up there and then a rich domain model. So transaction script model, just about everything that happens in the application is just some kind of base straight SQL call or SQL transaction. Just a little example there. We have a couple of functions in our database called, one's called save contact and another's called delete the contact. We've got over a thousand functions like this in our database. The reason this is an attractive option is it's simple to get started in procedural. And when we started the company years ago, this is what we went with because it was easy to get started. That's always important when you're trying to start something new. But there's no notion in this sort of architecture of a namespace or class hierarchy. And what happens is when the application starts to get larger, this sort of thing is really hard to scale. It's challenging to manage thousands of different transaction functions. So for quite a while, I was actually wanting to pursue what's called the table module approach. In the Microsoft world, you hear this a lot is called active record. I think it's popular in the Ruby world too. In this situation, basically the tables become classes. You have one class per table and you have some kind of gateway interface between your application and the database that organizes this. You manipulate the data and what are called record sets. And the nice thing about this is at least if you're starting fresh, it's easy to just spin your whole object hierarchy right off the tables structure in the database. It doesn't really do polymorphism that well and the data field structure can be really limiting. And that was the struggle I had because we have this legacy application. It didn't really turn out that the table, the relationship between the tables and our business objects were one to one. And trying to force it into that arrangement just never quite worked out. But when we decided to get on this new path, we had to start making some decisions. And then I picked up on this rich domain model. So this is a fully object oriented approach. And when you're using the rich domain model, at least from the client's perspective, you've got data and functions methods encapsulated in objects. So anybody who's worked on any kind of object oriented programming is pretty familiar with this. It takes advantage of inheritance. You can have multi-valued attributes. So any given data field might have more information than just the data itself. It might be including required fields and other faults and other behaviors like that. You make a call on a function in a rich domain model and it may call on a whole network of objects to get an answer. It's not just a simple procedural function like the first one I spoke about. So the idea of this is it scales well and things get complicated. And when I say complicated, I'm thinking of an invoice. An invoice is, for us, a header, line items, pricing, customer records and things in it. And then, boy, one of my favorites is taxes. I don't know if you've ever dealt with taxes, but taxes are just maddening. They're terribly complicated. So an object oriented structure offers the hope of handling that in an elegant way. So the tricky thing about the rich domain model is tables and classes are not necessarily the same thing. So you have to have a data mapper. And this is just kind of a, I guess, an abbreviated example of what an object model would look like in an application, an enterprise style application. At the root, we've got sort of a general object. In our case, it's used for handling getters and setters and key value observing. And then there's a record object that inherits from that, which has a lot of similar characteristics to a database table, not exactly. That record management object can do validation and privilege control and so on, handles parent-child relationships. And then there's a, in our case, we have a document object that inherits from that, which represents sort of high-level documents like customers and orders and things. It handles numbering specifically. And remember I said taxes a minute ago? We have a tax class that inherits from all these other things that deals with the tricky business of taxes. And then, finally, that all flows into an invoice object, which itself has many objects, right? Remember I mentioned it has customers, ship to object, invoice line, pricing, money objects, and things like that. So you can see how quickly an object model can get pretty sophisticated. But that, in the case of an enterprise app, that's great because that's what we need. Now, fortunately, I didn't spend too much time building this part of the presentation because it was a lot of it was in the keynote. But I will just make a mention that we spent some time looking at no-SQL databases ourselves. And the main reason is at first it looks attractive because I'm saying we want to go to an object model system, and it's well known that it's kind of tricky business sometimes to get a relational database to work well in an object-oriented environment. And since a lot of these no-SQL databases are basically working with objects, it just seemed like a nice fit. So there's this technical term, probably a fair number of you might have heard, called object relational impedance mismatch that describes what makes it attractive to use, at least from our perspective. I know there were a lot of other reasons mentioned in the keynote. The data mapping, I was just talking about, and I'll talk about more in a minute, actually most of this is about data mapping. That's a lot of hard work. And gee, if you could avoid that hard work, it should be nice. Most relational databases lack object features, such as sort of this notion of classes and the polymorphism and the inheritance. And then, again, data mapping is hard work with relational databases. So for all those reasons, you think maybe no SQL is the way to go. Well, in our case, we, after digging into this a bit, we finally determined, no, we're fine with the relational database, at least for our application. One of the things I heard in an OSBC conference last year is that there's a couple people talked about three big drivers. OSBC is the open source business conference. Three big drivers behind no SQL databases are a problem of velocity, volume, and variability. Velocity being, they just, these are, these are the databases where they just get huge volumes of data, like videos. Think about YouTube, getting a large amount of videos. Variability is the problem where the data is, you don't know exactly what the structure is going to be, so they want to be able to just throw any kind of structure in the database that will. And then, yeah, and velocity is somewhat of a volume. It's just coming really fast. Well, those aren't problems that we have to deal with so much. Our big problems are having to do more so with output, query output. So in an enterprise application, the data structures are fairly predictable. We know what a contact is, we know what a customer is, we know what a sales order is. So it's no problem for us to define these things ahead of time. But we don't know is how people want to report on these things. I mean, every company's got their own way that they want to see the data. So it's more important to us that we be able to query across tables in a variety of ways and that that be easy. I don't know how many of you have played with trying to query across nodes and hierarchical models, but it's difficult. It's not something you've got to put a lot of work into that. And if you're working with a really big app that's changing all the time with customers always wanting new ways of looking at things, it's not attractive at all. Also, I like this term that CJDate came up with in one of his writings that relational databases are application neutral. And this is really the clincher for us. We have this notion of contacts in our database, right? And they're highly normalized. They're all over the application. Contacts belong to customers and vendors and accounts and ship to addresses. So we ask ourselves this question, well, did the contacts belong to an account? An account is like an organization in our application. Or do the accounts belong to contacts? Well, if you're dealing with an object-oriented structure or database, you have to choose. And that's a real problem for us because, as I said a minute ago, people are always wanting to see the data in different ways. And we don't want to choose. We want to be able to see the data both ways where the contacts are members of accounts and accounts are members of contacts. And of course, the relational theory solves that problem because they're related. They're not necessarily parents or children of each other. So one thing that's really neat about the fact that we just happen to be using Postgres already is the fact that it's relational, but it already has some object-oriented features. It's had these for a very long time. Actually from the beginning, really. It supports custom data types, object inheritance, and the query language and the schema support these things. Because it does these things, this greatly simplifies our mapping problem. And I'll show you why. How many of you have worked with compound types? Anybody? Really? Just a couple? Okay, this is, I think, one of the greatest features about Postgres, and it's really made a huge difference in our, the way we're using the database now. So typically, when you want to select statements like this, right? Select star from, and C and CT is our contact table. Select star from contact, and you just get, you know, basic table, columns and rows. It's what you're used to seeing. And I said we wanted to go to a rich object model, so we want to start thinking of these things as objects. As it turns out, in Postgres, the contact is not just a table, but it is itself a type. So if you do this, select contact from contact, you get all the contacts returned as a single column, and this is Postgres representation of an object here. And as it turns out, if you really dig into it, the notion of a class and type in a database, or a class and object system and type in a database, are more or less the same thing. So this is really helpful. Now here's a select statement joining on another table. I'm selecting contact, first name, last name, and then I'm picking up everything, address, star, everything from an associated address table. So again, this is just traditional columns and rows, and I'm sure you've all worked with data like this, and we're doing a left join, so we get the addresses if they exist. And this is where it really starts to get interesting. If I do that same SQL statement and take off the dot star, what I get is, so I'm just pulling the address as an object, I get my columns for the contact just like I asked for, and I get the address as objects, or types from a Postgres point of view, they're types. And notice in one of the reasons I'm using PGM in here is it does a nice job of showing you, see its address is a type. Now this is interesting, even though they are objects, you can still filter on them. There's a syntax to filter on the types that you've pulled. So in this case, this is how the syntax works. You wrap the table in parentheses and then dot in a field you want to filter on. So this is the same query as before, pulling the contact fields, pulling address as a complete type, and I'm filtering on only the contacts where the address is in Denver. And the syntax is really simple for these types. It's just open, close, parentheses, and then comma separated. Values. You do if you're querying on this kind of syntax. This is specific to the use of compound types, which is what I'm doing. This is compound types. It would not work. Right. Okay, so here, looking at this from another angle, I'm selecting on a too many relationship where I'm trying to get all my contacts and their email addresses, and they may have multiple email addresses. There's actually a contact email table. So you can see here, Mike Johnson, he's got two different email addresses, so I get two records from Mike Johnson. Actually, right here, there's actually more than one Mike Johnson as well. Two Mike Johnson's down here, one for each address. If I change my query and cast this, the select on the email table as a sub query wrapped in this array clause, then what I get is, again, the email, contact email as a type. And notice it's got the double brackets. So what it's doing is it's giving me an array. So where's, let's see, my Mike Johnson's right here. So you can see I've got both those email address records wrapped in these curly braces, which is how Postgres represents an array. So this is all pretty good news for us because this starts to get us in a direction that can help us get to the rich object domain we want. Oh, and this is just one more slide that shows these last two things combined. Now I'm asking for contact first name, last name, the entire address type, and then the contact email as a too many relationship. So great. We've learned something new that we can do with Postgres, and we're going back to visit our real world use case. So we've got this classic client on the left, and we want to go to this three-tier architecture on the right. Now we made a decision to go with a JavaScript architecture. Specifically, we're using a framework for our client called Blossom, which is brand new, brand spanking new. It's actually a fork of Sproutcore. I don't know if you've all heard of Sproutcore, but it's a rich client framework that was developed by Apple. And it's spun off now as an open source project. So Blossom is sprout for with a special view layer. I'll show you that a little bit near the end. In the middle, we've chosen Node.js as a tool to build our application and data service that we need. And then we've got some special JavaScript and Postgres I'll be showing you. And you're probably asking yourself, at least some of you, I know are asking yourselves, are crossing arms and saying, why JavaScript? I'll tell you, I work in a C++ shop. JavaScript was not welcome as a first language for our application, at least at first. One thing that was helpful is QTT allows us to extend our current app with JavaScript, so at least we had some experience with it, which open minds a little bit. But the big driver here is we're trying to make a web app, and JavaScript is the language of the web. All the browsers support it natively. And if you're not paying attention, there is a lot of energy going into JavaScript toolkits now. They're becoming very mature and they're very powerful. And one of the most important things about it is it allows us to do a lot of the processing, the presentation processing on the client side instead of the server generating HTML and processing all these requests. And as you recall at the beginning, I said JavaScript, client side performance is critical for an application like ours. And as you can tell, you're all staring at the graph here, it happens to be the number one language used on GitHub, and notice it's a pretty good margin. There's a lot of work being done with JavaScript. One objection is, yeah, you're trying to build a rich domain, but JavaScript doesn't really have classes, does it? I picked up these quotes from Douglas Crawford in his little essay on JavaScript, the world's most misunderstood language. I'm like, I guess I'll read it to you. I don't need to. But basically, it has the characteristics we need to build an object-oriented environment. I mean, it has objects with data and methods. They can contain other objects. It has constructors and prototype-oriented inheritance. So as it turns out, even though it doesn't have objects in the classic sort of C++ definition that we're used to, you can get the behaviors we need to build the domain we want to build. So a lot of talk about JSON these days, right? Actually, Douglas Crawford spec that out. I didn't know, but it's the de facto format if you're working with JavaScript because JSON is JavaScript object notation. So a minute ago, we were just looking at a contact record, right? Well, here's one of those contact records represented in JSON. You got the contact at the top and then an address indented beneath it with the curly braces. And an email is in some of the email addresses are inside an array here that's bracket by the two square braces. And here's our two addresses. So this is the format we really need to work with if we're going to be using a JavaScript environment. Now, the Blossom data source that we're working with, and when I say Blossom data source, the Blossom client has a data store that's built into it. And that's one of the reasons we chose it. I was actually developed for SproutCorp, but it's very, very powerful. So it has this data store and it expects to talk to a data source, a service, a data service somewhere with an API that looks like this. And there's actually not a lot to it. It's surprisingly simple. There is more to it than I'm showing you, but these are the main things. You need to be able to retrieve a record where you ask for the type of record you want. Is it a contact? Is it a customer? What is it? And a specific ID, some kind of primary key. And then we also need to be able to commit a changed record as a unit of work, where again, you pass, this is the type of record it is, and you pass a data hash with all the pieces of the object. In the case of that invoice, it would include all the objects that are in that invoice. And then we also need to do this fetch call, which is similar to a query where you want to get a result set with a whole bunch of, potentially a whole bunch of records. Again, pass a type, and in this case, you also pass some conditions and parameters and you get that result set. So the question is, how do we get this out of Postgres? Because Postgres, you use SQL statements. You don't use an API like this. So first we need a data service. We chose Node.js. It's built on Chrome's JavaScript runtime. It's this again, I just pulled this right off the Node.js.org website. It describes what it is. It's a non-IO blocking web service that's designed for serving up applications and data just like this. The Node in the name Node.js is all about being able to scale this thing up by basically adding more and more instances of it to a server and they all coordinate well. And of course, everything for Node.js is written in JavaScript. One cool thing about Node.js is it's got a whole bunch of nice packages with really cool stuff, including a Postgres driver that we're using. There's OpenSSL and WebSockets, VDDs, behavioral driven design testing, and these are just a few examples. There are dozens and dozens, actually I think hundreds of packages already written for Node.js. So there's lots of tools already. So you don't have to build everything from scratch. A lot of things are already built. So we took a lot of those tools and put together a web service. It's super high performance and as I said before, it's designed for distributed cloud apps. It's got this non-blocking I.O. which means that it runs through the scripts and it doesn't necessarily wait for an answer. For example, if it makes a SQL call, it doesn't, if you're waiting, if you're expecting an answer to some particular call for value in a SQL statement, it just keeps running through the whole script even without getting that answer. You have to write your app, or your, yeah, you have to write your applications in Node.js in a way that it responds to the information when it gets it. So you might ask a question and then you have to put a listener out to wait for the response and process it when you get the answer. It means it's super fast, but it's kind of hard to write procedural type code, which we still need some of that. In our first prototype, we tried to use Node.js to do an ORM mapper and it was just way, way too difficult. Node was really good at some things, but this was not one of them. So we decided to just make Node a gateway for data. And we decided to take a look at how we could actually get this stuff out of Postgres directly, taking advantage of the object-oriented attributes that Postgres already has. So we found PLV8 hosted on Google Code. It was written by Hitoshi Hirata who's in here somewhere. There he is in the back. So this is the first thing I've met him. It's kind of cool. You all get to see me meeting the author of this cool thing we're using. Oh, okay, it's up there. Now I know you said it was coming and that's what it says over here, soon to be in PGXN. I guess it's already there. So he's principal software at Green Plum and he's going to be doing a presentation here right after mine so you can hear more from him then. So here's how some of our work in Postgres using the V8 engine works. What you get is when you install PLV8, every connection to Postgres can get its own instance of the Google V8 engine. And you can write functions that look something like this. So this function here, I'm calling example, returns text. Now this is an important thing to note that I hadn't been thinking too much about while I started here in the chatter here today. All the responses we get are text. They're not actually JSON objects with the new JSON type that's being worked on. But for what we're doing, the text is fine. So this function returns text. I just created a simple, or actually this is that exact SQL statement I did before where it joined on address and picked up the emails. Create a SQL statement, we execute it, and then we use the JSON stringify function call to turn it into a text. And I actually use a couple pretty print arguments so it looks nice so I can show you what the output looks like. Now I hate having to deviate from the PowerPoint, but it's just really hard to show this without actually running it. Plus I kind of want you to see that I'm really doing it. It's not just smoke and mirrors. So this is what the output looks like. As I said, it's a text string, but it's formatted like JSON objects. In this particular case, it's an array of JSON objects. You see the open square braces at the top. This is awesome. I mean, I don't know if you picked up what I just did, but I just took a basic SQL statement using built-in Postgres object functionality, and PLV8 spit it out in exactly the format I needed it. This is a huge, huge time saver for data mapping. You see the contact on top here. We got an address, and then there's an email. Here's the next contact address and email, and so on. So it's almost just like free work. I love it. So now we've got a way to get objects in the format we need them out of Postgres. So the next question is great. We can write these queries. We can get them in the format we need them. Where do we put them? We're going to have to build this. There's still maps, right? We still have to do the data mapping I talked about at the beginning. So where do we put them? How do we define them? How do we access them with that API I showed you earlier? Well, what we actually are doing here is we're saving those queries in the database with views that have CRUD rules. And the really nice thing about storing them is views is they're cataloged and validated, and they're easy to access from any SQL console. You can just run select star from these views and see what's going on with those things. We define them with adjacent mapping structure. It maps the tables and relationships in certain rules, like required and default values. They can be extended and they're stored in a database registry. And here's what one looks like. This is our contact mapping. So at the top it's got a context of ex-duple. The namespace is like our class namespace. And then the type, which is the actual class itself, is contact, and it maps to the contact table. It's got some other stuff that's special to our app. Here's where I define privileges. This is a really big deal for us. We have some complicated security enforcement that just does not map directly to tables at all. But we can map it here. And this is the guts of it, really. You've got a number of properties. So I can map for each property on the JavaScript object that I'm looking for to a table name. So here's, say, let's pick something obvious. The first name and the contact, it's going to be a string and it's going to map to the field contact first name. What gets really interesting is down below here, there's some two-one relationships. So these are ones where I want to actually call some other data type as an object. So the sequel will be written like I showed you earlier using that table as a type to get me that thing as an object. So that one is defined as a two-one. And then there's also some of them. Here's the email one. Too many. And what happens is when you load these JSON definitions through some of our function calls, it actually creates the views. All right, I got to, does it belong here? So then the third question is how do we access the data? So in PLV8, we created a whole set of functions in a class called XTORM where I can install those JSON files. I have the one I just showed you. It creates the views I was talking about. And then there's a fetch function so that I can inspect the properties of an object relational map. And then I've got a data layer that mirrors that client API I was talking about. So I can make a request to retrieve record, commit record, and fetch just like the client asked for. And what happens is Node just passes these things through to Postgres. Here's what that looks like in action. Again, I'm going to run it over here. The same thing that was in the presentation. And what this is, is this is our retrieve record call. Notice it's being called basically with a regular SQL statement. And this is the output. So that's the full output of my contact object based on the ORM that I just, the map that I just showed you. Here's the address. Here's the emails. You can do, that fetch statement is, that I was talking about earlier is, sorry about that. That fetch statement I was talking about earlier is, gives you kind of a no SQL style of querying. And this is our interface to Postgres to get that kind of querying. So I'm calling a function called xtFetch. I pass it the record type, some parameters, conditions, rollimits and whatnot, some other options. And I know I'm going kind of fast here. Like the presenter said, we'll have this posted up on the web so you guys can really study it a little more closely. Oops. I'll delete it. I just want to run it. Okay. So this is my fetch statement where I said I wanted first name equal to Jake or last name starting with F. And I get a big old result set. Again, it's an array. It starts with the braces. And I get several contacts that match the result. So this is really nice. Now I have an interface that looks like something JavaScript can work with because, well, it's written with JavaScript on the Postgres side of things. And then there's this really cool awesome bonus, which is that we can take those JSON files that I just showed you. We create these JSON RIMs for each of the objects that we want to set up. And we have some in this cruncher that pushes them into Postgres and makes the views. And we also have a function that will generate the models for the client side to match. So that's a huge time saver. One definition does both the client and the server side. We're real excited about that. And then finally, there's this question about server side procedures. Sometimes, you know, just a regular basic object isn't enough. So we want to do something really complicated or specific. Like we want to check the database to see if there's any records that already have this number. Or we want to do some crazy tax calculation that can really only be done on the server side. Or we want to post an invoice, which is a huge multi-step transaction. It has to be one of these asset-compliant kind of transactions. So we've got this function called dispatch, which is implemented to do class-specific function calls. And the way that works is we have what we're calling dispatchable classes. We created a function called xt install JavaScript. It takes a namespace, a class, a context, and then some kind of JavaScript. Here what I'm doing is we're defining an address object. We're saying that object is dispatchable, which means the client can call it directly. And then in this particular case, the function is I want a use count, which is how many places is this address being used in the system? And remember I said we have 1200 functions in the database? Well, we don't really have time to rewrite all those right now. So what we can do is just wrap those things up. We already had an address use count function using PL SQL. We just wrap it in here. And voila, we got a JavaScript exposure of the results of that function. So if I run it... Is this the right one? No. Sorry. Should be four. There it is. How many addresses are in the system? Two. That's the output of that function. And you see how it's called. The type of a call is dispatch. I pass in a class name. I pass in the function I want to call and any parameters, and I get the result. And the real thing about this is it works. So this is architecture. And I have a client running. So we've got a couple... A start on a couple of the pieces of our product. One is the CRM part, and the other is the billing part. You can jump into CRM. So what's interesting about this is if you notice this is running in a browser. This is in Chrome. It's kind of fun to talk to you guys because you're actually hopefully interested in some of the stuff I was just talking about. I suspect no one's going to care about that. They're all going to care about this. This is an application written in the browser that uses the HTML5 canvas, so there's no DOM. This is all being rendered with your PC's graphic, or in my case the Mac's graphic engine. So I can see all these different objects we have. Incidents are like our bug tracker. Accounts are kind of like companies. And here's our contacts. You can scroll through them. And the neat thing about all this is it feels like a native app, even though it's in a browser. And you see it's pretty snappy too. Again, that was one of our important things. It needs to be snappy. HTML5 lets us make a snappy app in a browser. And this also is set up to work on tablets. So we're going to be... You're actually the first ones to ever hear about this outside of ExTupel. We're going to be making a more splashy announcement next week at the Open Source Business Conference in San Francisco. So you might hear more about this. We're calling it NoHTML Web App. So that's the app. And we just made the code public yesterday. So if you're interested in what I'm talking about here, you can go to GitHub. And everything that I was just showing you is in the database repository, ExTupel database. And then the other part is the data source is the node part. You can look at that. And the client is the web app I was just showing you. And then the build tools are how the client gets put together. And then here's links to the other projects. This is probably out of date already, right, Hitoshi? Hitoshi, this is on. Yeah, that's a fast-moving project. The API kept changing on us. We're like, wait a minute. Oh, it's changed again. But it is pretty stable. It's been really stable and solved for us. We're very happy with it. It's just a great project. Node is here. And if you're sitting Blossom, it's here. I think you'll probably be hearing more about Blossom in the next few months. We're the first company building an app on Blossom. We sponsored its development. So that's what you'll probably be hearing a lot about in the next few weeks. That's all I have. And it looks like I even made the short time clock here. I have a couple minutes for questions. And then Hitoshi Yerz is starting now, isn't it? 11. Oh, OK. We got lots of time then. So any questions that I can or can't answer? So when we're selecting the entire table, so when we say select contact and the address, is it possible to only select some of the fields out of that address and still have it at the corners of type? Yes. If you make a type that is a subset. So that's a great question. What we actually have is we've got a set of these objects or views that are the full record. And then we have another set that's sort of a partial view. So there's XM address and then the partial view is XM address info. And that doesn't have everything. And there are lots of cases where you don't want the entire object because it's heavy. It has all the comments and everything about it. And maybe if you just want to browse the lists, you just want to see some of it. Exactly, yes. The info are designed not to have some of the sensitive information. They're less privileged restrictive. The full views are privileged restricted. So you have to have full privileges to see the whole thing. That's a great question. And the client supports lazy loading. So there's two ways you can access an object. One is defining it as a 2-1 regular or 2-1 nested. If you say it's nested, that means Postgres will return the whole object. If you just say 2-1 and it's not nested, it'll just return the key. And in our client, we'll actually ask for the rest of the data when it needs it. So we have a lot of knobs we can tweak there for performance issues if they come up to decide whether to have Postgres do the work or have the client do the work. You know what our question is? What excuse is the biggest pain point she did? Working with this? The fact that nobody's done it before. So there's no one to talk to, no one to ask, nothing on the forums. I hope with this, with PLV-8 going on PGXN now, you all will start looking at this and some collective wisdom and knowledge we're building up. Maybe we'll get some feedback. We've got a big working example project now out in public that people can look at and critique and please be kind. I'm sure you'll find some people will find things and go, what are you doing? So that's the main thing. It's been just a great thing to work with. Just have to figure it all out on our own. Did you try going on HTML, CSS before going to Canvas or did you go to Canvas? Yes. Our first prototype was with Sproutcore which is HTML based. The trouble with HTML DOM rendering is when you get in all the different browsers, they're all real persnickety about the DOM. They all deal with it a little differently. So it's really hard to make an app that works in all the browsers well. When you're drawing directly to the Canvas, you're drawing pixel by pixel so you're guaranteed to get the same result in every browser. And then the other really big thing was you cannot use the DOM on tablet devices because most of them don't have the horsepower to handle a huge complicated DOM layout like we have. So the beauty of this is that it's designed to work on tablets, works on my iPad. And it's, I don't know if we'll have it ready by our Monday show, but we have all the pieces ready to work on Explorer 10 for Windows 8 and Mozilla. And it works on Safari too. Right. In the Canvas part, is it part of the blossom? Yes. So this is what differentiates blossom from Sproutcore. Blossom is all DOM based and what the author of Blossom did is he ripped the DOM part off and replaced it with the Canvas view layer. So that's what makes blossom blossom. It's Sproutcore with the Canvas view layer. What would be the old problem? Nope. You've got to have the latest and greatest. Like I said, Explorer 10, the latest Chrome. I know this would be a non-starter for a lot of companies doing sort of public customer facing things, but remember we're making an application, right? And it's no big deal for us to tell prospective customers or users, look, you've got to have the latest and greatest browser. So it's not for everybody, but eventually these technologies will be ubiquitous anyway. So in the long run, it won't matter. You mentioned WebSockers. What's your experience with them? WebSockers. WebSockers, yeah. What's your experience with them? So this is an area that I didn't program at all. I didn't write any of the node part, one of my coworkers did. But what I do love about it is it's so forgiving. Our current C++ application, if there's any kind of interruption in the connection, you're done. You're dead. You lose whatever you were doing. It just says log out and start over. So with WebSockers, you can lose the connection and come back, and it'll pick up where it left off. So it's pretty nice. Is it stable? What's that? The document is stable. We're using the stable version of Node and all the packages. It has been stable for us. This is all new though, right? I mean, we aren't even in beta stage. Our goal is to have a beta in the next few weeks. So we'll be doing a lot more testing, load testing, and just general user testing in the next few weeks. But so far, so good. When you talk about the experience, how do you provide their or their area of support? Does your application support it offline? This version is not set up to do that. We know we can, and it's sort of in our long term nice to have plans, but the first thing is for it to get our app reconstructed in this environment. But yeah, absolutely. That's something we were very interested in as offline. When you see any like, if we're searching for a change to the port offline? Well, the nice thing is it ought to be minimal because we're already using this JavaScript API. The idea of blossom slash spout core is the data store can connect to a variety of data sources. So we could just have it basically switch from the live data source to an offline one that runs the SQLite database or whatever, and then just sync it up later. So that's definitely in our sites, but not for a little while. Given that the browser is really a HTML rendering engine, I think that you can probably wait all that. Does blossom have class apps just as Daniel and Client? That's Canvas and not all other regular HTML grins? It already has thrown out all the baggage. Everything in blossom is the kind of thing. Do you see your application kind of browser? Oh, right, yeah. You could theoretically run it in something like WebGap, and one of the original goals was to actually be able to compile it as a native app. I'm not sure if we're going to get to that or pull that off, but theoretically that should work. It's just, again, there's so many things to do and only so much time. But yeah, the guy who wrote blossom really wants to do that. He wants to have native compiled apps too. But at a minimum, it would be really easy to port it into WebGap. And in fact, even on the iPad, if I just save the link to home, it opens up just like an app. It's pretty cool. There's no, it's like it's not in a browser at all. Does the blossom deal with form factors, if I'm running out of my iPad versus my iPad Modder, does it have a different view for them? The different styles and some of the features. Right. So that's largely a design issue. We've tried to design this in a way so it'll fit on a variety of form factors, even a phone. You can use CSS or is there some other descriptive market language that you use that I have in the code, each pixel, in a declarative sense? You can use CSS. But we're mostly trying to cover it by designing things in a modular way so that the form factor is not really relevant, as long as you can get it all in there. So like these tiles, it's kind of pulling a page out of the Windows Playbook. And this is tricky because I don't have all the normal screen space I would have here. Yeah, low resolution. But, yeah, I can't get to the... Well, I can't get to the scroller at the bottom, which gets to your form factor problem. So we're doing work on that area, but the idea is you can scroll the tiles over. And the tiles will try and fit as many on the screen as it can for the device, and then you just scroll through them. Yeah. I see one of the advantages for this, in particular, is you can use a lot of functionality with a lot of that code. For the same functionality, you can have a like, how much it will save in terms of the last code? Well, our current project is a quarter million lines of C++ and probably an equal amount of SQL, and we're at about 150,000 so far with this. I don't know that it's going to save lines of code one for one, but I think the levels of abstraction and then encapsulation is way, way better. So it ought to be much easier to add on to this infrastructure than the current one we have, just because it's object-oriented. So easier, not necessarily less lines. What kind of tools are you using? I'm just using text editors. I mean, I know you can run JavaScript and Eclipse and all that, but most of my guys just use Vim. I use TextMate. Pretty straightforward. How do you guys go on text? So I mentioned in the JS node bit that there's a package called Vows, and actually there's another one called Jasmine, but we're using Vows, that is based on the behavioral, BDD behavioral design, design development, something like that. But anyway, the idea is we're running these data models through Vows. We've built a couple test runs of this, and the idea is we want to run all of our data models through that Vows application. So you basically build the test, and it just goes through and runs, pushes the data through, looks for expected results, and shows you what passed and what failed, and it should be pretty quick. So that runs on the Node.js part. So you can run all of the tests that you have to send to the data. Oh, yeah. Right. And there's ways of writing tests for just the views and everything too here on the client. I haven't got to that yet, but we're fast approaching that point. But it should all be done with Vows. I'm kind of talking about performance. I'm kind of thinking of performing a performance screening meeting for the roster. How flexible and the way that the group of all the data field is based on additional layer for the group on the same aspect of it to pooling by the time the infrastructure is not all along the way that we've been trying to develop these things. Well, what I've liked about this is the, there's that ORM layer built in PLV8 that creates the queries. And what's kind of neat about this is whenever I've had a problem with the queries not working that well, I just go change it in that one, how they're defined in that one place, and they all kind of get the fix. Where in our current system, when we decide we've been doing something wrong, we have to go fix it in a thousand places. So the nice thing is it's centralized code, and it's easy to fix. And then a lot of it, you just have to put careful thought into the design of your ORMs. Like I said, there are different extremes you can go to. Do you want to load everything as one giant object, or just pieces of it, and then lazily load the rest of the objects? These are design decisions you'd have to make depending on what you're trying to do and what kind of performance feedback you're getting when you try to actually run it. Actually, your model is kind of important to centralize your SQL in one place. Yeah, the idea is you don't have to write any SQL, right? The only place there's SQL is in those dispatch functions. And even those, I would like to rewrite them so they just use the ORMs themselves and not SQL statements. Do you have much testing in your CF? Oh, yeah. There's a lot of testing in a CF. How do you differentiate that to like, yes? Oh, it's completely non-translatable. We have to rebuild everything. But I will say this. The thing that blows us away the most is this is way faster than our C++ app. Way faster. Which is strange for us because we've always been kind of C++. Yeah, this is the only way to write a real program, but I have to admit this is faster. It's just going to spur us to finish rebuilding the framework as fast as possible because we think people are probably going to start complaining, why is your older classic app so slow? Is this being different only because a lot of it's done in the client? It's because a lot of it's done in the client. This has a data store, that whole object models in the data store, so it caches a lot more than our current client does. And that saves on a lot of query calls. It's also just hyper, like I said, this thing started at Apple. And there's some pretty smart guys at Apple, as you know. It's pretty highly tuned out of the box. Anybody else? Okay, well, I guess it's about time to switch rooms. Thank you all for coming.
 I hope you're here to attend the modern enterprise web application.  We have an open source ERP company.  We're about 10 years old.  We're a profitable growing company.  We have about 300 commercial users.  We've got an open source project called Post Books.  We think there's about 30,000 folks using that.  Right now, our product is two tier.  We've used Postgres exclusively since the inception of the company.  That's the back end.  And then we've got kind of your classic desktop client front end built with QT.  So users have to install a client to get that to work.  You can do some extension with it currently with JavaScript.  So that's just a real brief about who we are.  We're working on basically accounting systems and things like that.  So what I wanted to do is describe to you kind of this journey we've been through,  looking at new technologies as they've been coming out.  I wanted to describe the current architectural processes that we're at.  And just pass along experiences we've had with new JavaScript tools.  These could give you ideas for your own framework.  And if you're interested in an ERP style platform,  you'd be interested in getting involved in our community.  I'm not here to say that this is the right way or the only way to use these tools.  It's just solutions that have worked for or seem to be working for us.  And we don't think our problems are unique.  So hopefully you'll find some value in them.  So as I said at the beginning, we make a C++ based client.  And we've resisted going to a web architecture for a long time  because if you've ever worked with accounting ERP type software,  it's a struggle to do it on the web because these are transaction intensive applications.  People expect them to be very snappy and have a traditional desktop experience.  So we've waited a long time to look at this.  But with the advent of HTML5,  we thought that web technology is finally getting to a place where we can provide  a user experience that's equal to or better than a traditional, rich desktop client.  So that means that we kind of get to start from a clean slate.  And starting from a clean slate, we ask ourselves first,  well, what can we do better than what we're doing now?  So on the left is a diagram with what we're going to call our classic client.  It's the C++ one built with QT talks directly to Postgres.  With the web client, we're having to basically force to go to a three tier architecture  where we've got the client and then we need an application and data service that talks to Postgres.  Since we're an open source company, the tools all have to be open source.  And of course, the whole point of this is the web client has to perform as well or better  than the native application that we have today.  So just did a little rereading on this topic.  Martin Fowler has a great book on this called,  Patterns of Enterprise Application Architecture.  And I'm pulling out a little bit of it that I thought was useful for us in sort of framing our thoughts about this change.  He describes three different approaches to building large enterprise style applications.  The first is a scripted model.  Another one called actually it's table module.  I have table model up there and then a rich domain model.  So transaction script model, just about everything that happens in the application is just some kind of base straight SQL call  or SQL transaction.  Just a little example there.  We have a couple of functions in our database called, one's called save contact and another's called delete the contact.  We've got over a thousand functions like this in our database.  The reason this is an attractive option is it's simple to get started in procedural.  And when we started the company years ago, this is what we went with because it was easy to get started.  That's always important when you're trying to start something new.  But there's no notion in this sort of architecture of a namespace or class hierarchy.  And what happens is when the application starts to get larger, this sort of thing is really hard to scale.  It's challenging to manage thousands of different transaction functions.  So for quite a while, I was actually wanting to pursue what's called the table module approach.  In the Microsoft world, you hear this a lot is called active record.  I think it's popular in the Ruby world too.  In this situation, basically the tables become classes.  You have one class per table and you have some kind of gateway interface between your application and the database that organizes this.  You manipulate the data and what are called record sets.  And the nice thing about this is at least if you're starting fresh, it's easy to just spin your whole object hierarchy right off the tables structure in the database.  It doesn't really do polymorphism that well and the data field structure can be really limiting.  And that was the struggle I had because we have this legacy application.  It didn't really turn out that the table, the relationship between the tables and our business objects were one to one.  And trying to force it into that arrangement just never quite worked out.  But when we decided to get on this new path, we had to start making some decisions.  And then I picked up on this rich domain model.  So this is a fully object oriented approach.  And when you're using the rich domain model, at least from the client's perspective, you've got data and functions methods encapsulated in objects.  So anybody who's worked on any kind of object oriented programming is pretty familiar with this.  It takes advantage of inheritance.  You can have multi-valued attributes.  So any given data field might have more information than just the data itself.  It might be including required fields and other faults and other behaviors like that.  You make a call on a function in a rich domain model and it may call on a whole network of objects to get an answer.  It's not just a simple procedural function like the first one I spoke about.  So the idea of this is it scales well and things get complicated.  And when I say complicated, I'm thinking of an invoice.  An invoice is, for us, a header, line items, pricing, customer records and things in it.  And then, boy, one of my favorites is taxes. I don't know if you've ever dealt with taxes, but taxes are just maddening.  They're terribly complicated.  So an object oriented structure offers the hope of handling that in an elegant way.  So the tricky thing about the rich domain model is tables and classes are not necessarily the same thing.  So you have to have a data mapper.  And this is just kind of a, I guess, an abbreviated example of what an object model would look like in an application, an enterprise style application.  At the root, we've got sort of a general object.  In our case, it's used for handling getters and setters and key value observing.  And then there's a record object that inherits from that, which has a lot of similar characteristics to a database table,  not exactly. That record management object can do validation and privilege control and so on, handles parent-child relationships.  And then there's a, in our case, we have a document object that inherits from that, which represents sort of high-level documents like customers and orders and things.  It handles numbering specifically.  And remember I said taxes a minute ago?  We have a tax class that inherits from all these other things that deals with the tricky business of taxes.  And then, finally, that all flows into an invoice object, which itself has many objects, right?  Remember I mentioned it has customers, ship to object, invoice line, pricing, money objects, and things like that.  So you can see how quickly an object model can get pretty sophisticated.  But that, in the case of an enterprise app, that's great because that's what we need.  Now, fortunately, I didn't spend too much time building this part of the presentation because it was a lot of it was in the keynote.  But I will just make a mention that we spent some time looking at no-SQL databases ourselves.  And the main reason is at first it looks attractive because I'm saying we want to go to an object model system,  and it's well known that it's kind of tricky business sometimes to get a relational database to work well in an object-oriented environment.  And since a lot of these no-SQL databases are basically working with objects, it just seemed like a nice fit.  So there's this technical term, probably a fair number of you might have heard, called object relational impedance mismatch  that describes what makes it attractive to use, at least from our perspective.  I know there were a lot of other reasons mentioned in the keynote.  The data mapping, I was just talking about, and I'll talk about more in a minute, actually most of this is about data mapping.  That's a lot of hard work.  And gee, if you could avoid that hard work, it should be nice.  Most relational databases lack object features, such as sort of this notion of classes and the polymorphism and the inheritance.  And then, again, data mapping is hard work with relational databases.  So for all those reasons, you think maybe no SQL is the way to go.  Well, in our case, we, after digging into this a bit, we finally determined,  no, we're fine with the relational database, at least for our application.  One of the things I heard in an OSBC conference last year is that there's a couple people talked about three big drivers.  OSBC is the open source business conference.  Three big drivers behind no SQL databases are a problem of velocity, volume, and variability.  Velocity being, they just, these are, these are the databases where they just get huge volumes of data, like videos.  Think about YouTube, getting a large amount of videos.  Variability is the problem where the data is, you don't know exactly what the structure is going to be,  so they want to be able to just throw any kind of structure in the database that will.  And then, yeah, and velocity is somewhat of a volume.  It's just coming really fast.  Well, those aren't problems that we have to deal with so much.  Our big problems are having to do more so with output, query output.  So in an enterprise application, the data structures are fairly predictable.  We know what a contact is, we know what a customer is, we know what a sales order is.  So it's no problem for us to define these things ahead of time.  But we don't know is how people want to report on these things.  I mean, every company's got their own way that they want to see the data.  So it's more important to us that we be able to query across tables in a variety of ways and that that be easy.  I don't know how many of you have played with trying to query across nodes and hierarchical models, but it's difficult.  It's not something you've got to put a lot of work into that.  And if you're working with a really big app that's changing all the time with customers always wanting new ways of looking at things,  it's not attractive at all.  Also, I like this term that CJDate came up with in one of his writings that relational databases are application neutral.  And this is really the clincher for us.  We have this notion of contacts in our database, right?  And they're highly normalized.  They're all over the application.  Contacts belong to customers and vendors and accounts and ship to addresses.  So we ask ourselves this question, well, did the contacts belong to an account?  An account is like an organization in our application.  Or do the accounts belong to contacts?  Well, if you're dealing with an object-oriented structure or database, you have to choose.  And that's a real problem for us because, as I said a minute ago, people are always wanting to see the data in different ways.  And we don't want to choose.  We want to be able to see the data both ways where the contacts are members of accounts and accounts are members of contacts.  And of course, the relational theory solves that problem because they're related.  They're not necessarily parents or children of each other.  So one thing that's really neat about the fact that we just happen to be using Postgres already is the fact that it's relational,  but it already has some object-oriented features.  It's had these for a very long time.  Actually from the beginning, really.  It supports custom data types, object inheritance, and the query language and the schema support these things.  Because it does these things, this greatly simplifies our mapping problem.  And I'll show you why.  How many of you have worked with compound types?  Anybody?  Really?  Just a couple?  Okay, this is, I think, one of the greatest features about Postgres, and it's really made a huge difference in our,  the way we're using the database now.  So typically, when you want to select statements like this, right?  Select star from, and C and CT is our contact table.  Select star from contact, and you just get, you know, basic table, columns and rows.  It's what you're used to seeing.  And I said we wanted to go to a rich object model, so we want to start thinking of these things as objects.  As it turns out, in Postgres, the contact is not just a table, but it is itself a type.  So if you do this, select contact from contact, you get all the contacts returned as a single column,  and this is Postgres representation of an object here.  And as it turns out, if you really dig into it, the notion of a class and type in a database,  or a class and object system and type in a database, are more or less the same thing.  So this is really helpful.  Now here's a select statement joining on another table.  I'm selecting contact, first name, last name, and then I'm picking up everything, address, star,  everything from an associated address table.  So again, this is just traditional columns and rows, and I'm sure you've all worked with data like this,  and we're doing a left join, so we get the addresses if they exist.  And this is where it really starts to get interesting.  If I do that same SQL statement and take off the dot star, what I get is,  so I'm just pulling the address as an object,  I get my columns for the contact just like I asked for, and I get the address as objects,  or types from a Postgres point of view, they're types.  And notice in one of the reasons I'm using PGM in here is it does a nice job of showing you,  see its address is a type.  Now this is interesting, even though they are objects, you can still filter on them.  There's a syntax to filter on the types that you've pulled.  So in this case, this is how the syntax works.  You wrap the table in parentheses and then dot in a field you want to filter on.  So this is the same query as before, pulling the contact fields,  pulling address as a complete type, and I'm filtering on only the contacts where the address is in Denver.  And the syntax is really simple for these types.  It's just open, close, parentheses, and then comma separated.  Values.  You do if you're querying on this kind of syntax.  This is specific to the use of compound types, which is what I'm doing.  This is compound types.  It would not work.  Right.  Okay, so here, looking at this from another angle,  I'm selecting on a too many relationship where I'm trying to get all my contacts and their email addresses,  and they may have multiple email addresses.  There's actually a contact email table.  So you can see here, Mike Johnson, he's got two different email addresses,  so I get two records from Mike Johnson.  Actually, right here, there's actually more than one Mike Johnson as well.  Two Mike Johnson's down here, one for each address.  If I change my query and cast this, the select on the email table as a sub query wrapped in this array clause,  then what I get is, again, the email, contact email as a type.  And notice it's got the double brackets.  So what it's doing is it's giving me an array.  So where's, let's see, my Mike Johnson's right here.  So you can see I've got both those email address records wrapped in these curly braces,  which is how Postgres represents an array.  So this is all pretty good news for us because this starts to get us in a direction  that can help us get to the rich object domain we want.  Oh, and this is just one more slide that shows these last two things combined.  Now I'm asking for contact first name, last name, the entire address type,  and then the contact email as a too many relationship.  So great. We've learned something new that we can do with Postgres,  and we're going back to visit our real world use case.  So we've got this classic client on the left, and we want to go to this three-tier architecture on the right.  Now we made a decision to go with a JavaScript architecture.  Specifically, we're using a framework for our client called Blossom, which is brand new, brand spanking new.  It's actually a fork of Sproutcore.  I don't know if you've all heard of Sproutcore, but it's a rich client framework that was developed by Apple.  And it's spun off now as an open source project.  So Blossom is sprout for with a special view layer.  I'll show you that a little bit near the end.  In the middle, we've chosen Node.js as a tool to build our application and data service that we need.  And then we've got some special JavaScript and Postgres I'll be showing you.  And you're probably asking yourself, at least some of you, I know are asking yourselves, are crossing arms and saying, why JavaScript?  I'll tell you, I work in a C++ shop.  JavaScript was not welcome as a first language for our application, at least at first.  One thing that was helpful is QTT allows us to extend our current app with JavaScript,  so at least we had some experience with it, which open minds a little bit.  But the big driver here is we're trying to make a web app, and JavaScript is the language of the web.  All the browsers support it natively.  And if you're not paying attention, there is a lot of energy going into JavaScript toolkits now.  They're becoming very mature and they're very powerful.  And one of the most important things about it is it allows us to do a lot of the processing,  the presentation processing on the client side instead of the server generating HTML and processing all these requests.  And as you recall at the beginning, I said JavaScript, client side performance is critical for an application like ours.  And as you can tell, you're all staring at the graph here, it happens to be the number one language used on GitHub,  and notice it's a pretty good margin.  There's a lot of work being done with JavaScript.  One objection is, yeah, you're trying to build a rich domain, but JavaScript doesn't really have classes, does it?  I picked up these quotes from Douglas Crawford in his little essay on JavaScript,  the world's most misunderstood language.  I'm like, I guess I'll read it to you.  I don't need to.  But basically, it has the characteristics we need to build an object-oriented environment.  I mean, it has objects with data and methods.  They can contain other objects.  It has constructors and prototype-oriented inheritance.  So as it turns out, even though it doesn't have objects in the classic sort of C++ definition that we're used to,  you can get the behaviors we need to build the domain we want to build.  So a lot of talk about JSON these days, right?  Actually, Douglas Crawford spec that out.  I didn't know, but it's the de facto format if you're working with JavaScript  because JSON is JavaScript object notation.  So a minute ago, we were just looking at a contact record, right?  Well, here's one of those contact records represented in JSON.  You got the contact at the top and then an address indented beneath it with the curly braces.  And an email is in some of the email addresses are inside an array here that's  bracket by the two square braces.  And here's our two addresses.  So this is the format we really need to work with if we're going to be using a JavaScript environment.  Now, the Blossom data source that we're working with, and when I say Blossom data source,  the Blossom client has a data store that's built into it.  And that's one of the reasons we chose it.  I was actually developed for SproutCorp, but it's very, very powerful.  So it has this data store and it expects to talk to a data source, a service,  a data service somewhere with an API that looks like this.  And there's actually not a lot to it.  It's surprisingly simple.  There is more to it than I'm showing you, but these are the main things.  You need to be able to retrieve a record where you ask for the type of record you want.  Is it a contact? Is it a customer? What is it?  And a specific ID, some kind of primary key.  And then we also need to be able to commit a changed record as a unit of work,  where again, you pass, this is the type of record it is, and you pass a data hash with all the pieces of the object.  In the case of that invoice, it would include all the objects that are in that invoice.  And then we also need to do this fetch call, which is similar to a query where you want to get a result set  with a whole bunch of, potentially a whole bunch of records.  Again, pass a type, and in this case, you also pass some conditions and parameters and you get that result set.  So the question is, how do we get this out of Postgres?  Because Postgres, you use SQL statements.  You don't use an API like this.  So first we need a data service.  We chose Node.js.  It's built on Chrome's JavaScript runtime.  It's this again, I just pulled this right off the Node.js.org website.  It describes what it is.  It's a non-IO blocking web service that's designed for serving up applications and data just like this.  The Node in the name Node.js is all about being able to scale this thing up by basically adding more and more instances of it  to a server and they all coordinate well.  And of course, everything for Node.js is written in JavaScript.  One cool thing about Node.js is it's got a whole bunch of nice packages with really cool stuff, including a Postgres driver that we're using.  There's OpenSSL and WebSockets, VDDs, behavioral driven design testing, and these are just a few examples.  There are dozens and dozens, actually I think hundreds of packages already written for Node.js.  So there's lots of tools already.  So you don't have to build everything from scratch.  A lot of things are already built.  So we took a lot of those tools and put together a web service.  It's super high performance and as I said before, it's designed for distributed cloud apps.  It's got this non-blocking I.O. which means that it runs through the scripts and it doesn't necessarily wait for an answer.  For example, if it makes a SQL call, it doesn't, if you're waiting, if you're expecting an answer to some particular call for value in a SQL statement,  it just keeps running through the whole script even without getting that answer.  You have to write your app, or your, yeah, you have to write your applications in Node.js in a way that it responds to the information when it gets it.  So you might ask a question and then you have to put a listener out to wait for the response and process it when you get the answer.  It means it's super fast, but it's kind of hard to write procedural type code, which we still need some of that.  In our first prototype, we tried to use Node.js to do an ORM mapper and it was just way, way too difficult.  Node was really good at some things, but this was not one of them.  So we decided to just make Node a gateway for data.  And we decided to take a look at how we could actually get this stuff out of Postgres directly, taking advantage of the object-oriented attributes that Postgres already has.  So we found PLV8 hosted on Google Code.  It was written by Hitoshi Hirata who's in here somewhere.  There he is in the back.  So this is the first thing I've met him.  It's kind of cool.  You all get to see me meeting the author of this cool thing we're using.  Oh, okay, it's up there.  Now I know you said it was coming and that's what it says over here, soon to be in PGXN.  I guess it's already there.  So he's principal software at Green Plum and he's going to be doing a presentation here right after mine so you can hear more from him then.  So here's how some of our work in Postgres using the V8 engine works.  What you get is when you install PLV8, every connection to Postgres can get its own instance of the Google V8 engine.  And you can write functions that look something like this.  So this function here, I'm calling example, returns text.  Now this is an important thing to note that I hadn't been thinking too much about while I started here in the chatter here today.  All the responses we get are text.  They're not actually JSON objects with the new JSON type that's being worked on.  But for what we're doing, the text is fine.  So this function returns text.  I just created a simple, or actually this is that exact SQL statement I did before where it joined on address and picked up the emails.  Create a SQL statement, we execute it, and then we use the JSON stringify function call to turn it into a text.  And I actually use a couple pretty print arguments so it looks nice so I can show you what the output looks like.  Now I hate having to deviate from the PowerPoint, but it's just really hard to show this without actually running it.  Plus I kind of want you to see that I'm really doing it. It's not just smoke and mirrors.  So this is what the output looks like.  As I said, it's a text string, but it's formatted like JSON objects.  In this particular case, it's an array of JSON objects.  You see the open square braces at the top.  This is awesome.  I mean, I don't know if you picked up what I just did, but I just took a basic SQL statement using built-in Postgres object functionality,  and PLV8 spit it out in exactly the format I needed it.  This is a huge, huge time saver for data mapping.  You see the contact on top here.  We got an address, and then there's an email.  Here's the next contact address and email, and so on.  So it's almost just like free work. I love it.  So now we've got a way to get objects in the format we need them out of Postgres.  So the next question is great. We can write these queries.  We can get them in the format we need them.  Where do we put them?  We're going to have to build this.  There's still maps, right?  We still have to do the data mapping I talked about at the beginning.  So where do we put them? How do we define them?  How do we access them with that API I showed you earlier?  Well, what we actually are doing here is we're saving those queries in the database with views that have CRUD rules.  And the really nice thing about storing them is views is they're cataloged and validated,  and they're easy to access from any SQL console.  You can just run select star from these views and see what's going on with those things.  We define them with adjacent mapping structure.  It maps the tables and relationships in certain rules, like required and default values.  They can be extended and they're stored in a database registry.  And here's what one looks like.  This is our contact mapping.  So at the top it's got a context of ex-duple.  The namespace is like our class namespace.  And then the type, which is the actual class itself, is contact, and it maps to the contact table.  It's got some other stuff that's special to our app.  Here's where I define privileges. This is a really big deal for us.  We have some complicated security enforcement that just does not map directly to tables at all.  But we can map it here. And this is the guts of it, really.  You've got a number of properties.  So I can map for each property on the JavaScript object that I'm looking for to a table name.  So here's, say, let's pick something obvious.  The first name and the contact, it's going to be a string and it's going to map to the field contact first name.  What gets really interesting is down below here, there's some two-one relationships.  So these are ones where I want to actually call some other data type as an object.  So the sequel will be written like I showed you earlier using that table as a type to get me that thing as an object.  So that one is defined as a two-one. And then there's also some of them.  Here's the email one. Too many.  And what happens is when you load these JSON definitions through some of our function calls, it actually creates the views.  All right, I got to, does it belong here?  So then the third question is how do we access the data?  So in PLV8, we created a whole set of functions in a class called XTORM where I can install those JSON files.  I have the one I just showed you. It creates the views I was talking about.  And then there's a fetch function so that I can inspect the properties of an object relational map.  And then I've got a data layer that mirrors that client API I was talking about.  So I can make a request to retrieve record, commit record, and fetch just like the client asked for.  And what happens is Node just passes these things through to Postgres.  Here's what that looks like in action.  Again, I'm going to run it over here. The same thing that was in the presentation.  And what this is, is this is our retrieve record call.  Notice it's being called basically with a regular SQL statement.  And this is the output.  So that's the full output of my contact object based on the ORM that I just, the map that I just showed you.  Here's the address. Here's the emails.  You can do, that fetch statement is, that I was talking about earlier is, sorry about that.  That fetch statement I was talking about earlier is, gives you kind of a no SQL style of querying.  And this is our interface to Postgres to get that kind of querying.  So I'm calling a function called xtFetch.  I pass it the record type, some parameters, conditions, rollimits and whatnot, some other options.  And I know I'm going kind of fast here.  Like the presenter said, we'll have this posted up on the web so you guys can really study it a little more closely.  Oops. I'll delete it. I just want to run it.  Okay. So this is my fetch statement where I said I wanted first name equal to Jake or last name starting with F.  And I get a big old result set.  Again, it's an array. It starts with the braces.  And I get several contacts that match the result.  So this is really nice. Now I have an interface that looks like something JavaScript can work with  because, well, it's written with JavaScript on the Postgres side of things.  And then there's this really cool awesome bonus, which is that we can take those JSON files that I just showed you.  We create these JSON RIMs for each of the objects that we want to set up.  And we have some in this cruncher that pushes them into Postgres and makes the views.  And we also have a function that will generate the models for the client side to match.  So that's a huge time saver. One definition does both the client and the server side.  We're real excited about that.  And then finally, there's this question about server side procedures.  Sometimes, you know, just a regular basic object isn't enough.  So we want to do something really complicated or specific.  Like we want to check the database to see if there's any records that already have this number.  Or we want to do some crazy tax calculation that can really only be done on the server side.  Or we want to post an invoice, which is a huge multi-step transaction.  It has to be one of these asset-compliant kind of transactions.  So we've got this function called dispatch, which is implemented to do class-specific function calls.  And the way that works is we have what we're calling dispatchable classes.  We created a function called xt install JavaScript.  It takes a namespace, a class, a context, and then some kind of JavaScript.  Here what I'm doing is we're defining an address object.  We're saying that object is dispatchable, which means the client can call it directly.  And then in this particular case, the function is I want a use count,  which is how many places is this address being used in the system?  And remember I said we have 1200 functions in the database?  Well, we don't really have time to rewrite all those right now.  So what we can do is just wrap those things up.  We already had an address use count function using PL SQL.  We just wrap it in here.  And voila, we got a JavaScript exposure of the results of that function.  So if I run it...  Is this the right one?  No.  Sorry.  Should be four. There it is.  How many addresses are in the system?  Two.  That's the output of that function.  And you see how it's called.  The type of a call is dispatch.  I pass in a class name.  I pass in the function I want to call and any parameters, and I get the result.  And the real thing about this is it works.  So this is architecture.  And I have a client running.  So we've got a couple...  A start on a couple of the pieces of our product.  One is the CRM part, and the other is the billing part.  You can jump into CRM.  So what's interesting about this is if you notice this is running in a browser.  This is in Chrome.  It's kind of fun to talk to you guys because you're actually hopefully interested in some of the stuff I was just talking about.  I suspect no one's going to care about that.  They're all going to care about this.  This is an application written in the browser that uses the HTML5 canvas, so there's no DOM.  This is all being rendered with your PC's graphic, or in my case the Mac's graphic engine.  So I can see all these different objects we have.  Incidents are like our bug tracker.  Accounts are kind of like companies.  And here's our contacts.  You can scroll through them.  And the neat thing about all this is it feels like a native app, even though it's in a browser.  And you see it's pretty snappy too.  Again, that was one of our important things.  It needs to be snappy.  HTML5 lets us make a snappy app in a browser.  And this also is set up to work on tablets.  So we're going to be...  You're actually the first ones to ever hear about this outside of ExTupel.  We're going to be making a more splashy announcement next week at the Open Source Business Conference in San Francisco.  So you might hear more about this.  We're calling it NoHTML Web App.  So that's the app.  And we just made the code public yesterday.  So if you're interested in what I'm talking about here, you can go to GitHub.  And everything that I was just showing you is in the database repository, ExTupel database.  And then the other part is the data source is the node part.  You can look at that.  And the client is the web app I was just showing you.  And then the build tools are how the client gets put together.  And then here's links to the other projects.  This is probably out of date already, right, Hitoshi?  Hitoshi, this is on.  Yeah, that's a fast-moving project.  The API kept changing on us.  We're like, wait a minute.  Oh, it's changed again.  But it is pretty stable.  It's been really stable and solved for us.  We're very happy with it.  It's just a great project.  Node is here.  And if you're sitting Blossom, it's here.  I think you'll probably be hearing more about Blossom in the next few months.  We're the first company building an app on Blossom.  We sponsored its development.  So that's what you'll probably be hearing a lot about in the next few weeks.  That's all I have.  And it looks like I even made the short time clock here.  I have a couple minutes for questions.  And then Hitoshi Yerz is starting now, isn't it?  11.  Oh, OK.  We got lots of time then.  So any questions that I can or can't answer?  So when we're selecting the entire table, so when we say select contact and the address,  is it possible to only select some of the fields out of that address and still have it at the corners of type?  Yes.  If you make a type that is a subset.  So that's a great question.  What we actually have is we've got a set of these objects or views that are the full record.  And then we have another set that's sort of a partial view.  So there's XM address and then the partial view is XM address info.  And that doesn't have everything.  And there are lots of cases where you don't want the entire object because it's heavy.  It has all the comments and everything about it.  And maybe if you just want to browse the lists, you just want to see some of it.  Exactly, yes.  The info are designed not to have some of the sensitive information.  They're less privileged restrictive.  The full views are privileged restricted.  So you have to have full privileges to see the whole thing.  That's a great question.  And the client supports lazy loading.  So there's two ways you can access an object.  One is defining it as a 2-1 regular or 2-1 nested.  If you say it's nested, that means Postgres will return the whole object.  If you just say 2-1 and it's not nested, it'll just return the key.  And in our client, we'll actually ask for the rest of the data when it needs it.  So we have a lot of knobs we can tweak there for performance issues if they come up  to decide whether to have Postgres do the work or have the client do the work.  You know what our question is?  What excuse is the biggest pain point she did?  Working with this?  The fact that nobody's done it before.  So there's no one to talk to, no one to ask, nothing on the forums.  I hope with this, with PLV-8 going on PGXN now,  you all will start looking at this and some collective wisdom and knowledge we're building up.  Maybe we'll get some feedback. We've got a big working example project now  out in public that people can look at and critique and please be kind.  I'm sure you'll find some people will find things and go, what are you doing?  So that's the main thing.  It's been just a great thing to work with.  Just have to figure it all out on our own.  Did you try going on HTML, CSS before going to Canvas or did you go to Canvas?  Yes. Our first prototype was with Sproutcore which is HTML based.  The trouble with HTML DOM rendering is when you get in all the different browsers,  they're all real persnickety about the DOM.  They all deal with it a little differently.  So it's really hard to make an app that works in all the browsers well.  When you're drawing directly to the Canvas, you're drawing pixel by pixel  so you're guaranteed to get the same result in every browser.  And then the other really big thing was you cannot use the DOM on tablet devices  because most of them don't have the horsepower to handle a huge complicated DOM layout like we have.  So the beauty of this is that it's designed to work on tablets, works on my iPad.  And it's, I don't know if we'll have it ready by our Monday show,  but we have all the pieces ready to work on Explorer 10 for Windows 8 and Mozilla.  And it works on Safari too.  Right.  In the Canvas part, is it part of the blossom?  Yes. So this is what differentiates blossom from Sproutcore.  Blossom is all DOM based and what the author of Blossom did is he ripped the DOM part off  and replaced it with the Canvas view layer.  So that's what makes blossom blossom.  It's Sproutcore with the Canvas view layer.  What would be the old problem?  Nope. You've got to have the latest and greatest.  Like I said, Explorer 10, the latest Chrome.  I know this would be a non-starter for a lot of companies doing sort of public customer facing things,  but remember we're making an application, right?  And it's no big deal for us to tell prospective customers or users,  look, you've got to have the latest and greatest browser.  So it's not for everybody, but eventually these technologies will be ubiquitous anyway.  So in the long run, it won't matter.  You mentioned WebSockers. What's your experience with them?  WebSockers.  WebSockers, yeah.  What's your experience with them?  So this is an area that I didn't program at all.  I didn't write any of the node part, one of my coworkers did.  But what I do love about it is it's so forgiving.  Our current C++ application, if there's any kind of interruption in the connection,  you're done. You're dead. You lose whatever you were doing.  It just says log out and start over.  So with WebSockers, you can lose the connection and come back,  and it'll pick up where it left off.  So it's pretty nice.  Is it stable?  What's that?  The document is stable.  We're using the stable version of Node and all the packages.  It has been stable for us.  This is all new though, right?  I mean, we aren't even in beta stage.  Our goal is to have a beta in the next few weeks.  So we'll be doing a lot more testing, load testing,  and just general user testing in the next few weeks.  But so far, so good.  When you talk about the experience,  how do you provide their or their area of support?  Does your application support it offline?  This version is not set up to do that.  We know we can, and it's sort of in our long term nice to have plans,  but the first thing is for it to get our app reconstructed in this environment.  But yeah, absolutely.  That's something we were very interested in as offline.  When you see any like, if we're searching for a change to the port offline?  Well, the nice thing is it ought to be minimal  because we're already using this JavaScript API.  The idea of blossom slash spout core is the data store  can connect to a variety of data sources.  So we could just have it basically switch from the live data source  to an offline one that runs the SQLite database or whatever,  and then just sync it up later.  So that's definitely in our sites, but not for a little while.  Given that the browser is really a HTML rendering engine,  I think that you can probably wait all that.  Does blossom have class apps just as Daniel and Client?  That's Canvas and not all other regular HTML grins?  It already has thrown out all the baggage.  Everything in blossom is the kind of thing.  Do you see your application kind of browser?  Oh, right, yeah.  You could theoretically run it in something like WebGap,  and one of the original goals was to actually be able to compile it as a native app.  I'm not sure if we're going to get to that or pull that off,  but theoretically that should work.  It's just, again, there's so many things to do and only so much time.  But yeah, the guy who wrote blossom really wants to do that.  He wants to have native compiled apps too.  But at a minimum, it would be really easy to port it into WebGap.  And in fact, even on the iPad, if I just save the link to home,  it opens up just like an app.  It's pretty cool.  There's no, it's like it's not in a browser at all.  Does the blossom deal with form factors,  if I'm running out of my iPad versus my iPad Modder,  does it have a different view for them?  The different styles and some of the features.  Right.  So that's largely a design issue.  We've tried to design this in a way so it'll fit on a variety of form factors,  even a phone.  You can use CSS or is there some other descriptive market language  that you use that I have in the code, each pixel, in a declarative sense?  You can use CSS.  But we're mostly trying to cover it by designing things in a modular way  so that the form factor is not really relevant,  as long as you can get it all in there.  So like these tiles, it's kind of pulling a page out of the Windows Playbook.  And this is tricky because I don't have all the normal screen space I would have here.  Yeah, low resolution.  But, yeah, I can't get to the...  Well, I can't get to the scroller at the bottom,  which gets to your form factor problem.  So we're doing work on that area, but the idea is you can scroll the tiles over.  And the tiles will try and fit as many on the screen as it can for the device,  and then you just scroll through them.  Yeah.  I see one of the advantages for this, in particular,  is you can use a lot of functionality with a lot of that code.  For the same functionality, you can have a like, how much it will save in terms of the last code?  Well, our current project is a quarter million lines of C++  and probably an equal amount of SQL, and we're at about 150,000 so far with this.  I don't know that it's going to save lines of code one for one,  but I think the levels of abstraction and then encapsulation is way, way better.  So it ought to be much easier to add on to this infrastructure than the current one we have,  just because it's object-oriented.  So easier, not necessarily less lines.  What kind of tools are you using?  I'm just using text editors.  I mean, I know you can run JavaScript and Eclipse and all that,  but most of my guys just use Vim.  I use TextMate. Pretty straightforward.  How do you guys go on text?  So I mentioned in the JS node bit that there's a package called Vows,  and actually there's another one called Jasmine, but we're using Vows,  that is based on the behavioral, BDD behavioral design, design development, something like that.  But anyway, the idea is we're running these data models through Vows.  We've built a couple test runs of this,  and the idea is we want to run all of our data models through that Vows application.  So you basically build the test, and it just goes through and runs,  pushes the data through, looks for expected results,  and shows you what passed and what failed, and it should be pretty quick.  So that runs on the Node.js part.  So you can run all of the tests that you have to send to the data.  Oh, yeah. Right.  And there's ways of writing tests for just the views and everything too here on the client.  I haven't got to that yet, but we're fast approaching that point.  But it should all be done with Vows.  I'm kind of talking about performance.  I'm kind of thinking of performing a performance screening meeting for the roster.  How flexible and the way that the group of all the data field  is based on additional layer for the group on the same aspect of it  to pooling by the time the infrastructure is not all along the way  that we've been trying to develop these things.  Well, what I've liked about this is the,  there's that ORM layer built in PLV8 that creates the queries.  And what's kind of neat about this is whenever I've had a problem with the queries not working that well,  I just go change it in that one, how they're defined in that one place,  and they all kind of get the fix.  Where in our current system, when we decide we've been doing something wrong,  we have to go fix it in a thousand places.  So the nice thing is it's centralized code, and it's easy to fix.  And then a lot of it, you just have to put careful thought into the design of your ORMs.  Like I said, there are different extremes you can go to.  Do you want to load everything as one giant object, or just pieces of it,  and then lazily load the rest of the objects?  These are design decisions you'd have to make depending on what you're trying to do  and what kind of performance feedback you're getting when you try to actually run it.  Actually, your model is kind of important to centralize your SQL in one place.  Yeah, the idea is you don't have to write any SQL, right?  The only place there's SQL is in those dispatch functions.  And even those, I would like to rewrite them so they just use the ORMs themselves  and not SQL statements.  Do you have much testing in your CF?  Oh, yeah. There's a lot of testing in a CF.  How do you differentiate that to like, yes?  Oh, it's completely non-translatable. We have to rebuild everything.  But I will say this. The thing that blows us away the most is this is way faster than our C++ app.  Way faster. Which is strange for us because we've always been kind of C++.  Yeah, this is the only way to write a real program, but I have to admit this is faster.  It's just going to spur us to finish rebuilding the framework as fast as possible  because we think people are probably going to start complaining,  why is your older classic app so slow?  Is this being different only because a lot of it's done in the client?  It's because a lot of it's done in the client.  This has a data store, that whole object models in the data store,  so it caches a lot more than our current client does.  And that saves on a lot of query calls.  It's also just hyper, like I said, this thing started at Apple.  And there's some pretty smart guys at Apple, as you know.  It's pretty highly tuned out of the box.  Anybody else?  Okay, well, I guess it's about time to switch rooms.  Thank you all for coming.
Using_PostgreSQL_in_modern_enterprise_web_applications_0_131.png Using_PostgreSQL_in_modern_enterprise_web_applications_132_791.png Using_PostgreSQL_in_modern_enterprise_web_applications_792_2027.png Using_PostgreSQL_in_modern_enterprise_web_applications_2028_3311.png Using_PostgreSQL_in_modern_enterprise_web_applications_3312_4607.png Using_PostgreSQL_in_modern_enterprise_web_applications_4608_5411.png Using_PostgreSQL_in_modern_enterprise_web_applications_5412_6227.png Using_PostgreSQL_in_modern_enterprise_web_applications_6228_7595.png Using_PostgreSQL_in_modern_enterprise_web_applications_7596_9263.png Using_PostgreSQL_in_modern_enterprise_web_applications_9264_11735.png Using_PostgreSQL_in_modern_enterprise_web_applications_11736_15935.png Using_PostgreSQL_in_modern_enterprise_web_applications_15936_18947.png Using_PostgreSQL_in_modern_enterprise_web_applications_18948_20615.png Using_PostgreSQL_in_modern_enterprise_web_applications_20616_21431.png Using_PostgreSQL_in_modern_enterprise_web_applications_21432_31307.png Using_PostgreSQL_in_modern_enterprise_web_applications_31308_34391.png Using_PostgreSQL_in_modern_enterprise_web_applications_34392_35699.png Using_PostgreSQL_in_modern_enterprise_web_applications_35700_37919.png Using_PostgreSQL_in_modern_enterprise_web_applications_37920_38927.png Using_PostgreSQL_in_modern_enterprise_web_applications_38928_41711.png Using_PostgreSQL_in_modern_enterprise_web_applications_41712_42851.png Using_PostgreSQL_in_modern_enterprise_web_applications_42852_44735.png Using_PostgreSQL_in_modern_enterprise_web_applications_44736_44807.png Using_PostgreSQL_in_modern_enterprise_web_applications_44808_45035.png Using_PostgreSQL_in_modern_enterprise_web_applications_45036_45095.png Using_PostgreSQL_in_modern_enterprise_web_applications_45096_46247.png Using_PostgreSQL_in_modern_enterprise_web_applications_46248_46307.png Using_PostgreSQL_in_modern_enterprise_web_applications_46308_46355.png Using_PostgreSQL_in_modern_enterprise_web_applications_46356_46547.png Using_PostgreSQL_in_modern_enterprise_web_applications_46548_46763.png Using_PostgreSQL_in_modern_enterprise_web_applications_46764_47891.png Using_PostgreSQL_in_modern_enterprise_web_applications_47892_48371.png Using_PostgreSQL_in_modern_enterprise_web_applications_48372_49295.png Using_PostgreSQL_in_modern_enterprise_web_applications_49296_50555.png Using_PostgreSQL_in_modern_enterprise_web_applications_50556_50927.png Using_PostgreSQL_in_modern_enterprise_web_applications_50928_50987.png Using_PostgreSQL_in_modern_enterprise_web_applications_50988_52127.png Using_PostgreSQL_in_modern_enterprise_web_applications_52128_52211.png Using_PostgreSQL_in_modern_enterprise_web_applications_52212_52271.png Using_PostgreSQL_in_modern_enterprise_web_applications_52272_52751.png Using_PostgreSQL_in_modern_enterprise_web_applications_52752_52859.png Using_PostgreSQL_in_modern_enterprise_web_applications_52860_52907.png Using_PostgreSQL_in_modern_enterprise_web_applications_52908_52955.png Using_PostgreSQL_in_modern_enterprise_web_applications_52956_53099.png Using_PostgreSQL_in_modern_enterprise_web_applications_53100_53171.png Using_PostgreSQL_in_modern_enterprise_web_applications_53172_53255.png Using_PostgreSQL_in_modern_enterprise_web_applications_53256_53903.png Using_PostgreSQL_in_modern_enterprise_web_applications_53904_54479.png Using_PostgreSQL_in_modern_enterprise_web_applications_54480_54563.png Using_PostgreSQL_in_modern_enterprise_web_applications_54564_54611.png Using_PostgreSQL_in_modern_enterprise_web_applications_54612_54683.png Using_PostgreSQL_in_modern_enterprise_web_applications_54684_54731.png Using_PostgreSQL_in_modern_enterprise_web_applications_54732_54779.png Using_PostgreSQL_in_modern_enterprise_web_applications_54780_54959.png Using_PostgreSQL_in_modern_enterprise_web_applications_54960_55067.png Using_PostgreSQL_in_modern_enterprise_web_applications_55068_55127.png Using_PostgreSQL_in_modern_enterprise_web_applications_55128_55895.png Using_PostgreSQL_in_modern_enterprise_web_applications_55896_56855.png Using_PostgreSQL_in_modern_enterprise_web_applications_56856_58271.png Using_PostgreSQL_in_modern_enterprise_web_applications_58272_58343.png Using_PostgreSQL_in_modern_enterprise_web_applications_58344_58391.png Using_PostgreSQL_in_modern_enterprise_web_applications_58392_58475.png Using_PostgreSQL_in_modern_enterprise_web_applications_58476_58547.png Using_PostgreSQL_in_modern_enterprise_web_applications_58548_59147.png Using_PostgreSQL_in_modern_enterprise_web_applications_59148_59207.png Using_PostgreSQL_in_modern_enterprise_web_applications_59208_59255.png Using_PostgreSQL_in_modern_enterprise_web_applications_59256_59315.png Using_PostgreSQL_in_modern_enterprise_web_applications_59316_59363.png Using_PostgreSQL_in_modern_enterprise_web_applications_59364_59411.png Using_PostgreSQL_in_modern_enterprise_web_applications_59412_59459.png Using_PostgreSQL_in_modern_enterprise_web_applications_59460_60611.png Using_PostgreSQL_in_modern_enterprise_web_applications_60612_60827.png Using_PostgreSQL_in_modern_enterprise_web_applications_60828_61019.png Using_PostgreSQL_in_modern_enterprise_web_applications_61020_61391.png Using_PostgreSQL_in_modern_enterprise_web_applications_61392_61859.png Using_PostgreSQL_in_modern_enterprise_web_applications_61860_62027.png Using_PostgreSQL_in_modern_enterprise_web_applications_62028_62087.png Using_PostgreSQL_in_modern_enterprise_web_applications_62088_76847.png Using_PostgreSQL_in_modern_enterprise_web_applications_76848_76907.png Using_PostgreSQL_in_modern_enterprise_web_applications_76908_77447.png Using_PostgreSQL_in_modern_enterprise_web_applications_77448_77795.png Using_PostgreSQL_in_modern_enterprise_web_applications_77796_77855.png Using_PostgreSQL_in_modern_enterprise_web_applications_77856_87217.png