Chapter 6. Inter-module interaction

This chapter covers

  • A review of modules
  • Interaction between modules
  • Using module dependencies
  • Using the publish/subscribe pattern

In chapter 3, you learned a great deal about modular programming. One of the biggest takeaways from that chapter is the idea that you can internalize the complexity of your code and provide a public API to its features by applying an architectural design pattern commonly referred to as the module pattern. This is a way to achieve encapsulation in JavaScript.

As you discovered, coding with modules helps organize your application’s logic into small, single-purpose units, which are easier to maintain and update. This inevitably leads to greater reusability for your code. Using modules also helps with data integrity, code organization, and the avoidance of name collisions. After all, you’re creating code in a single, nonrefreshing page. Without this kind of design for your application’s code base, relying purely on global variables and functions would quickly become unmanageable (see figure 6.1).

Figure 6.1. As your project grows, your code base become less and less manageable if you put all your variables and functions in the global namespace.

Even though the modules themselves are at the heart of modular programming, being able to use them to create a successful SPA requires more than knowing how they work mechanically. You also need to understand how they can interact: how one module can invoke the functionality of another module and, possibly, receive a response.

This chapter continues talking about modules but this time within the context of shaping your SPA’s architecture by the way you design module interaction. The chapter begins with a review of a module’s structure at a high level but mainly concentrates on the design of the inter-module interaction process.

Note

Because modules are based on a variety of pattern styles (such as the traditional, revealing, AMD, and AngularJS-styled modules, to name a few), I’ll keep our discussion as neutral as possible when covering the chapter’s concepts. Also, as in the other chapters, I’ll include highlights from a concrete example, with its entire source available for download.

For your project this time, you’ll create an SPA for an acquaintance who wants to start an online store to sell used video games. You won’t need to get into the complexities of having a shopping cart in this exercise. You’ll instead focus on the store’s product search feature. This application, though simple, will still give you a chance to create several modules and design how they’ll interact, without drowning in source code.

As in the preceding chapter, you’ll save the details of the project for later. Even though the interface is fairly trivial, we’ll make sure that the modules that power the application are interesting. Before talking about methods for inter-module interaction, though, let’s set the stage by reviewing some basic concepts for modular programming.

6.1. Review of module concepts

Let’s begin by reviewing some basic module concepts at a high level. We’ll use this as a baseline for the rest of our discussion.

6.1.1. Modules encapsulate code

Because the JavaScript specification at the time of this writing has no built-in syntax for creating modules or classes to encapsulate parts of your code, it’s simulated using the module pattern.

Note

The next version of JavaScript, ECMAScript 6 (also called Harmony or ES.next), adds official support of the module to the language.

A module, as far as JavaScript goes, is a specially constructed function. This type of function is often called an immediately invoked function expression (IIFE).

Immediately invoked function expression

As a reminder, the module pattern’s outer function is often referred to as an immediately invoked function expression, or IIFE, because it’s written as a function expression (it doesn’t start with the function keyword) instead of a function declaration and has a trailing set of parentheses to make the function get invoked immediately. The IIFE’s syntax looks like this:

var x = (function() {
    // do something
})();

If you want to learn more about function expressions and how they differ from function declarations, here’s a good resource: http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions

The following listing is an example of the traditional module pattern. It creates a module to apply a discount to the price of a product. The price of the product is passed in via the calculate function, and the new, discounted price is returned.

Listing 6.1. Traditional module pattern

Note

For AMD/CommonJS modules, an assignment to a global variable isn’t needed.

Chapter 3 also introduced a popular variation of the module pattern called the revealing module pattern. The following listing shows the same module written using this style.

Listing 6.2. Revealing module pattern

In this version, everything is the same except for the returned object literal. Here, the public function is merely a pointer to the internal code. This makes the API cleaner and easier to read.

With either version, the pattern’s design enables the module to be used as a wrapper for a piece of functionality (see figure 6.2).

Figure 6.2. The module forms a protective barrier around your code. Variables and functions declared within the module are private.

