I’d like to share ebike-go, a source-available collection of Go services for shared e-bike and pedal-assist bike operations:
https://github.com/wanghengwen/ebike-go
The project grew from real operational requirements for city bike-sharing programs and tourist attractions. From a rider’s perspective, the process is simple: scan, unlock, ride, and return. On the backend, however, every trip involves live device connections, location updates, parking validation, helmet checks, pricing, promotions, payments, and operational reporting.
This type of system has turned out to be a good match for Go.
Where Go fits in a shared e-bike system
1. Maintaining connections with a large number of bikes
Each bike has an IoT controller that reports information such as:
- GPS location
- Battery level and BMS data
- Lock and riding status
- Faults and alarms
- Smart helmet status
The backend also needs to send commands for unlocking, locking, powering the bike, finding a bike, or opening the battery compartment.
In ebike-go, the device gateway is implemented as a separate Go service. Go’s lightweight goroutines and networking support make it practical to manage many long-lived device connections without requiring a complicated threading model.
Device messages can arrive through TCP or MQTT. After protocol decoding, events are passed through Kafka to consumer and worker services. Redis is used for frequently accessed device state, while business events continue to the order and operations services.
The simplified flow looks like this:
Bike / IoT controller
|
TCP or MQTT
|
Go device gateway and protocol decoder
|
Kafka
|
Go consumers and workers
|
Redis, MySQL, and business services
This separation keeps connection handling and binary protocol decoding away from slower business operations.
2. Processing bursts of device events
Bike traffic is not evenly distributed. Morning commutes, tourist opening hours, promotions, and large return events can create sudden traffic spikes.
Go works well here because the services can process independent messages concurrently while keeping the code relatively straightforward. Device ingestion, command delivery, state updates, and asynchronous tasks are split into independently deployable services, so the busiest part can be scaled without scaling the entire platform.
Kafka provides buffering between live device traffic and downstream processing. This is important when bikes continue reporting data while a database or business service is temporarily slow.
3. Fast geofencing during unlock and return
Parking is one of the hardest operational problems in shared mobility.
Before unlocking or returning a bike, the platform may need to determine whether the current location is:
- Inside the service area
- Inside an approved parking zone
- Inside a no-parking or no-riding area
- Close enough to a designated station
These checks happen on the user-facing path, so latency matters. The geofencing service is written in Go and kept separate from general business APIs. This lets us optimize and scale geometry-heavy checks independently.
GPS alone is not always accurate enough. The business flow can also combine geofencing with Bluetooth beacons or RFID tags to confirm that a bike is parked in the correct place.
4. Keeping business domains independent
The repository is organized as multiple Go modules rather than one large application. The main service areas include:
- API gateway and authentication
- Customer and operator APIs
- Device gateway and protocol handling
- Device state consumers and background workers
- Geofencing
- Order, pricing, and promotion integration
- Operational analytics
- Notifications
- Open APIs for external systems
Most HTTP services use Gin. The supporting infrastructure includes Nacos, Redis, Kafka, MySQL, Docker, and Kubernetes.
Using multiple services does add deployment and observability work, so this structure is not intended as a recommendation for every Go project. In this case, the boundaries follow workloads that behave differently: long-lived connections, CPU-heavy geofencing, user-facing APIs, and asynchronous event processing.
5. Building small deployment units
Go’s static binaries and relatively fast startup are useful for containerized deployment. Each service can be built, deployed, restarted, and scaled independently.
This is particularly helpful for shared mobility, where traffic varies by region and time of day. A device gateway may need more capacity during a fleet rollout, while customer-facing APIs may peak during commuting hours.
Business capabilities built on top
The Go services support more than bike connectivity. The platform also covers:
- The complete scan, unlock, ride, and return process
- GPS geofences, Bluetooth parking beacons, and RFID parking points
- Smart helmet pairing, status checks, and return rules
- Configurable fares for different regions and vehicle types
- Ride passes, coupons, memberships, referrals, and promotional campaigns
- Multi-tenant operations
- Merchant accounts, revenue-sharing records, refunds, and revenue reports
The goal is to provide a practical foundation for organizations operating their own shared e-bike service, while keeping device communication and operational rules under their control.
What I’d like feedback on
I’m especially interested in hearing from Go developers who have worked on IoT, mobility, or real-time systems:
- How do you structure connection ownership and command delivery across multiple gateway instances?
- Which approaches have worked well for high-frequency geospatial checks in Go?
- Where would you draw service boundaries in a system that combines IoT traffic and transactional business flows?
- Which parts of this project would be most useful to document or provide as standalone examples?
Issues and pull requests are welcome, especially for bug fixes, documentation, observability, and clean example configurations.
Repository: https://github.com/wanghengwen/ebike-go