The Registration Problem
Consider this: you want customers to register with your site. When they do, a number of things need to happen:
The information needs to be validated
The customer record inserted
An email sent to say "thank you"
In a typical scenario, there's probably more - but let's use this for now.
This is some code that you might see in a Customers module:
This code is not only hideous-looking, it's also synchronous and a nightmare to maintain. Node allows you to do this much better with EventEmitters. Let's see how to use Events to clean this code up.
There are two ways to do this: encapsulate the eventing, or make your entire object an EventEmitter through inheritance. I'll do the latter.
The first thing to do is reference Node's event module and the util module as well - it has some helpers we'll need. Then we rewire the module to handle the events - I'll explain in a second, but here's the final code:
So what's going on here? Well first - there are no more callbacks - we don't need them! We have events to listen to.
Node helps you with this using the "util" library. On line 42 we're telling the util to push the prototype from events.EventEmitter onto our Customer function. Notice that this is a function, not an instance of a function as I had in the first example above.
Next, on line 7, I had to invoke the "base" constructor to be sure that I don't miss any internal instancing or setting of values. Turns out for EventEmitters you don't need to do that and you can omit this line - but it's safe to just do it, no matter what.
In the body of each method I'm simply "emitting" an event to all listeners (there's obviously some code missing here - pretend that I have an insert routine and so on). I can emit an event for whatever happens along the way - a successful validation fires "validated", a failure might fire "validationFailed". This frees up our code to do what it needs to do and no more, making it much cleaner and clearer.
On line 29 I've added a final event trigger if everything works out: "successfulRegistration". This is what calling code will really be interested in the most - either that or "failedRegistration" - and we pass along the customer record (more on that in a second).
On lines 35 through 38 we've implemented a bit of workflow. This doesn't need to be inside the Customer function - you can arrange these events wherever and however you like. The calling code can remove every event listener and replace it with its own if it wanted to reorganize the flow here.Speaking of calling code, here's what it might look like:
You hook into the events, then run register() and respond as needed.
All Over The Place
Node is built on top of EventEmitters - you'll find them everywhere. Understanding them is key to writing cleaner code that's more functional and maintainable - and it also helps keep things asynchronous.
n the first example, we had a synchronous drop all the way down - even though we were using callbacks. Our code above isn't synchronous at all - we've hooked into an event and when Node is ready, it will process the emitted event callback.
Domain Specific Languages (DSLs) have been around since I've been in computing, but it's hard to find much information about how to work with them. DSLs are small languages, focused on a particular aspect of a software system. You can't build a whole program with a DSL, but you often use multiple DSLs in a system mainly written in a general purpose language.
DSLs come in two main forms: external and internal. An external DSL is a language that's parsed independently of the host general purpose language: good examples include regular expressions and CSS. External DSLs have a strong tradition in the Unix community. Internal DSLs are a particular form of API in a host general purpose language, often referred to as a fluent interface. The way mocking libraries, such as JMock, define expectations for tests are good examples of this, as are many of the mechanisms used by Ruby on Rails. Internal DSLs also have a long tradition of usage, particularly in the Lisp community.
People find DSLs valuable because a well-designed DSL can be much easier to program with than a traditional library. This improves programmer productivity, which is always valuable. In particular it may also improve communication with domain experts, which is an important tool for tackling one of the hardest problems in software development. CSS is an excellent example of this, most people who program CSS don't consider themselves to be programming. Despite this, however, I don't generally think that end-users will usually write in DSLs directly - it's the communication enhancement that's important.
Although DSLs have been around for a long time, the lack of knowledge of how to program with them is a significant barrier - which is exactly why I've worked on this book. The books provides techniques to develop both internal and external DSLs, giving you both the information to choose between them and a good package of information to begin your work. I also stress the importance of layering a DSL over a library, so that you usually build both together. There's also material on code-generation, which is an occasionally essential part of working with a DSL.
Like P of EAA this book is a Duplex Book, providing both a narrative to learn about DSLs and a reference book for the details, so don't be scared by the high page count. You can get a good grasp of the topic by reading the narrative section (142 pages) and use the rest as a reference to dip into when you need it.
Electronic copies of this book are available. Amazon has a version for kindle. InformIT has epub and pdf versions. Apple has an epub version in the ibookstore.