Skip to content

Marketplace

This tutorial demonstrates how to build a marketplace using Python microservices and a VueJS Web App. The goal is to showcase the ease of setting up an application network with Bondy, where components communicate using RPC and Publish/Subscribe.

Objective

In this tutorial, we will build a marketplace where individuals can buy and sell items.

The objective is to showcase how fast and simple it is to set up an application network with Bondy. In this network, all components communicate using RPC and Publish/Subscribe.

Marketplace Architecture

Domain Model

The demo models the following actors:

Marketplace

The place where sellers and buyers congregate to exchange goods.

Seller

A seller sells an item at a given starting price for a given period of time, i.e. people can bid on the item until it times out.

There are 2 possible outcomes once the end of the sell period is reached:

  • There were no bids, the item just expired. No deal happened.
  • There was at least one bid higher than the initial price. The higher bidder wins. We have a deal.
Buyer

A buyer tries to buy an item by bidding until the sell period ends.

Bid

An offer (a certain price) for an item listed on the marketplace. Only bids higher than the current highest price are accepted.

Technical View

The architecture design of the example application is depicted in the following diagram.


The diagram shows the following components:
Market

A Python microservice implementing a simple marketplace.

Uses AutobahnPython, WAMP client to open a session on com.example.demo realm in Bondy and registers the WAMP procedures URIs (RPCs) the Web App and Bot instances will use to sell and buy goods.

Bot

A Python microservice that creates named bots (via its CLI). Once a bot is created it will automatically bid for items.

Uses AutobahnPython

Configuration

The demo spawns 5 bots that are configured to:

  • Bid on any items whose prices are lower than a given limit,
  • Increasing the price by a given amount
  • Taking a given amount of time to perform the bid, i.e. lag between computing the price and actually bidding. We use this lag is to have some bids from the bots rejected and slow down the demo to a more human friendly rhythm.
Web App

A single page Web App written in Javascript. Uses using VueJS and AutobahnJS (Browser).

Interactive CLI

An interactive command line interface written in Python and using the Autobahn Python WAMP client.

All components open a single WAMP session to Bondy on the com.market.demo realm.

Steps

Requirements

  • Docker (Docker Desktop in case you use macOS or Windows)
  • git
  • make

Make sure that Docker is running before you start.

Clone the Demo repository

bash
$ git clone https://github.com/bondy-io/bondy-demo-marketplace.git

Build and run the Demo

The following command uses Docker compose to download and/or build the images for the components mentioned before.

bash
$ make

This will result in the following Docker containers:

  • 1 instance of Bondy
  • 1 instance of the Marketplace Web App
  • 1 instance of the Market service
  • 4 instances of the Bot service, each one ready to bid (with names alice, tom, victor and mary)
Docker Dashboard showing all containers

Note

Bondy needs a few seconds to start and be ready to accept connections, this is because Bondy is validating and indexing the internal database. This can actualle be controlled via configuration.

From the Docker containers' logs you might notice microservices are trying to reconnect with logs like the following, do not worry, they will keep on retrying to connect to Bondy.

text
2022-11-07T15:37:51 Connection failed with OS error:
    ConnectionRefusedError: [Errno 111] Connect call
    failed ('192.168.16.2', 18080)
2022-11-07T15:37:51 trying transport 0 ("ws://bondy:18080/ws")
    using connect delay 2.2569295356372576

Join the marketplace

To sell an item and see the bots competing, just open the Web App from a browser pointing at http://localhost:8080.

Note

Again, since Bondy needs a few seconds to start, you may notice the Web App spinning (Loading... Please wait) before successfully connecting and printing No items to show.

Once the web app has loaded, click the button with user icon (top right corner) to setup yourself up as a market participant.

You should see something similar to the following screen capture:

The screen capture shows the market docker instance log window below the web app, you might want to do the same and open the logs to see what is going on, although the Web App will print all events as well.

Sell one or more items

Using the Web App again, click on the SELL ITEM button to sell an item.

Enter the following information (as shown in the screen capture below):

  • the name of the item e.g. Apple,
  • the initial price e.g. 0.5,
  • the number of minutes before the deal closes e.g. 2

In the following example we add an item called apple. You can repeat this operation multiple times to add several items.

Notice that once we enter the item the app receives a notification (Publish/Subscribe event) shown in the green banner below.

See the bots bidding for your items

Once you click on Save, you'll see the bots starting to compete for the item, placing bids, unless your initial price is too high, i.e. more than $10,000.

