Home Web Development Modern JavaScript Applications

Modern JavaScript Applications

By Narayan Prusty
books-svg-icon Book
eBook $43.99 $29.99
Print $54.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $43.99 $29.99
Print $54.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Breaking into Microservices Architecture
About this book
Over the years, JavaScript has become vital to the development of a wide range of applications with different architectures. But JS moves lightning fast, and it’s easy to fall behind. Modern JavaScript Applications is designed to get you exploring the latest features of JavaScript and how they can be applied to develop high-quality applications with different architectures. Begin by creating a single page application that builds on the innovative MVC approach using AngularJS, then move forward to develop an enterprise-level application with the microservices architecture using Node to build web services. After that, shift your focus to network programming concepts as you build a real-time web application with websockets. Learn to build responsive, declarative UIs with React and Bootstrap, and see how the performance of web applications can be enhanced using Functional Reactive Programming (FRP). Along the way, explore how the power of JavaScript can be increased multi-fold with high performance techniques. By the end of the book, you’ll be a skilled JavaScript developer with a solid knowledge of the latest JavaScript techniques, tools, and architecture to build modern web apps.
Publication date:
July 2016
Publisher
Packt
Pages
330
ISBN
9781785881442

 

Chapter 1. Breaking into Microservices Architecture

The architecture of server-side application development for complex and large applications (applications with huge number of users and large volume of data) shouldn't just involve faster response and providing web services for wide variety of platforms. It should be easy to scale, upgrade, update, test, and deploy. It should also be highly available, allowing the developers write components of the server-side application in different programming languages and use different databases. Therefore, this leads the developers who build large and complex applications to switch from the common monolithic architecture to microservices architecture that allows us to do all this easily. As microservices architecture is being widely used in enterprises that build large and complex applications, it's really important to learn how to design and create server-side applications using this architecture. In this chapter, we will discuss how to create applications based on microservices architecture with Node.js using the Seneca toolkit.

In this chapter, we'll cover the following topics:

  • Understanding monolithic architecture

  • Scaling, upgrading, deploying, and writing applications based on monolithic architecture

  • Discussing microservices architecture in depth

  • Scaling, upgrading, deploying, and writing applications based on microservices architecture

  • Getting started with Seneca

  • Creating and calling services using Seneca

 

What is monolithic architecture?


To understand microservices architecture, it's important to first understand monolithic architecture, which is its opposite.

In monolithic architecture, different functional components of the server-side application, such as payment processing, account management, push notifications, and other components, all blend together in a single unit.

For example, applications are usually divided into three parts. The parts are HTML pages or native UI that run on the user's machine, server-side application that runs on the server, and database that also runs on the server. The server-side application is responsible for handling HTTP requests, retrieving and storing data in a database, executing algorithms, and so on. If the server-side application is a single executable (that is, running is a single process) that does all these tasks, then we say that the server-side application is monolithic.

This is a common way of building server-side applications. Almost every major CMS, web servers, server-side frameworks, and so on are built using monolithic architecture.

This architecture may seem successful, but problems are likely to arise when your application is large and complex.

Demerits of monolithic architecture

The following are some of the issues caused by server-side applications built using the monolithic architecture.

Scaling monolithic architecture

As traffic to your server-side application increases, you will need to scale your server-side application to handle the traffic.

In case of monolithic architecture, you can scale the server-side application by running the same executable on multiple servers and place the servers behind a load balancer or you can use round robin DNS to distribute the traffic among the servers:

In the preceding diagram, all the servers will be running the same server-side application.

Although scaling is easy, scaling monolithic server-side application ends up with scaling all the components rather than the components that require greater resource. Thus, causing unbalanced utilization of resources sometimes, depending on the quantity and types of resources the components need.

Let's consider some examples to understand the issues caused while scaling monolithic server-side applications:

  • Suppose there is a component of server-side application that requires a more powerful or special kind of hardware, we cannot simply scale this particular component as all the components are packed together, therefore everything needs to be scaled together. So, to make sure that the component gets enough resources, you need to run the server-side application on some more servers with powerful or special hardware, leading to consumption of more resources than actually required.

  • Suppose we have a component that requires to be executed on a specific server operating system that is not free of charge, we cannot simply run this particular component in a non-free operating system as all the components are packed together and therefore, just to execute this specific component, we need to install the non-free operating system on all servers, increasing the cost greatly.

