Monthly archives for April, 2016

On my current project, our client chose to use Seneca to re-platform their entire e-commerce system. Seneca is a microservices framework built on top of the rich Node.js ecosystem. The project required building a bunch of microservices that could serve multiple clients/channels (web, mobile, in-store kiosk, etc). I hadn’t heard of Seneca prior to the engagement. I was hesitant to use it as the client is mainly a .NET shop. However, after a little bit of digging, I’ve come to enjoy working with the framework as it makes building and organizing small bits of business features really fun.

Seneca wasn’t built initially to serve microservices. It was mainly built to truly decouple components and provide plug-and-play architecture where developers can focus on writing business logic, extending that logic easily and, more importantly, replacing it quickly. However, its clean design enables it to be a strong player in the microservices space. Here are some aspects of Seneca that I really like:

It makes no assumption about your architecture/design patterns. Hence, you’re not locked into an MVC-style pattern though you could organize your code in an MVC way if you wanted to

You can use any of the standard test frameworks/strategies that are available in JavaScript or NodeJS

Perhaps the most important aspect of Seneca is the plugin architecture, which allows the development of simple plugins for things like data abstractions, etc.

That said, there are some things to watch out for. Since Seneca makes no assumption about your architecture, you’re left to come up with design patterns and code organization on your own. Also, due to the richness of Seneca (and NodeJS), there is a bit of a learning curve. However, once you get around that, you start moving quite fast. If you like code generators to get started, there is a seneca yeoman generator though it doesn’t appear to be actively maintained (at the time of this writing) so we didn’t use it.

Enough talk, let’s build a simple, RESTful microservice with Seneca. Following the scheme of e-commerce, our service will be dealing with product information for camera widgets/parts. In this post, we will be using hard-coded data but we will swap this out for a real data store (MongoDB or any other data store) in future posts.

There are two interesting bits that warrant mention. Let’s take a look at the first bit:

this.add('role: products, cmd: getProductById', function() {...});

This is Seneca’s way of registering an action (getProductById) on a business feature/pattern (role: products). Whenever Seneca encounters this pattern from a client, it will execute the callback function associated with this code.

In contrast to registering an action, this.act('role: web') executes the action using Seneca’s web plugin. As I mentioned before, Seneca is transport-layer agnostic. The web plugin provides HTTP features (API routing) for the framework. It is responsible for translating url patterns into Seneca action patterns. It is packaged with Seneca by default so there is no need to include it or require it separately. By default, the alias that we have identified above is a GET request to get a product by the passed in ID. As you can see, that url pattern is mapped to our action function getProductById.

The code above starts up the server on port 3000, using the json body parser to exchange data in JSON. Express is another dependency here which is used by the web module.

Now you’re able to fire up your browser or any other REST client (postman) and hit the following end-point:

http://localhost:3000/products/123

You should be able to see the product information listed in JSON format. The full project is listed on GitHub. A sample http POST request for searching is also included in the code. In future posts, we will be looking at using Seneca’s data abstraction plugin and a file store so we’re not hard-coding the data.