The outer function of the module pattern forms a kind of protective barrier around your code. This is possible thanks to the limiting scope of the outer function.

Note

Scope (in a broad sense) refers to the accessibility of one part of an application to another part of that application.

The clever design of this function also enables you to avoid polluting the global namespace with your application’s variables and functions, because they’re local to the module’s outer function.

6.1.2. APIs provide controlled access to internal functionality

Another nice feature of the module pattern is that it allows for the creation of an application programming interface, or API. An API is like the module’s contract when another module wants to talk to it. The API defines what’s publicly available. Code from other modules can use the API to gain limited and controlled access to its internal code.

The API is formed via the module’s return statement (see figure 6.3). This forms a bridge between the internal functionality of the module and the outside world.

Figure 6.3. Each public function in the API (left of the colon) has a corresponding reference to a private object inside the module (right of the colon).

In figure 6.3, we’re returning an object defined with object literal syntax. In the returned object, any object member names to the left of the colon are exposed. Those to the right are the references to the internal code of the module.

After the object is returned, it’s assigned to an external variable. This variable acts like a remote control to the module’s functionality. Other modules will send messages to the object referenced by the variable. This variable will continue to hold a valid reference to that object as long as the variable persists (see figure 6.4).

Figure 6.4. The assigned external variable can be used to indirectly reference the internal objects.

Providing an API for your encapsulated code enables you to not only access the code within the module but also custom-tailor any interaction with it. You can purposely name the exposed functions of the API something meaningful to the others while naming private functions something meaningful only to the internal code. You can also choose to expose certain things about the module’s inner workings while hiding others.

Remember that you’re not hiding functionality out of secrecy. You’re limiting what’s exposed in the API to only what’s needed for other modules to successfully use it.

6.1.3. SRP means designing with a single purpose

When you design a module, you try to limit the scope of its functionality to a single purpose. Having only one purpose per object is the crux of the single-responsibility principle (SRP) for software design. You can extend this idea to the modules you design: the module itself might have many variables and many functions inside it, but all of them are to support the module’s overall reason for existence. When modules are designed with SRP in mind, they become like cogs in a machine. Each cog has a particular purpose but works harmoniously with the other modules of the application.

As the application grows in complexity, it’s also normal for the complexity in your modules to increase. The nice thing about modules, though, is that if their code starts getting out of control in some way (becoming too large or enabling other purposes to emerge, perhaps), you can always refactor and split that module into one or more other modules.

For example, imagine that your video game application begins as a single module, but its functionality eventually grows beyond its initial purpose. The best strategy is to refactor the code, dividing it into smaller, single-purpose modules (see figure 6.5).

Figure 6.5. Modules can have multiple functions but should ideally have a single, overall purpose.

Refactoring large, multipurpose modules helps you preserve the SRP aspect of your application’s code base.

6.1.4. Code reuse helps your project scale

Another thing that sometimes emerges when you refactor is the potential to find functionality that can be reused either in the immediate project or in future phases of the project. Reusable components mean less work as your project evolves and grows larger, because having shared modules eliminates the need to repeat code in multiple places.

Currently, your SPA in this chapter’s example displays the price of a game only after a product is selected from the search results. Imagine, however, that your acquaintance calls you back to ask for additional features, such as a shopping cart or a product list, which also require displaying the game’s calculated discount price (see figure 6.6).

Figure 6.6. Modules can be reused in other modules, other features, or even throughout the entire application.

With your application designed with reusable modules, you could support these types of enhancement requests with less retooling than typically required.

Being able to design your application’s infrastructure in a modular fashion is well and good, but how are these self-contained units able to interact with each other? In the next section, you’ll look at a couple of basic interaction methods.

6.2. Inter-module interaction methods

Modules interact in two ways: directly via module APIs, which creates a direct dependency, or through events. We’ve talked about module APIs in detail, so this section focuses more on how using dependencies for module interaction affects the application’s architecture. This section also covers the decoupling effect of events and, more specifically, an event aggregation pattern called pub/sub.

6.2.1. Inter-module interaction through dependencies