These are just some examples. There are many more issues that you are likely to come across while scaling a monolithic server-side application.

So, when we scale monolithic server-side applications, the components that don't need more powerful or special kind of resource starts receiving them, therefore deceasing resources for the component that needs them. We can say that scaling monolithic server-side application involves scaling all components that are forcing to duplicate everything in the new servers.

Writing monolithic server-side applications

Monolithic server-side applications are written in a particular programming language using a particular framework. Enterprises usually have developers who are experts in different programming languages and frameworks to build server-side applications; therefore, if they are asked to build a monolithic server-side application, then it will be difficult for them to work together.

The components of a monolithic server-side application can be reused only in the same framework using, which it's built. So, you cannot reuse them for some other kind of project that's built using different technologies.

Other issues of monolithic architecture

Here are some other issues that developers might face, depending on the technology that is used to build the monolithic server-side application:

  • It may need to be completely rebuild and redeployed for every small change made to it. This is a time-consuming task and makes your application inaccessible for a long time.

  • It may completely fail if any one of the components fails. It's difficult to build a monolithic application to handle failure of specific components and degrade application features accordingly.

  • It may be difficult to find how much resources are each components consuming.

  • It may be difficult to test and debug individual components separately.

 

Microservices architecture to the rescue


We saw the problems caused by monolithic architecture. These problems lead developers to switch from monolithic architecture to microservices architecture.

In microservices architecture, the server-side application is divided into services. A service (or microservice) is a small and independent process that constitutes a particular functionality of the complete server-side application. For example, you can have a service for payment processing, another service for account management, and so on; the services need to communicate with each other via a network.

Tip

What do you mean by "small" service?

You must be wondering how small a service needs to be and how to tell whether a service is small or not. Well, it actually depends on many factors such as the type of application, team management, availability of resources, size of application, and how small you think is small. However, a small service doesn't have to be the one that is written is fewer lines of code or provides a very basic functionality. A small service can be the one on which a team of developers can work independently, which can be scaled independently to other services, scaling it doesn't cause unbalanced utilization of recourses, and overall they are highly decoupled (independent and unaware) of other services.

You don't have to run each service in a different server, that is, you can run multiple services in a single computer. The ratio of server to services depends on different factors. A common factor is the amount and type of resources and technologies required. For example, if a service needs a lot of RAM and CPU time, then it would be better to run it individually on a server. If there are some services that don't need much resources, then you can run them all in a single server together.

The following diagram shows an example of the microservices architecture:

Here, you can think of Service 1 as the web server with which a browser communicates and other services providing APIs for various functionalities. The web services communicate with other services to get data.

Merits of microservices architecture

Due to the fact that services are small and independent and communicate via network, microservices architecture solves many problems that monolithic architecture had. Here are some of the benefits of microservices architecture:

  • As the services communicate via a network, they can be written in different programming languages using different frameworks

  • Making a change to a service only requires that particular service to be redeployed instead of all the services, which is a faster procedure

  • It becomes easier to measure how much resources are consumed by each service as each service runs in a different process

  • It becomes easier to test and debug, as you can analyze each service separately

  • Services can be reused by other applications as they interact via network calls

Scaling services

Apart from the preceding benefits, one of the major benefits of microservices architecture is that you can scale individual services that require scaling instead of all the services, therefore preventing duplication of resources and unbalanced utilization of resources.

Suppose we want to scale Service 1 in the preceding diagram. Here is a diagram that shows how it can be scaled:

Here, we are running two instances of Service 1 on two different servers kept behind a load balancer, which distributes the traffic between them. All other services run the same way, as scaling them wasn't required. If you wanted to scale Service 3, then you can run multiple instances of Service 3 on multiple servers and place them behind a load balancer.

Demerits of microservices architecture

