Angular 7: Data Architecture with Observables - Part 1: Services
Observables and RxJS
In Angular, we can structure our application to use Observables as the backbone of our data architecture. Using Observables to structure our data is called Reactive Programming.
But what are Observables, and Reactive Programming anyway? Reactive Programming is a way to work with asynchronous streams of data. Observables are the main data structure we use to implement Reactive Programming. But I’ll admit, those terms may not be that clarifying. So we’ll look at concrete examples through the rest of this chapter that should be more enlightening.
Note: Some RxJS Knowledge Required
I want to point out this book is not primarily about Reactive Programming. There are several other good resources that can teach you the basics of Reactive Programming and you should read them. We’ve listed a few below.
Consider this chapter a tutorial on how to work with RxJS and Angular rather than an exhaustive introduction to RxJS and Reactive Programming.
In this chapter, I’ll explain in detail the RxJS concepts and APIs that we encounter. But know that you may need to supplement the content here with other resources if RxJS is still new to you.
Use of Underscore.js in this chapter
Underscore.js is a popular library that provides functional operators on JavaScript data structures such as Array
and Object
. We use it a bunch in this chapter alongside RxJS. If you see the _
in code, such as _.map
or _.sortBy
know that we’re using the Underscore.js library. You can find the docs for Underscore.js here.
Learning Reactive Programming and RxJS
If you’re just learning RxJS I recommend that you read this article first:
After you’ve become a bit more familiar with the concepts behind RxJS, here are a few more links that can help you along the way:
Throughout this chapter I’ll provide links to the API documentation of RxJS. The RxJS docs have tons of great example code that shed light on how the different streams and operators work.
Do I have to use RxJS to use Angular? - No, you definitely don’t. Observables are just one pattern out of many that you can use with Angular. We talk more about other data patterns you can use here.
I want to give you fair warning: learning RxJS can be a bit mind-bending at first. But trust me, you’ll get the hang of it and it’s worth it. Here’s a few big ideas about streams that you might find helpful:
- Promises emit a single value whereas streams emit many values. - Streams fulfill the same role in your application as promises. If you’ve made the jump from callbacks to promises, you know that promises are a big improvement in readability and data maintenance vs. callbacks. In the same way, streams improve upon the promise pattern in that we can continuously respond to data changes on a stream (vs. a one-time resolve from a promise)
- Imperative code “pulls” data whereas reactive streams “push” data - In Reactive Programming our code subscribes to be notified of changes and the streams “push” data to these subscribers
- RxJS is functional - If you’re a fan of functional operators like
map
, reduce
, and filter
then you’ll feel right at home with RxJS because streams are, in some sense, lists and so the powerful functional operators all apply
- Streams are composable - Think of streams like a pipeline of operations over your data. You can subscribe to any part of your stream and even combine them to create new streams
Chat App Overview
In this chapter, we’re going to use RxJS to build a chat app. Here’s a screenshot:

Usually we try to show every line of code here in the book text. However, this chat application has a lot of moving parts, so in this chapter we’re not going to have every single line of code in the text.
You can find the sample code for this chapter in the folder code/rxjs/rxjs-chat
. We’ll call out each filter where you can view the context, where appropriate.
In this application we’ve provided a few bots you can chat with. Open up the code and try it out:
{lang=shell,line-numbers=off}
cd code/rxjs/rxjs-chat
npm install
npm start
Now open your browser to http://localhost:4200
.
Notice a few things about this application:
- You can click on the threads to chat with another person
- The bots will send you messages back, depending on their personality
- The unread message count in the top corner stays in sync with the number of unread messages
Let’s look at an overview of how this app is constructed. We have
- 3 top-level Angular Components
- 3 models
- and 3 services
Let’s look at them one at a time.
Components
The page is broken down into three top-level components:

ChatNavBarComponent
- contains the unread messages count
ChatThreadsComponent
- shows a clickable list of threads, along with the most recent message and the conversation avatar
ChatWindowComponent
- shows the messages in the current thread with an input box to send new messages
Models
This application also has three models:

User
- stores information about a chat participant
Message
- stores an individual message
Thread
- stores a collection of Messages
as well as some data about the conversation
Services
In this app, each of our models has a corresponding service. The services are singleton objects that play two roles:
- Provide streams of data that our application can subscribe to
- Provide operations to add or modify data
For instance, the UsersService
:
- publishes a stream that emits the current user and
- offers a
setCurrentUser
function which will set the current user (that is, emit the current user from the currentUser
stream)
Summary
At a high level, the application data architecture is straightforward:
- The services maintain streams which emit models (e.g.
Message
s)
- The components subscribe to those streams and render according to the most recent values
For instance, the ChatThreads
component listens for the most recent list of threads from the ThreadService
and the ChatWindow
subscribes for the most recent list of messages.
In the rest of this chapter, we’re going to go in-depth on how we implement this using Angular and RxJS. We’ll start by implementing our models, then look at how we create Services to manage our streams, and then finally implement the Components.
Implementing the Models
Let’s start with the easy stuff and take a look at the models.
User
Our User
class is straightforward. We have an id
, name
, and avatarSrc
.
import { uuid } from '../util/uuid';
/**
* A User represents an agent that sends messages
*/
export class User {
id: string;
constructor(public name: string,
public avatarSrc: string) {
this.id = uuid();
}
}
Notice above that we’re using a TypeScript shorthand in the constructor. When we say public name: string
we’re telling TypeScript that 1. we want name
to be a public property on this class and 2. assign the argument value to that property when a new instance is created.
Thread
Similarly, Thread
is also a straightforward TypeScript class:
import { Message } from '../message/message.model';
import { uuid } from '../util/uuid';
/**
* Thread represents a group of Users exchanging Messages
*/
export class Thread {
id: string;
lastMessage: Message;
name: string;
avatarSrc: string;
constructor(id?: string,
name?: string,
avatarSrc?: string) {
this.id = id || uuid();
this.name = name;
this.avatarSrc = avatarSrc;
}
}
Note that we store a reference to the lastMessage
in our Thread
. This lets us show a preview of the most recent message in the threads list.
Message
Message
is also a simple TypeScript class, however in this case we use a slightly different form of constructor:
import { User } from '../user/user.model';
import { Thread } from '../thread/thread.model';
import { uuid } from './../util/uuid';
/**
* Message represents one message being sent in a Thread
*/
export class Message {
id: string;
sentAt: Date;
isRead: boolean;
author: User;
text: string;
thread: Thread;
constructor(obj?: any) {
this.id = obj && obj.id || uuid();
this.isRead = obj && obj.isRead || false;
this.sentAt = obj && obj.sentAt || new Date();
this.author = obj && obj.author || null;
this.text = obj && obj.text || null;
this.thread = obj && obj.thread || null;
}
}
The pattern you see here in the constructor allows us to simulate using keyword arguments in the constructor. Using this pattern, we can create a new Message
using whatever data we have available and we don’t have to worry about the order of the arguments. For instance we could do this:
{lang=typescript,line-numbers=off}
let msg1 = new Message();
# or this
let msg2 = new Message({
text: "Hello Nate Murray!"
})
Now that we’ve looked at our models, let’s take a look at our first service: the UsersService
.
The point of the UsersService
is to provide a place where our application can learn about the current user and also notify the rest of the application if the current user changes.
This page is a preview of ng-book 2.
Get the rest of this chapter plus hundreds of pages Angular 7 instruction, 5 sample projects, a screencast, and more.
Ready to master Angular 7?
- What if you could master the entire framework – with solid foundations – in less time without beating your head against a wall? Imagine how quickly you could work if you knew the best practices and the best tools?
- Stop wasting your time searching and have everything you need to be productive in one, well-organized place, with complete examples to get your project up without needing to resort to endless hours of research.
- You will learn what you need to know to work professionally with ng-book: The Complete Book on Angular 7 or get your money back.
Download the First Chapter (for free)