You should see something similar to the capture below:

Under the hood

Now let see how this was done and what is happening.

Market

Connection to Bondy

The connection to bondy is performed through a component that requires a fairly light configuration. This component uses AutobahnPython, the WAMP client.

The client will handle the WAMP protocol interactions and let us focus on the business logic.

See the source code for the connection here.

python
class Market:
  def __init__(self):
    ab_component_config = create_autobahn_component_config(user_id="market")
    self._component = Component(**ab_component_config)
    self._component.on("join", self._on_join)

In line 3 we call a util function that returns the following dictionary:

python
{
    "realm": "com.market.demo",
    "transports": [
        {
            "type": "websocket",
            "url": "ws://localhost:18080/ws",
            "serializers": ["json"]
        }
    ],
    "authentication": {
        "cryptosign": {
            "authid": "market",
            "privkey": PRIVATE_KEY
        }
    }
}

This tells Autobahn to open a websocket session with Bondy attached to the com.market.demo realm and using WAMP Cryptosign authentication.

Note

The com.market.demo realm is configured by the Make target responsible to running the Bondy docker instance.

RPC Registration

Once connected, the _on_join method is called with the established session.

The market registers 6 RPCs under the following URIs:

Once it has established a session to Bondy the com.example.demo realm, registers the following RPCs.

  • com.market.bidder.add - Add a user as bidder
  • com.market.bidder.gone - Remove a user as bidder
  • com.market.get - List all the items for sale
  • com.market.item.bid - Place a bid on a listed item
  • com.market.item.get - Return an item's details
  • com.market.item.sell - List a new item for sale.

A registration is performed by simply calling the register method on the session object giving the callback function and the procedure URI.

For example:

See the source code for the registrations here.

python
def _on_join(self, session, details):
  session.register(self._get_items, "com.market.get")
  ...

Publications

Once the marketplace is connected and ready to accept items and bids, it has to notify all the other components connected to the same realm about certain events.

The market will publishe events under the following WAMP topics (Publish/Subscribe):

  • com.market.opened - When market is connected and has registered the RPC URIs, it publishes this topic to let the listeners it is ready to accept calls.
  • com.market.item.added - When a new item is on offer.
  • com.market.item.expired - When an item times out without any bids.
  • com.market.item.new_price - When a bid was accepted.
  • com.market.item.sold - When an item times out with a winner.

This is performed by calling the session's object publish method.

In the following example no arguments are needed since the event has no payload, but some can be provided in a more general case.

See the source code for the publications here.

python
def _on_join(self, session, details):
    ...
    # MARKET_OPENED resolves to "com.market.opened"
    session.publish(MARKET_OPENED)

Web App

The Web App subscribes to several topics and calls the WAMP procedures on the market to perform actions like the ones you've done already: add a new participant and sell and item.

You can also bid for item using the item button at the end of the row.

Bots

Similarly the bots connect to Bondy with their own private crytpsign key. However, upon successful connections, the bots will only subscribe to the com.market.opened topic and wait until the marketplace is open before trying to bid on items.

The subscription is simply done by calling subscribe on the session:

See the source code for the bots here.

python
def _on_join(self, session, details):
    session.subscribe(self._on_market_opening, "com.market.opened")

Once the marketplace is open, the bots will perform several important tasks, including:

  • Join the marketplace by identifying themselves.
    • Making a call to com.market.bidder.add
  • Query the marketplace for all currently available items
    • Making a call to com.market.get
    • Making a call to com.market.item.get
  • bid on any item that are under $10,000.
    • Making a call to com.market.item.bid
  • Subscribe to com.market.item.added to be notified every time a new item is on sale.
  • Subscribe to com.market.item.new_price to be notified every time there is a new big on an item.

Making an RPC call is done by calling the call method on the session object.

For instance when the bot Alice joins the marketplace, it calls:

python
await session.call("com.market.bidder.add", "Alice")

Note

This is an asynchronous call because the bot has to know if the call was successful and it was accepted in the marketplace. Similarly, bidders have to wait for the bid to return to know if it was accepted or rejected.

For more...

The snippets here were simplified to keep the tutorial simple but for the full picture and more detail, please have a look at the code at the GitHub bondy-demo-marketplace repo.

Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution-ShareAlike (CC-BY-SA) 4.0 International license.
Bondy and Leapsight are registered trademarks of Leapsight Technologies Ltd.