Even though the syntax of any of the module pattern styles looks alien, a module is still just a function. As such, you can pass things into it via its parameters. Passing in another module as a parameter is one way modules can interact. This interaction method is considered direct, because one module is directly accessing the API of another. When one module interacts with another by directly calling the other’s API, the other module is known as its dependency.

Each module style provides a way to declare other modules as dependencies. Although the syntax varies, the dependency list of each type serves a universal purpose: to allow a module access to the APIs of other modules so they can interact with one another. If you’re using the traditional module pattern, for example, you declare dependencies in the module’s trailing parentheses and gain access to them via its parameter (dependency) list.

To illustrate, let’s use the pricing module shown previously. To get the price of each selected game at a discount, you need to add it as a dependency to the product display module (see the following listing). You’re also adding the product data module as a dependency to gain access to your stub product data.

Listing 6.3. Traditional module pattern dependency list

After a module is declared as a dependency of another, you can gain access to its API. The module’s API ensures that you access its functionality as it was intended, passing in any necessary information for the call to be successful. Bear in mind that you still don’t have direct access to the dependent module’s module-scoped functions and other objects.

Note

Even though adding a module as a dependency is a direct method of module interaction, you’re still interacting only via the dependent module’s API.

Interacting through dependencies is a good choice for many situations but not always. This method has both pros and cons.

6.2.2. Dependency method pros and cons

Here are a few advantages and disadvantages of direct inter-module interaction. Don’t see this list as reasons to adopt this method or not, however. It’s not practical to avoid using dependencies in modular programming. Think of the list as a helpful guide for when to use them.

Pros:

  • No intermediary objects are involved; one module can directly call the API of another.
  • Direct interaction is sometimes easier to debug.
  • Using the dependency list of a module, it’s easy to look at the source code and figure out which modules have been grouped together for a particular functionality or feature.

Cons:

  • With dependent modules, a certain amount of coupling is involved. Coupling refers to how directly tied one part of your code is to another. When you couple modules, you reduce the flexibility you normally have when updating your code.
  • Dependency lists can get rather long, which can sometimes make it a little hairy to keep track of what’s dependent on what.
  • When a module interacts with its dependency, it’s a one-to-one relationship. This type of interaction is narrow and has only one recipient, as opposed to the method described in the next section, which can have multiple recipients.

The other option for module interaction is through events. The next section highlights a popular event aggregation pattern called the publish/subscribe (or pub/sub) pattern. You’ll learn about what publish/subscribe is, how it works at a high level, and some of the pros and cons of using it in your SPA.

6.2.3. Inter-module interaction through publish/subscribe

Whether you’re talking about interaction with the DOM or interaction between objects as you’ve seen in our discussion of MV* frameworks, events are used extensively in modern applications. You can think of the entire browser environment as being event driven. Events provide a natural way to achieve loose coupling, because recipients can choose to listen or not and also decide on how to respond.

Several design patterns around events have emerged over the years. The one this section focuses on is called the publish/subscribe, or pub/sub, pattern. Pub/sub is a common and useful pattern for interaction between disparate modules. Pub/sub is based on a classic design pattern called the observer pattern.

With the observer pattern, one object is directly observed (the observable), and any number of other objects (called observers) can choose to pay attention to it, as shown in figure 6.7. The observable sends out a notification (typically through events) whenever its state changes so the observers can react accordingly.

Figure 6.7. In the observer pattern, each observer is notified whenever something changes in the object it’s observing.

What distinguishes pub/sub from the traditional observer pattern is that usually an intermediary service publishes (sends/broadcasts) the notifications on behalf of another object. Other objects in the application can choose to listen or not.

This type of brokered, indirect inter-module interaction is ideal when two unrelated modules need to interact or an application-wide message needs to be broadcast without any expectations by the publisher about what happens when the message is received.

Topics

Though not a requirement, notifications with most pub/sub implementations are topic based. A topic (or event name in AngularJS) is a simple name that’s used to represent a particular notification. If another object wants to listen, it subscribes to that topic. When a topical message is published, the message broker delivers that notification to any of the topic’s subscribers.

