Designing Windows Store Apps with HTML and JavaScript: An Overview
- 7/18/2014
- Objective 1.1: Design the UI layout and structure
- Objective 1.2: Design for separation of concerns
- Objective 1.3: Design and implement Process Lifetime Management (PLM)
- Objective 1.4: Plan for an application deployment
- Answers
Objective 1.2: Design for separation of concerns
Software development is still a relatively young profession. Although other disciplines have established well-defined and accepted rules for their craft, the rules of software development are still evolving.
This objective discusses the principles that the software industry has established in the last couple of decades. These principles revolve around building maintainable software that can be easily understood and extended. You will learn how to use layers to build your application and how Windows Metadata (WinMD) Components fit in the picture.
Planning the logical layers of your solution to meet application requirements
When doing some handiwork (or watching someone else do it) you probably use a variety of tools. Some tools, like a multi tool, can be used for a multiple scenarios. However, you can imagine scenarios where some parts of a multi tool become obsolete or broken. Changes to one element of your tool will affect other parts. Having dedicated tools with a specific goal often use fuller and requires less maintenance.
Maintenance and having a specific goal is also important for software design. But when building software, it’s a lot harder to recognize and avoid mistakes in these types of designs.
Separation of concerns (SoC) entails splitting a computer program into logical sections so that each section addresses a specific concern. SoC is important for creating maintainable applications that can be easily tested.
Typical examples of SoC are HTML, JavaScript, and CSS. Each performs a unique role in creating a webpage or app. HTML is the semantic structure of your page, CSS is the styling, and JavaScript adds client-side functionality.
You can mix these three concerns. For example, although you can add styling directly on your HTML elements, these designs are unnecessarily complex, hard to maintain, and difficult to extend.
How does SoC apply to your app? A typical app has to do multiple things: fetch data from somewhere, extract the necessary fields from it, and maybe perform some other validations on it before showing it on the screen. These tasks should not be plunged into one single monolithic object that spans your whole app. Instead, you should divide those tasks into separate areas and make them work together.
This process is called layering, and an application can consist of several logical layers. An application typically includes a UI layer, service layer, business layer, and data layer.
The layers have specific tasks. The business layer doesn’t have to know how to fetch data; that is the responsibility of the data layer. The data layer doesn’t know how the data is displayed on the screen; that is the work of the UI layer.
A typical diagram of this architecture is shown in Figure 1-9.
FIGURE 1-9 Diagram showing layering architecture
The diagram shows three typical layers stacked on top of each other. All layers also must address security and logging.
Before you start coding your app, you should have a reasonable idea of the different layers that you need in your application.
When you create a new app from one of the templates (the Hub App template, for example), you see that data.js is responsible for fetching data. In this way, you centralize all knowledge about data access to one location. Other parts of your app can call into the data object and use it without knowing anything about the specifics of your data storage mechanism.
Logical layers of an application differ from tiers. Maybe you have heard the term N-tiered application. A tier is a layer (or a couple of layers) that is physically separated from other layers. It might be a web service running somewhere in the cloud on Microsoft Azure that contains a piece of functionality from your app or from a database that stores the data for your app.
Logical layers can be placed on separate tiers, but it’s not a requirement to do so to achieve a well-designed app.
Designing loosely coupled layers
JavaScript as a language has its own particular challenges for designing large-scale applications. Languages such as C++ or C# implement a paradigm called object-oriented programming, which enables you to create small classes that are targeted at doing one single task. You can configure scope for objects, group them, and build your application this way.
JavaScript is a powerful language, but it wasn’t designed for building large applications. Ideas from object-oriented languages such as classes and modules are not built in to the JavaScript language.
Of course, you can just jump in and start developing your app without thinking too much about layering. However, as your app starts to grow, maintainability and the overall quality of your app will be negatively affected.
Avoiding global state
In JavaScript, everything you create is globally accessible by default, so every variable you declare can be modified anywhere in your code. Naming conflicts can occur when you declare the same variable twice, which can lead to unforeseen problems when a variable is modified somewhere in the application without an easy way to track the changes.
These conflicts don’t occur for variables and functions declared inside a function. JavaScript limits the scope of those objects to the scope of the function. This limitation enables you to implement the concept of private (which you can find in languages such as C#) in JavaScript.
To avoid global state and create private data, the default JavaScript files created by the Visual Studio templates wrap their content like this:
(function () { ... })();
What you see here is an anonymous, self-invoking function. The function is declared without a name, and it is immediately executed at the end of its declaration. This function allows you to scope all the items inside the function.
You don’t want to keep all items private; some functions or variables should be exposed. To help you, WinJS uses the concept of namespaces.
By using the WinJS.Namespace.define method, you can set a name for your namespace and configure which items are accessible:
var namespacePublicMembers = { clickEventHandler: button1Click }; WinJS.Namespace.define("startPage", namespacePublicMembers);
In this example, you define a namespace called startPage and expose a variable named clickEventHandler. This event handler points to the button1Click function defined inside your anonymous method.
Instead of having to use only plain functions and exposing them through namespaces, you can use WinJS to create classes. By using the WinJS.Class.define method, you can create a new class that has both behavior and data.
You can use the following plain JavaScript code to create a class-like object:
function Robot(name) { this.name = name; } Robot.prototype.modelName = '4500'; Robot.harmsHumans = false;
You create a class named Robot that expects a name on creation. It also has a modelName property that’s unique for each instance. The harmsHumans property is static, meaning that it is shared across all instances.
Instead of using this syntax, WinJS exposes a helper method called WinJS.Class.define. You can use the following code to create your Robot class:
var Robot = WinJS.Class.define( // The constructor function. function(name) { this.name = name; }, // The set of instance members. { modelName: "" }, // The set of static members. { harmsHumans: false }); var myRobot = new Robot("Mickey"); myRobot.modelName = "4500"; Robot.harmsHumans = false;
WinJS also gives you a helper method to implement inheritance, which is a concept of object-oriented development in which you define base classes and derived classes. You can create a hierarchy of classes that all share behavior and data, but can also add additional elements to their base class.
A classic example is found with animals. If you have a base class Animal, you can add elements to it such as IsAlive or Age. Now you can derive specific subtypes such as Mammal or Bird. They can add their own data such as IsWarmBlooded or Fly.
There are problems, however. Can all birds fly? How should you express that not all types of birds can fly? You can start adding checks to make sure you don’t execute a method that’s not implemented on the current class, but going down this road where not all subclasses support the methods defined on a base class leads to code that’s unmaintainable. Discussing all the fine-grained details of developing class hierarchies is outside the scope of this exam. If you start building more-complex applications, it pays to be familiar with object-oriented design concepts.
Using strict mode
JavaScript is usually very forgiving of the way you write your code. You can use a variable without ever declaring it, write to a read-only property, extend objects that are marked as not extensible, delete functions, or duplicate properties and other strange errors that won’t be immediately obvious when they start producing errors in your code.
Strict mode is a feature in JavaScript that you must explicitly enable. Enabling it results in better error-checking in your code to avoid the types of errors mentioned previously. You enable strict mode by adding the following line to your programs:
"use strict";
This line can be scoped to global scope (which applies to all code in your whole application, even external code), or you can use it inside a function that scopes it to the function.
Using TypeScript
JavaScript needs some help to become suitable for building large applications. Microsoft also noticed this lack, so it started developing TypeScript.
TypeScript is a superset of JavaScript that still compiles to plain JavaScript that can be used to run your apps. At development time, it adds extra features such as typing, classes, modules, generics, and inheritance.
These features significantly improve working with JavaScript. Look at the following TypeScript code:
class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; } } var greeter = new Greeter("world");
The class keyword, constructors, and property typing are elements that are added by TypeScript, which allows the compiler to give you much better support. It finds errors, helps you with IntelliSense, and works with other Visual Studio features that improve your workflow.
There is nothing that stops you from using TypeScript for building your Windows Store apps. There are even TypeScript definition files for the Document Object Model (DOM) and WinJS libraries. As a JavaScript developer, you should definitely consider using TypeScript.
Incorporating WinMD Components
When working on your Windows Store apps with HTML, JavaScript, and CSS, you call in to libraries defined in WinJS, which are built on the native C++ WinRT run time.
Calling in to a native dynamic-link library (DLL) from JavaScript is normally not supported. Microsoft put a lot of effort into creating an infrastructure that supports the interoperability of different languages to create apps for the Windows platform.
A regular C++ native component does not include metadata, which is necessary to create the correct mapping between the native components and the other languages. To make this work, Microsoft created a new file type named Windows Metadata (WinMD).
If you are running Windows 8, you can find these files in C:\Windows\System32\WinMetadata. The format of these files is the same as used by the .NET Framework for the Common Language Infrastructure (CLI).
WinMD files can contain both code and metadata. Those in your System32 directory contain only metadata, however. This metadata is used by Visual Studio to provide IntelliSense at design time. At run time, the metadata is used to signal that the implementation of all the methods found there is supplied by the run time, which is why the files don’t have to contain actual code; they make sure that the methods are mapped to the correct methods in WinRT.
When building JavaScript apps, you have the choice to implement part of your application in another language, such as C# or C++. Those projects can contain code that’s hard to implement in JavaScript while still integrating nicely with your app.
If you want to create your own WinMD assembly, create a WinRT Component in Visual Studio. The WinRT Component compiles down to a .winmd file that you can then use.
The following example shows some code that you can have inside your WinRT project:
namespace MyComponent { public sealed class MyClass { public int DoSomething(int x) { return x + 42; } } }
If you add a reference to your WinRT Component project in your app project, you can use the MyClass class from the C# project in the following way from your JavaScript code:
var myClass = new MyComponent.MyClass(); var value = myClass.doSomething(42);
This code calls in to the C# code and executes a method. The function name starts with a lowercase “d” in the JavaScript code. C# has the convention that element names should start with an uppercase character; JavaScript follows a convention in which each element starts with a lowercase character. This is why a method starting with an uppercase character in C# starts with a lowercase letter in JavaScript.
There are a couple of restrictions when you use WinRT Components:
- The fields, parameters, and return values of all the public types and members in your component must be WinRT types.
Public classes and interfaces can contain methods, properties, and events. A public class or interface can’t do the following, however:
- Be generic
- Implement an interface that is not a WinRT interface
- Derive from types that are not inside the WinRT
- Public classes must be sealed.
- Public structures can have only public fields as members, which must be value types or strings.
- All public types must have a root namespace that matches the assembly name and does not start with Windows.
Objective summary
- Dividing your application into distinct layers helps you create maintainable applications that are easier to extend.
- Typical layers are the UI, business, and data layers.
- When working with JavaScript, pay attention to how you structure your code to avoid some of the inherent JavaScript problems such as global state. You can use self-invoking anonymous functions to apply some scoping to your code.
- TypeScript is a superset of JavaScript that helps you write application-scale JavaScript.
- WinMD Components can be written in other languages such as C++ and C#, and can then be used from JavaScript Windows Store apps.
Objective review
Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter.
You are designing an app that connects to an external web service to load data for the app. This data is then processed and displayed on the screen. From which layer should the web service be called?
- Data layer
- Service layer
- Business layer
- UI layer
Should you avoid global state in JavaScript applications?
- No. Global state is easy because you can share data between different parts of your app.
- No. It’s not possible to avoid global state in JavaScript.
- Yes. Avoiding global state keeps your code free from unwanted side effects and aids maintainability.
- Yes. Global state is not possible in Windows Store apps.
Which elements can you use to build a layered application? (Choose all that apply.)
- WinJS.Class.define
- WinJS.Namespace.define
- WinMD Components
- Web services