Robert Reppel
Robert Reppel
Mar 8, 2019 4 min read

Storystreaming by Example: Event Storming a Bike Sharing App

thumbnail for this post

Early-morning cold rain darkness misery. Walk to the coffee shop or … look, is that a bike share? SIGN UP, download the app RIGHT HERE RIGHT NOW and GO. Push the UX, indulge that geek, REALLY push it, nothing like cold-fingered drizzle, street lights on puddles, fiddling phone and credit card, User Experience my !@!@$$…

And reverse-engineer the bike sharing app by event storming it with StoryStream at the same time.

StoryStream is our event storming tool, based on something we’ve used in-house for a while. It’s definitely in alpha. Very much in alpha. It’s what I was going to work on when I get to that coffee shop. So why not start now … 

Event storming is great for understanding the behaviour of both existing and new systems. It avoids crossed wires and gets everybody on the same page.

So let’s reverse-engineer a bike sharing app.

Shout-out to the folks at uBicycle. It’s great to see more riding happening in the Vancouver area and bike sharing is a big part of that!

Let’s establish the initial version of the “happy path” (= the path usually taken by most types of users in the ordinary run of business) from the bike share company configuring locations and stocking them with bikes, to users registering, riding, eventually un-registering, and full circle to admins removing bike locations. 

We find that covering the deletion/removal/unregister workflows early on as part of building a minimum viable product has many benefits because being able to go all the way from an “empty database” to a fully working system and back to an “empty database” uncovers many potentially bug-prone edge cases and encourages better testing.

From beginning to end, our “happy path” for the bike sharing system consists of the following workflows:

  • Set up Bike Sharing Locations
  • Register User
  • Ride
  • User Deletes Account
  • Remove Bike Sharing Location

Let’s walk through them one by one:

Set up Bike Sharing Locations

The initial setup to get started in a new city where the bike share service is offered: We need to identify the places where bikes are kept and stock them with bicycles.


/// Locations

Set Up Bike Sharing Location-> : locationId, lat, long, name
Location Added
Locations* locationId, lat, long, name

Add Bike To Location-> bikeId, locationId
Bike Added : bikeId, locationId
// "Bike Added" could happen many times, depending on how many bikes are kept at this location.

Location* : list of bikes at location

Register User

We are live! Users can register. We take a deposit. They are good to ride!

/// Register User

Register-> email
Registration Started : userId, email
Pending Registrations*

Confirm Registration-> confirmationCode
Registration Confirmed : userId
Confirmed Registrations*

Set Password-> userId, password
Password Updated : passwordHash

Add Payment Method-> 
Payment Method Added : creditCardToken, last4Digits
My Profile* : userId, email, creditCardToken, last4Digits

Charge Deposit-> 
Payment Processed
Deposit Received
// Deposit received only occurs if payment processing succeeded.The "Charge Deposit" command may be (e.g.) issued by a process controller "microservice" which issues it as a result of having received "Registration Confirmed" and "Payment Method Added" events.



Cyclists can now unlock bikes and ride.

/// Ride

Unlock Bike-> locationId, bikeId
Bike Unlocked : bikeId
Ride Started : rideId, bikeId
Rides In Progress* : 
Bike Removed : locationId, bikeId

Lock Bike-> bikeId, bikeLocationLat, bikeLocationLong
Bike Locked
Bike Returned
// Only mark bike as "returned" if it's within 10 meters of a location.
Bike Added: bikeId, locationId


User Deletes Account


/// User deletes account

Rides in Progress* : userId, bikeId, startLocationId, startTime
// Registration cannot be deleted if a user is currently on a ride
Delete Registration-> userId
Deposit Refunded : userId, amount, date
Registration Deleted

Remove Bike Sharing Location

/// Remove bike sharing location

Remove Location-> locationId=123423
Bike Removed : locationId=123423, bikeId
Bike Added : locationId=123456, bikeId
Location Removed : locationId
// "Bike Removed" for each bike at the location which is being removed (123423), followed by "Bike Added" for to another location, e.g. 123456. Maybe one location (e.g. 123456) could be the depot or maintenance location? 



You have seen an example of the ESL event storming language in action. In reality we’d explore workflows other than the “happy path” to understand edge cases and user experiences better, for example:

  • What happens if a rider does not return the bike to one of the designated locations? 
  • What if payment processing of either deposit or rental time charges fails?
  • How about riders deleting their account immediately after unlocking a bike but before they return it?

We have used ESL as a requirements modeling language in a number of different client projects in the last year or so, all of them CQRS/event sourcing based, with good results. It has evolved a bit during that time and will no doubt continue to do so, but the current version was sufficiently expressive for the Real Life systems we have used it for at this point. The advantage over Post-Its is that we get a nice digital, versionable, shareable and editable artifact suitable for use with assorted tooling.