In the case of your application, you’ve created a module whose sole purpose is to broadcast system-wide messages using AngularJS’s built-in pub/sub mechanism. This module will use pub/sub to publish a message with the topic userMessage.

As you can see in figure 6.8, the topic has only one subscriber: the controller for a view that displays user alerts. Because the controller is a subscriber of the userMessage topic, it will update the text in the view anytime it receives a new message.

Figure 6.8. The messaging module uses pub/sub to publish messages, via an intermediary service, to any subscribers in the application.

Usually, pub/sub topic notifications and subscriptions are created programmatically by using the syntax style provided by the pub/sub implementation. If you decide to use the pub/sub method in your application, you’ll need some type of pub/sub software.

Pub/sub libraries

In your SPA, either the message broker implementation will be built into the MV* framework or you’ll have to download one of the many JavaScript pub/sub libraries available. Table 6.1 lists a few of the pub/sub libraries available at the time of this writing.

Table 6.1. A sampling of pub/sub libraries

Pub/sub library

URL

AmplifyJS http://amplifyjs.com
PubSubJS https://github.com/mroderick/PubSubJS
Radio.js http://radio.uxder.com
Arbiter.js http://arbiterjs.com

As with any dependency in your application’s code base, check out all available alternatives, using the usual set of criteria I’ve previously mentioned: learning curve, bugs and fix rate, documentation, maturity, and community support.

Basic notifications

The most basic type of notification doesn’t include any data being passed. It’s merely the topic name of the message that’s being published. To illustrate a basic notification in pub/sub, we’ll keep things vendor agnostic by using pseudocode.

We’ll start with how to publish a topical message. To publish a message in module A, you include a line similar to the following:

pSub.publish("hello_world_topic");

It’s that simple. Then, in module B, subscribing to that topic is equally easy. You include the topic name of the message you’re interested in hearing and what you want to happen when you hear it:

pSub.subscribe("hello_world_topic", functionToCallWhenHeard);

You typically use a basic notification to inform all subscribers that something happened. Then each subscriber, upon hearing it, can react in a completely different way.

At times, however, you’ll want to include data along with the topic being published. This, too, is accomplished easily using pub/sub.

Notifications with data

In addition to basic notifications, most pub/sub brokers let you pass data along when the message is published. In turn, each subscriber of that topic gets this data passed into its callback function by the broker. To publish with data, you use a line similar to the following in module A:

pSub.publish("hello_world_topic", dataObjectToSend);

In module B, your subscription line would be the same. The message broker passes the data sent into the function you list in the subscription:

pSub.subscribe("hello_world_topic", functionToCallWhenHeard);

The only difference when receiving data is in your callback function itself. Here, you’ll need a parameter in the function’s signature to represent the data being passed to it from the subscription:

function functionToCallWhenHeard( paramForDataPassed ) { ... }

With most brokers, any valid JavaScript object or value can be passed with the notification.

Unsubscribing

Another feature common to most pub/sub implementations is the ability to unsubscribe. Because subscriptions are topic based, the subscriber can invoke the broker’s unsubscribe function when it doesn’t want to react to that topic anymore. Once again, most pub/sub implementations make doing this super easy:

pSub.unsubscribe("hello_world_topic");

Various other options might be available, such as setting a priority for a topic, but are specialized and vendor specific. Additionally, the options mentioned thus far are the bare minimum but aren’t guaranteed to be available in the pub/sub implementation you’re using. The documentation for the broker you’re using will specify the list of available features.

6.2.4. Pub/sub pros and cons

As noted earlier, pub/sub is a pattern that helps keep the modules of your code base decoupled. It can be a powerful and flexible tool, but it’s not without its disadvantages. The following are some of the main pros and cons of using pub/sub in your SPA.

Pros:

  • It promotes a loose coupling of your modules through notifications posted to a message broker, instead of having to maintain direct dependencies.
  • As with using APIs, pub/sub is easy to implement.
  • Notification topical messages can be broadcast to many subscribers at once.
  • Different parts of the application can elect whether to pay attention to published messages.

