← Publish / subscribe lesson
Lab

Publish / subscribe

Sign in to run your code

Read the brief and the starter here. With a free account you can edit it, run the sample, step through it and check your work, and your progress is kept.

// A scan on a topic: the topic is what happened, to which parcel.
type Message = { topic: string; parcel: string };

// A subscription: the topics it wants, and its own inbox of unread copies.
type Subscription = {
  id: number;
  topics: string[];
  inbox: Message[];
};

// The depot's broker: scanners publish to it, the depot's screens and
// services subscribe to it, and none of them know each other.
class Broker {
  nextId = 1;
  subscriptions: Subscription[] = [];

  subscribe(topics: string[]): number {
    const id = this.nextId++;
    this.subscriptions.push({ id, topics, inbox: [] });
    return id;
  }

  // Leaves a copy in every subscription that wants this topic.
  publish(topic: string, parcel: string) {
    for (const s of this.subscriptions) {
      if (!s.topics.includes(topic)) continue;
      s.inbox.push({ topic, parcel });
    }
  }

  // Hands one subscriber its unread copies, oldest first, and empties its inbox.
  drain(id: number): Message[] {
    const s = this.subscriptions.find((s) => s.id === id);
    if (!s) return [];
    const messages = s.inbox;
    s.inbox = [];
    return messages;
  }
}

// The notifier texts the customer when a parcel is delivered or fails,
// through an SMS gateway that is sometimes down.
class Notifier {
  gatewayUp = true;
  sent: string[] = [];
  text(parcel: string, event: string) {
    if (!this.gatewayUp) throw new Error('sms gateway down');
    this.sent.push(parcel + ' ' + event);
  }
  // Runs every few minutes.
  catchUp(broker: Broker) {
    // Nothing to read yet: the depot texts directly.
  }
}

// The depot: scanners at its doors report each parcel's scan here.
class Depot {
  broker = new Broker();
  notifier = new Notifier();

  // A scan: sorted, out (for delivery), delivered, or failed (delivery).
  scan(parcel: string, event: string) {
    if (event === 'delivered' || event === 'failed') {
      this.notifier.text(parcel, event);
    }
  }

  // Each service reads what is waiting for it.
  catchUp() {
    this.notifier.catchUp(this.broker);
  }
}