Although there are a lot of merits of using microservices architecture compared to monolithic architecture, there are some demerits of microservices architecture as well:

  • As the server-side application is divided into services, deploying, and optionally, configuring each service separately is a cumbersome and time-consuming task.

    Note

    Note that developers often use some sort automation technology (such as AWS, Docker, and so on) to make deployment somewhat easier; however, to use it, you still need a good level of experience and expertise with that technology.

  • Communication between services is likely to lag as it's done via a network.

  • This sort of server-side applications more prone to network security vulnerabilities as services communicate via a network.

  • Writing code for communicating with other services can be harder, that is, you need to make network calls and then parse the data to read it. This also requires more processing. Note that although there are frameworks to build server-side applications using microservices that make fetching and parsing data easier, it still doesn't deduct the processing and network wait time.

  • You will surely need some sort of monitoring tool to monitor services as they may go down due to network, hardware, or software failure. Although you may use the monitoring tool only when your application suddenly stops, to build the monitoring software or use some sort of service, monitoring software needs some level of extra experience and expertise.

  • Microservices-based server-side applications are slower than monolithic-based server-side applications as communication via networks is slower compared to memory.

When to use microservices architecture

It may seem like its difficult to choose between monolithic and microservices architecture, but it's actually not so hard to decide between them.

If you are building a server-side application using monolithic architecture and you feel that you are unlikely to face any monolithic issues that we discussed earlier, then you can stick to monolithic architecture. In future, if you are facing issues that can be solved using microservices architecture, then you should switch to microservices architecture.

If you are switching from a monolithic architecture to microservices architecture, then you don't have to rewrite the complete application, instead you can only convert the components that are causing issues to services by doing some code refactoring. This sort of server-side applications where the main application logic is monolithic but some specific functionality is exposed via services is called microservices architecture with monolithic core. As issues increase further, you can start converting more components of the monolithic core to services.

If you are building a server-side application using monolithic architecture and you feel that you are likely to face any of the monolithic issues that we discussed earlier, then you should immediately switch to microservices architecture or microservices architecture with monolithic core, depending on what suits you the best.

Data management

In microservices architecture, each service can have its own database to store data and can also use a centralized database.

Some developers don't use a centralized database at all, instead all services have their own database to store the data. To synchronize the data between the services, the services omit events when their data is changed and other services subscribe to the event and update the data. The problem with this mechanism is that if a service is down, then it may miss some events. There is also going to be a lot of duplicate data, and finally, it is difficult to code this kind of system.

Therefore, it's a good idea to have a centralized database and also let each service to maintain their own database if they want to store something that they don't want to share with others. Services should not connect to the centralized database directly, instead there should be another service called database service that provides APIs to work with the centralized database. This extra layer has many advantages, such as the underlying schema can be changed without updating and redeploying all the services that are dependent on the schema, we can add a caching layer without making changes to the services, you can change the type of database without making any changes to the services and there are many other benefits. We can also have multiple database services if there are multiple schemas, or if there are different types of database, or due to some other reason that benefits the overall architecture and decouples the services.

 

Implementing microservices using Seneca


Seneca is a Node.js framework for creating server-side applications using microservices architecture with monolithic core.

Earlier, we discussed that in microservices architecture, we create a separate service for every component, so you must be wondering what's the point of using a framework for creating services that can be done by simply writing some code to listen to a port and reply to requests. Well, writing code to make requests, send responses, and parse data requires a lot of time and work, but a framework like Seneca makes all this easy. Also, converting the components of a monolithic core to services is also a cumbersome task as it requires a lot of code refactoring, but Seneca makes it easy by introducing the concepts of actions and plugins. Finally, services written in any other programming language or framework will be able to communicate with Seneca services.

In Seneca, an action represents a particular operation. An action is a function that's identified by an object literal or JSON string called the action pattern.

In Seneca, these operations of a component of monolithic core are written using actions, which we may later want to move from monolithic core to a service and expose it to other services and monolithic cores via a network.

Tip

Why actions?

You might be wondering what is the benefit of using actions instead of functions to write operations and how actions make it easy to convert components of monolithic core to services? Suppose you want to move an operation of monolithic core that is written using a function to a separate service and expose the function via a network, then you cannot simply copy and paste the function to the new service, instead you need to define a route (if you are using Express). To call the function inside the monolithic core, you will need to write code to make an HTTP request to the service. To call this operation inside the service, you can simply call a function so that there are two different code snippets depending from where you are executing the operation. Therefore, moving operations requires a lot of code refactoring. However, if you would have written the preceding operation using the Seneca action, then it would have been really easy to move the operation to a separate service.

In case the operation is written using action, and you want to move the operation to a separate service and expose the operation via a network, then you can simply copy and paste the action to the new service. That's it. Obviously, we also need to tell the service to expose the action via the network and tell the monolithic core where to find the action, but all these require just couple of lines of code.

A Seneca service exposes actions to other services and monolithic cores. While making requests to a service, we need to provide a pattern matching an action's pattern to be called in the service.

Tip

Why patterns?

Patterns make it easy to map a URL to an action. Patterns can overwrite other patterns for specific conditions, therefore it prevents editing the existing code, as editing the existing code in a production site is not safe and has many other disadvantages.

Seneca also has a concept of plugins. A seneca plugin is actually a set of actions that can be easily distributed and plugged in to a service or monolithic core.

As our monolithic core becomes larger and complex, we can convert components to services. That is, move actions of certain components to services.

Creating your first Seneca application

Let's create a basic application using Seneca to demonstrate how to use it. We will create an application that allows users to log in and register. This will be a demo application just to demonstrate how actions, plugins, and services can be created, and not how login and registration functionality works.

Before you proceed further, create a directory named seneca-example and place a file named package.json in it. Inside the seneca-example directory, we will place our services and monolithic core. Inside the package.json file, place the following code so that npm will be able to download the dependencies for our application:

{
  "name": "seneca-example",
  "dependencies": {
    "seneca": "0.6.5",
    "express" : "latest"
  }
}

Here we are using Seneca version 0.6.5. Make sure that you are also using the same version to avoid code incompatibility.

Now run the npm install command inside the seneca-example directory to install Seneca and other packages locally.

Now create a file named main.js that will be the monolithic core of our server side application.

The monolithic core and services are all seneca instances programmatically. Place this code in the main.js file to create a seneca instance:

var seneca = require("seneca")();

Now using this seneca object, we are going to add actions, call actions, add plugins, and everything.

Creating actions

Let's create actions for login and registration functionality and place them in the main.js file. To create actions, we need to use the add method of the seneca object. The first argument of the add method takes a JSON string or object that is the action identifier (called pattern to identify the action). The second argument is a callback that will be executed when the action is invoked.

Place this code in the main.js file that creates two actions for login and registration, as follows:

seneca.add({role: "accountManagement", cmd: "login"}, function(args, respond){
});

seneca.add({role: "accountManagement", cmd: "register"}, function(args, respond){
});

We will see the code for the body of the actions later in this chapter.

There is nothing special about role and cmd properties. You can use any property names you wish too.

The second argument is a callback, which will be invoked when the action is called.

If there are multiple actions with the same pattern, then the later overrides the others.

We need to use the act method of the seneca object to invoke an action that's local to the instance or resides on some other service. The first argument of the act method is a pattern to match an action, and the second argument is a callback that will be executed once the action has been invoked.

Here is an example code that shows how to call the preceding two actions:

seneca.act({role: "accountManagement", cmd: "register", username: "narayan", password: "mypassword"}, function(error, response){
});

seneca.act({role: "accountManagement", cmd: "login", username: "narayan", password: "mypassword"}, function(error, response){
});

The callback passed to the act method is executed asynchronously once the result of the action arrives.

Here, the object we passed to the act method has two extra properties than the action's pattern it is supposed to match. However, the action is still matched and invoked because in case the pattern passed to the act method has more properties than the action's pattern it is supposed to match, Seneca finds all the action's patterns whose properties are in the pattern passed to the act method and invokes the one that has the highest number of matching properties.

If Seneca finds multiple action patterns with equal number of matching properties, then they are matched in ascending alphabetical order.

Note

You can learn more about Seneca pattern matching at http://senecajs.org/getting-started.html#patterns-unique-override.

Creating plugins

A Seneca plugin is just a set of related actions packed together. Programmatically, a seneca plugin can be created using a function or module.

A plugin makes it easy to distribute a set of actions among applications. You will also find seneca plugins in online public package registry maintained by npm. For example, there is a seneca plugin that provides actions to work with the MongoDB database. This plugin can be inserted into monolithic cores or services with just a single line of code.

By default, Seneca installs four built-in plugins when we create a seneca instance. These plugins are basic, transport, web, and mem-store.

Let's first create a plugin using a function. The function name is the plugin name, and a plugin can also have an initialization action, which will be invoked as soon as the plugin is attached to the seneca instance.

So, let's create a plugin named account and place the login and register actions in that, as later on, we will be creating a service and moving the actions there. Remove the actions we defined earlier in the main.js file and place the following code instead:

function account(options)
{
  this.add({init: "account"}, function(pluginInfo, respond){
    console.log(options.message);
    respond();
  })

  this.add({role: "accountManagement", cmd: "login"}, function(args, respond){
  });

  this.add({role: "accountManagement", cmd: "register"}, function(args, respond){
  });
}

seneca.use(account, {message: "Plugin Added"});

Here we defined a function named account and attached it using the use method of the seneca object. To attach multiple plugins, we can call the use method multiple times.

The init:account action is the initialization action invoked by Seneca once the plugin is added. This can be used to do things such as establishing database connection or other things that the actions of the plugin depend on.

The this keyword inside the plugin refers to the seneca instance.

Let's create the same plugin using a module so that it's easily distributable and can be put up in the npm registry. Create a file named account.js and place it in the seneca-example directory. account.js is the plugin module. Place this code inside the account.js file:

module.exports = function(options)
{
  this.add({init: "account"}, function(pluginInfo, respond){

    console.log(options.message);

    respond();
  })

  this.add({role: "accountManagement", cmd: "login"}, function(args, respond){
  });

  this.add({role: "accountManagement", cmd: "register"}, function(args, respond){
  });

  return "account";
}

Here is the plugin name in the string returned by the anonymous function.

Remove the plugin code that we previously defined in the main.js file and place the following code instead:

seneca.use("./account.js", {message: "Plugin Added"});

Here, to attach the plugin, we are providing the module path.

Note

You can learn more about creating Seneca plugins at http://senecajs.org/write-a-plugin.html, and you can find all the Seneca plugins at http://senecajs.org/plugins.html.

Creating services

A service is a seneca instance that exposes some actions via network. Let's create a service that exposes the login and register actions.

Create an account-service.js file in the seneca-example directory that will act as the service. Then place the following code in it to create a service that exposes the login and register actions:

var seneca = require("seneca")();

seneca.use("./account.js", {message: "Plugin Added"});

seneca.listen({port: "9090", pin: {role: "accountManagement"}});

Here, we first created a seneca instance. Then we added actions via a plugin. You can also manually add actions using the add method of the seneca object. Finally, we exposed the actions via an HTTP protocol. Seneca also supports other protocols, but we will stick to HTTP, as it's the most commonly used one.

seneca.listen creates an HTTP server to listen to requests. We also provided the port number and pin, which are optional. The default port is 10101, and by default, there is no pin if not provided.

You must be wondering what is a pin and what is it used for? Well, you may not always want to expose all the actions of the service via a network. In that case, you can provide a pattern to the pin property and the server will handle these requests that match the pin pattern.

Now, for other services or monolithic cores to be able to call the actions of this service, they need to register this service.

Remove the previous plugin attachment code from the main.js file and add the following code to register the service:

seneca.client({port: "9090", pin: {role: "accountManagement"}});

Here we are registering the service by providing the port number and pin. Both of them are optional. In case if we don't use any port number, then it defaults to 10101. In case the service is on different server, then you should use the host property to provide the IP address.

The pin attached to the client method is used to tell the seneca instance about what actions are exposed by the service. It's completely optional. Seneca won't send requests to a service that doesn't match the pin pattern.

You can add as many services as you want by calling the client method multiple times.

When you call the act method to invoke an action, the seneca instance first looks for the action locally before requesting services. If it's not found locally, then it checks for the services that have a pin to see if it matches any. If a pin matches, then it sends request to this particular service. Finally, if any of the pin doesn't match, it sends the requests one by one to all other services that don't have a pin till it gets a valid response from one of them.

You can also manually call an action of a service by sending the GET request to these types of URL:

http://localhost:9090/act?role=accountManagement&cmd=login&username=narayan&password=mypassword

You can also call a service by using the POST request. Here is how to do it using CURL:

curl -d '{"role":"accountManagement","cmd":"login","username":"narayan","password":"mypassword"}' -v http://localhost:9090/act

Storing data

Seneca provides a built-in mechanism to store data. Seneca provides some built-in actions that allow us to store data. The built-in actions use mem-store to store data by default. mem-store is an asynchronous in-memory storage system.

You can create your application using the default storing mechanism. In case you want to change the underlying store system, you just need to install plugin for this particular storage system that will overwrite the built-in storage actions, therefore you will not have to refactor any code.

The built-in actions to do the CRUD operations are as follows:

  • role:entity,cmd:load,name:<entity-name>: This is used to retrieve an entity using its ID. An entity can be thought of as a row in MySQL. Every entity gets a unique ID.

  • role:entity,cmd:save,name:<entity-name>: This is used to update (if you provide entity ID) or add an entity if it does not exist. Entities are stored and retrieved in form of objects.

  • role:entity,cmd:list,name:<entity-name>: This is used to list all the entities that are matching a query.

  • role:entity,cmd:remove,name:<entity-name>: This is used to remove an entity using its ID.

Seneca also provides some wrapper functions that extract these actions and make it easy to call these actions. These functions are load$, save$, list$, and remove$.

Let's implement the login and register actions to allow us to log in and also register new accounts.

Here is the implementation of the account action. Update this code in the account.js file:

this.add({role: "accountManagement", cmd: "login"}, function(args, respond){
  var accounts = this.make("accounts");

  accounts.list$({username: args.username, password: args.password}, function(error, entity){
    if(error) return respond(error);

    if(entity.length == 0)
    {
      respond(null, {value: false});
    }
    else
    {
      respond(null, {value: true});
    }
  });
});

The first argument of the callback passed to the add method holds reference to the object that matched against the pattern of the action.

Here we are first calling the make method of the seneca object. It's used to get reference of an entity's store. For example, in case of MySQL, the make method gets reference to a table.

Then, we will find whether there are any entities with the username and password passed by the act method. As entities are added as objects, to query for entities, we need to pass an object. Now list$ looks for all entities with the same username and password.

We are passing a callback to the $list method that will be invoked asynchronously once the data is retrieved. This callback takes two parameters, that is, the first parameter is an error object if there is an error, otherwise null. Similarly, the second parameter is an array of entities found matching the given object.

For the action to respond back, it needs to call the second parameter of the action callback by passing it an object as the second argument. In case an error has occurred, we need to pass the error in the first argument.

Similarly, now let's write the code for the register action, as follows:

this.add({role: "accountManagement", cmd: "register"}, function(args, respond){
  var accounts = this.make("accounts");

  accounts.list$({username: args.username}, function(error, entity){
    if(error) return respond(error);

    if(entity.length == 0)
    {
      var data = accounts.data$({username: args.username, password: args.password})

      data.save$(function(error, entity){
        if(error) return respond(error);

        respond(null, {value: true});
      });
    }
    else
    {
      respond(null, {value: false});
    }
  });
});

Here, most of the code is understandable as it works the same way as the previous action. To store data, we are creating a new entity store reference using the data$ method by passing the entity we want to store. Then we are calling the save$ method to save the entity.

Note

To learn more about storing data in Seneca, visit http://senecajs.org/data-entities.html.

Integrating Express and Seneca

We have completed creating our login and register actions. Now, as our backend will be used by an app or it may represent as a website, we need to provide URLs to the clients who will use them to talk to the server.

Monolithic core is the part of our server-side application that the client interacts with for most of the functionality. Clients can also interact with services directly for some specific functionality if required.

So, we need to use some sort of website development framework in the monolithic core and services of our server-side application. We will be using Express, as it's the most popular one.

Seneca also provides a built-in way to map the URLs to actions, that is, requests made to an HTTP server can be automatically mapped to a particular action to invoke them. This is done using a definition object whose properties define a route mapping from URLs to action patterns. This built-in method defines route mapping independent of the framework being used. Once we have defined the definition objects, we need a plugin specific to the web server framework that will capture and resolve the URLs to action patterns using the definition objects. Definition object allows you to attach callbacks that will get the response of the action via a parameter, and then the callbacks can return the data to the user in whatever format they want. This can be useful in case you are creating a plugin for distribution that exposes a few actions that need to be called for specific URL requests, then you will have to use the built-in method, as it defines route mapping independent of the framework being used.

Note

You can learn more about how to use the built-in way to integrate Seneca and Express at https://github.com/rjrodger/seneca-web.

Add the following code to the main.js file to start the Express server in it:

var app = require("express")();
app.use(seneca.export("web"))
app.listen(3000);

On the second line, we are exporting a middleware function provided by the seneca-web plugin. seneca-web is the plugin to integrate Seneca and Express directly, that is, to translate URLs into action patterns using the definition object for Express framework. This is only required if we use the definition object to define route mapping. We won't be using definition objects, but we should still use seneca-web, as some third-party plugins may use definition objects if we are using these plugins. For example, if you are using the seneca-auth plugin, then you will have to include second line.

We want the user to be able to log in using the /account/login path and register using the /account/register path. The user will provide a username and password via query string. Here is the code to define routes to handle HTTP requests for login and registration:

app.get('/account/register', function(httpRequest, httpResponse, next){
  httpRequest.seneca.act({role: "accountManagement", cmd: "register", username: httpRequest.query.username, password: httpRequest.query.password}, function(error, response){
    if(error) return httpResponse.send(error);

    if(response.value == true)
    {
      httpResponse.send("Account has been created");
    }
    else
    {
      httpResponse.send("Seems like an account with same username already exists");
    }
  });
});

app.get('/account/login', function(httpRequest, httpResponse, next){
  httpRequest.seneca.act({role: "accountManagement", cmd: "login", username: httpRequest.query.username, password: httpRequest.query.password}, function(error, response){
    if(error) return httpResponse.send(error);

    if(response.value == true)
    {
      httpResponse.send("Logged in!!!");
    }
    else
    {
      httpResponse.send("Please check username and password");
    }
  });
});

Here we are calling the appropriate actions depending on the path of the URL request.

Here, instead of using seneca.act, we are using httpRequest.seneca.act as the middleware function that we exported earlier adds a new seneca property to request the object of every HTTP requests. This property inherits the actual seneca instance. Actions in the third-party plugins add information in form of properties to the seneca property in order to share information related to a particular HTTP request with other route handers. The preceding code will behave in the same way even if we use seneca.act, but it's a good practice to use httpRequest.seneca.act as we may use such types of plugins. Your own route handlers can also use httpRequest.seneca.act to pass information related to seneca to each other.

Now, to run the application, first run the account-service.js file and then the main.js file. You can then log in and register using the following URLs:

  • http://localhost:8080/account/login?username=narayan&password=mypassword

  • http://localhost:8080/account/register?username=x&password=mypassword

Here, we saw how to create a web interface, which can be used by an app or to serve HTML pages in case it's a website.

We can also move the routes of Express to a service if we want a different service to handle certain requests.

 

Summary


In this chapter, we saw the difference between monolithic and microservices architecture. Then we discussed what microservices architecture with monolithic core means and its benefits. Finally, we jumped into the Seneca framework for implementing microservices architecture with monolithic core and discussed how to create a basic login and registration functionality to demonstrate various features of the Seneca framework and how to use it.

In the next chapter, we will create a fully functional e-commerce website using Seneca and Express frameworks.

About the Author
  • Narayan Prusty

    Narayan Prusty is a full-stack developer. He works as a consultant for various start-ups around the world. He has worked on various technologies and programming languages but is very passionate about JavaScript, WordPress, Ethereum, Solr, React, Cordova, MongoDB, and AWS. Apart from consulting for various start-ups, he also runs a blog titled QNimate and a video tutorial site titled QScutter, where he shares information about a lot of the technologies he works on.

    Browse publications by this author
Latest Reviews (8 reviews total)
Overall the book is great so far
Excellent content and explanation.
Because of the quality of the content
Modern JavaScript Applications
Unlock this book and the full library FREE for 7 days
Start now