Cons:

  • If not built into the MV* framework, the message broker implementation itself is an extra dependency that must be separately maintained.
  • Notifications flow in only one direction. No acknowledgement or response is sent back to the publisher (although you could create a response topic to create a kind of ping-pong effect).
  • Topics are simple text strings. You must rely on a naming convention to ensure that they’re routed to the correct recipient.
  • It’s harder to track the flow of messages through the system while debugging.
  • In your code, you must ensure that the subscriber is available and listening before the notification is published, or the topic won’t be heard.

Now that we’ve reviewed module concepts and the ways modules can interact, let’s review some of the highlights of this chapter’s project. For consistency with the other chapters in this book, we’ll use AngularJS.

6.3. Project details

As mentioned at the start of the chapter, in this project you’ll create a simple online store for a friend who has a small business selling used video games. You’ll create only the product search portion of the SPA, because that’s enough to demonstrate both methods of inter-module interaction discussed in this chapter.

In previous chapters, you’ve stuck to a feature or two for your sample application to stay focused on the concepts at hand. But now your application’s code base is a little more elaborate. In this chapter, your application is divided into the following features:

  • Search
  • Product display
  • Pricing
  • Messaging
  • User alerts

As you go through some of the code highlights, you’ll see the inter-module interaction method used by each module so you can see how the application is connected. Before going over the code, though, let’s discuss the objectives for this project.

For starters, figure 6.9 provides a glimpse of what the application will look like when you’re finished. With that image in mind, here’s a list of features that you want the application to have:

Figure 6.9. Our sample project is an online store to sell used video games.

  • Customers can search by both partial and complete game titles.
  • Successful searches display a list of results, including a thumbnail image of the game and its title.
  • In addition to the search results, a message with the number of results appears briefly at the bottom of the application after each search.
  • When a game is selected, the user is taken to the product view to display the details of that particular game.
  • The discounted price of the used game is based on the current retail price with a standard 40% discount (to keep things simple).

Because previous chapters have thoroughly covered routing and views, I’ll refer to them only when setting the stage for each section of our module discussion. As always, the complete project is available for download.

Because this chapter’s example uses AngularJS, you’ll also need a brief overview of modules and dependencies in AngularJS. Although this discussion of the sample project will be as neutral as possible, you’ll need a little AngularJS knowledge to follow the source code.

High-level overview of AngularJS modules and dependencies

In AngularJS, you can create a module by calling the framework’s module() function, supplying it with a name for your module and, optionally, including a dependency list:

angular.module("moduleName", ["dependency1", "dependency2"])

Let’s compare that with the traditional module pattern:

var moduleName = (function(depParam1,depParam2) {
})(dependency1, dependency2)

If you don’t have any dependencies, you provide empty brackets:

angular.module("moduleName", [])

After creating your module, you can create Angular-specific components within that module based on what you need your code to do. AngularJS provides the following out-of-the-box components: filters, directives, controllers, values, constants, services, factories, and providers. The details of the various AngularJS components are beyond the scope of this book but can be found in the online documentation at https://angularjs.org.

In addition to the built-in AngularJS directives, you’re using controllers, values, and factories. You’re using the value component to hold your stub data, because this component is ideal for storing values used in an application. The factory components are the closest equivalent to your traditional module pattern (in terms of purpose), so you’ll mostly use those for basic functionality. The controller components will, as you’ve already learned, act as a bridge between your application’s code and the UI.

To create components for a module, you add a component function, such as factory() or controller(), to the module declaration. You can also include the name of other components in your declaration, and AngularJS will inject them into the one you’re creating. This is called dependency injection (or DI).

To tell AngularJS you want another component injected into the component you’re creating, you add the other component to the function parameter list. The names of injected components are duplicated as text strings, to decouple the name of the concrete implementation of the injected dependency from the named reference that the consuming code binds to:

angular.module("moduleName",[])
   .factory("componentName",
     ["otherComponent",function(otherComponent){...}]
   )

The components you ask AngularJS to inject for you can be any you’ve created in another module (if that module is in the current module’s dependency list) or any one of the out-of-the-box components from AngularJS or other third-party components.

Now that you understand the bare minimum of AngularJS modules, components, and dependencies, you can move on to the source code for this chapter’s SPA example.

6.3.1. Searching

When the application loads, users are greeted with a welcome message and a way to search for the games they’re interested in. The header view and the search view are fixed, so they stay present as the main content changes with each search. When a title is searched, clicking the Search button invokes the route to display your search results (see figure 6.10).

Figure 6.10. Upon arrival, users are greeted with a welcome message and can immediately begin to search for games.

Figure 6.11 gives you a big-picture look at this transaction, end to end, and notes the type of inter-module interaction, where relevant.

Figure 6.11. High-level view of what happens when a user searches for a game title

All searches are keyword searches, so any game with the search term in its title is added to the result list and displayed.

That’s the high-level view of what happens when a user searches. Let’s break down the steps now and look at the code behind each type of module interaction.

The search controllers module

The search.controllers module has two controller components: one to handle the search itself and the other to display the search results. It also includes the search.services module as its sole dependency (see figure 6.12).

Figure 6.12. The search controllers module uses the search services module to perform the searches. The search services module is its only dependency.

After searching, your search results controller uses a component in the search.services module to do the work of looking up the term a user has entered. If anything is found, the search component in the search.services module will return a list of game objects for the results controller to pass to its view for display.

The following listing shows the code for your search.controllers module. Remember that in AngularJS you add module-level dependencies in the brackets of the module declaration.

Listing 6.4. Module for search controllers

Now that you’ve seen the controllers used for the search, let’s review some of the code in the search.services module that you’ve added as a dependency. This is the module that does the heavy lifting.

The search services module

The search.services module has two dependencies: one to access your data and another to broadcast the number of search results found (see figure 6.13).

Figure 6.13. The search services module uses the app data module as a data source and the messaging services module to broadcast the number of search results.

Let’s break up your analysis of the search so it’s easier to read. Let’s start with the dependency list:

angular.module("search.services",["data.appData"
,"messaging.services])

Here you’ll notice that the search.services module also has its own dependencies. Its dependencies include the following:

  • data.appData —A module that contains your game inventory
  • messaging.services —A module that creates a message about the number of results found and uses pub/sub to broadcast that message

The following listing provides the entire source code for this module. It’s a lot of code, but most of it is routine JavaScript used to match the search term with any of the game titles.

Listing 6.5. The search services module

This asks AngularJS to inject productData from the data.appData module and messageSvc from the messaging.services module. These are the worker bees of these two modules.

For each match of the search term, the information is returned to the caller (the search results controller). The results are then displayed for the user. Figure 6.14 shows the search results view after a successful search. In this case, the user used the term of in the search. This matched the game titles Call of Duty Advanced Warfare and Middle Earth: Shadow of Mordor.

Figure 6.14. Search results are displayed to the user, along with a brief user alert about the number of records found. Each search result is a link to display the item’s details.

Additionally, the number of records found is briefly presented at the bottom of the screen. Before the searchByTitle function returns, you’ll use the messageSvc component of the messaging.services module to create a message about the number of matches in your search and broadcast it to the rest of the application. This is the message in yellow in figure 6.11. This messaging module will then use pub/sub to do the broadcast.

The messaging module

The messaging module has no module-level dependencies, so no direct interaction occurs with another module. It does, however, interact indirectly with the user alerts module using pub/sub (see figure 6.15).

Figure 6.15. The messaging service is a generic utility, blindly broadcasting any message it’s given. The only module listening in this case is the user alerts module.

The code in this module is straightforward. It has a single component called messageSvc that uses the built-in pub/sub system of AngularJS to broadcast any messages passed to it, as you can see in the following listing.

Listing 6.6. The messaging services module

With a generic, system-wide feature such as broadcasting a message, it’s acceptable, if not preferable, to use pub/sub. Also, writing the module in this generic way lets you use it as a general messaging utility that can be reused anywhere you need to broadcast a message.

With the search results being displayed and the number of results being broadcasted, you’re left with the module that will consume the broadcast and display it as a user alert.

The user alerts module

The user.alerts module also has no module-level dependencies. As you saw in the previous section, it’s communicated with indirectly by the messaging services module (refer back to figure 6.15).

Inside the module, you have one component that’s a controller, so you can display any information received in the pub/sub subscription in the user alerts view (see the following listing).

Listing 6.7. The user alerts module

At this point, you’ve seen how the application uses both direct and indirect methods of inter-module interaction when searching. Let’s take a look at what happens when the user makes a selection from the search results list.

6.3.2. Displaying product information

When the user clicks one of the search results, a change in state allows the application to display the details about the selected game. Figure 6.16 shows an overview of this transaction.

Figure 6.16. High-level view of the product display process

After a game is selected from the search results list, its ID is sent to the product display controller as a parameter.

The product display controllers module

The productdisplay.services module has only one module-level dependency: the product display services module (see figure 6.17).

Figure 6.17. The product display services module finds the correct product information by using the selected game’s ID.

As shown in the following listing, you use the productDisplaySvc component of the productdisplay.services module to look up the selected game’s details, using the product ID sent in via the parameter. These details are then assigned to the $scope (ViewModel) for display.

Listing 6.8. The product display controller

Let’s take a peek at what’s going on inside the product display services module in order to look up the game information and apply the price discount.

The product display services module

The productdisplay.services module has two dependencies: one to access your data and the other to calculate a 40% price discount for the selected used game (see figure 6.18).

Figure 6.18. Your data and the components of the pricing module are used to calculate the price discount for the game selected.

You have some typical JavaScript coding in this module to iterate over the list of games and find the one with the matching ID (see the following listing).

Listing 6.9. Product display services module

Having seen the product display services module, let’s examine the last leg of your transaction to see how the discount is calculated and returned to the calling module.

The pricing services module

The pricing.services module has no dependencies of its own and is straightforward. It takes in an amount from an outside module and multiplies that by the discount rate, as shown in the following listing.

Listing 6.10. The pricing services module

After the appropriate game has been found via its ID, and the discount has been applied to its price, a new game object is returned to the controller, where the information is passed over to the view for display. Figure 6.19 shows the outcome of the product selection.

Figure 6.19. The resulting view after a matching game is found and a discount has been applied to the game’s price

The resulting view is the culmination of your inter-module interaction design for this project, including both the dependency method and the pub/sub method.

6.4. Chapter challenge

Now here’s a challenge for you to see what you’ve learned in this chapter. Create a movie title search for which the matching names of movies appear in a list as the user types into an input text field. Use a single view, with the input text field at the top and an unordered list for the results below it. Use a mix of dependencies and pub/sub to create the functionality. Using your preferred MV* framework, bind a key-up event to a function that will publish the contents of the input field with every keystroke. One module should be listening for the topical message and perform the search. It should also use pub/sub to publish the results. Upon hearing the results, populate the unordered list by using your MV* framework.

6.5. Summary

You’ve accomplished a lot in this chapter. You’ve learned the following about inter-module interaction:

  • Modules are a vital part of your application’s infrastructure, providing encapsulation and a means for code reuse.
  • A well-designed module can have many functions but should have a single overall purpose.
  • Although the internal code of a module remains hidden, its API provides a central, controlled access point for its functionality.
  • Two main methods exist for inter-module interaction: directly via APIs or indirectly via events. An event aggregation pattern called pub/sub was used to illustrate.
  • No hard-and-fast rules exist for the type of inter-module interaction that you should choose. In general, modules related to a feature are good candidates for the dependency method. General-use modules, such as utility modules, are also OK to have as a direct dependency. Pub/sub is most suitable for unrelated modules and application-wide notifications.
  • Interaction via dependencies is narrow but allows for direct access to another module’s API.
  • Interaction via pub/sub is broad and allows all subscribers of a topic to be notified at the same time.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset