CampusG - Food Delivery Platform for Schools

Project grade received: "A+"
Overview
Developed as part of our Enterprise Solution Development (ESD/IS213) module, CampusG is a microservices-based application that hosts a campus food delivery service.
Our platform connects customers placing orders with runners who deliver them.
Demo:
Landing Page & Sign-in Flow

Role-Selector (Image)
We colour coded the roles to be blue for placing orders, and green for delivering orders.

Placing an Order:
After adding the order details, the place order button will trigger the "Create Order Saga". It sends off the work to the backend, and returns a HTTP 202 Code (Accepted). The user is loaded to the order history page to view the order details at a glance.

Accepting an Order & Receiving Status Updates:
Via the green profile (runner), accepting an order via the available orders page will allow the runner to fulfil the order, and provide updates on the status of the order. The accept order button on the initial frame triggers the "Accept Order Saga".

Completing an Order:
After going through all the required status updates, the runner can confirm the delivery of the item, triggering the "Complete Order Saga".

User's Dashboard (Image):
Users can see their expenditure and their earnings, as well as make profile adjustments. Here, users can also add their payment method.

Technical Breakdown
System Architecture & Tech Stack
The following table summarizes the core components of CampusG, the technologies used, and their respective roles within the system:
Component | Technology / Tool | Role / Description |
---|---|---|
Frontend | React + Vite (Bolt.new) | User interface for customers and runners |
User Service | Flask, Clerk Auth, Stripe API | Profile management, authentication, and payment integration |
Order Service | Flask | Manages order lifecycle and states |
Payment Service | Flask, Stripe API | Handles payment processing and fund transfers |
Notification Service | Kafka, Grafana | Event logging and monitoring |
Timer Service | Outsystems (REST API) | Manages order timeouts and cancellations |
Message Broker | Kafka | Event-driven communication between microservices |
Workflow Orchestrator | Temporal | Orchestrates Accept/Complete Order sagas |
Database | PostgreSQL (per microservice) | Persistent storage for each microservice |
Containerization | Docker | Service isolation and deployment |
External Services | Clerk, Stripe, Outsystems | Authentication, payments, and timer management |
Saga Orchestrators
1. Create Order Saga (Flask/Kafka)
Coordinates the order creation flow through Kafka events, interfacing with Order, User, and Payment services, and communicating with the Timer Service via HTTP. Handles compensation actions for failures.
Step | Action |
---|---|
1 | Customer places order |
2 | Saga coordinates order creation |
3 | Retrieves user payment info |
4 | Authorizes payment via Stripe |
5 | Updates order status |
6 | Starts timer (Outsystems) |
7 | Handles failures with compensation (e.g., cancels order if payment fails) |

2. Accept Order Saga (Temporal)
Manages the runner order acceptance workflow using Temporal activities to update the Order Service and Timer Service, with compensation logic for failures.
Step | Action |
---|---|
1 | Runner accepts order |
2 | Saga updates order status (assigns runner) |
3 | Notifies Timer Service (Outsystems) |
4 | Handles failures with compensation (e.g., reverts order status) |


3. Complete Order Saga (Temporal)
Orchestrates order completion and payment flows, coordinating between Order, User, and Payment services, and managing fund release to runners via Stripe. Handles failure scenarios with compensation actions.
Step | Action |
---|---|
1 | Runner marks order as completed |
2 | Saga updates order status |
3 | Retrieves runner payment info |
4 | Retrieves payment ID |
5 | Releases funds (Stripe payout) |
6 | Updates final order status |
7 | Handles failures with compensation (e.g., reverts payout/status) |

4. Order Timeout Process
Handles automatic order expiration and cancellation if not accepted within a set time frame.
Step | Action |
---|---|
1 | Timer Service polls every 5 minutes to check if 30 minutes have passed since order creation. |
2 | If expired, marks the order as expired. |
3 | Create Order Saga polls every 1 minute for expired orders. |
4 | If found, initiates order cancellation via Kafka. |
5 | Order Service processes cancellation and confirms via Kafka event. |

Why Saga?
While planning the project, we discovered a post from AWS on the Saga Orchestration Pattern.

Link to diagram from the AWS documentation
In fact, this diagram that closely resembled the steps our services would need to go through, as it involved an ordering process where issues could occur across multiple services with various data states to manage.
Additionally, as most of the team lacked Java experience, we sought Python-based tools to assist with this implementation. We initially used a Pub-Sub system with Kafka to implement one of our Sagas (orchestration via Saga but with choreography-style execution). Later, we discovered a orchestration tool called Temporal and implemented two additional Sagas with it, more closely resembling the AWS Step Functions diagram.
We opted against AWS Step Functions as we were building our solution to run locally, and developing a fully cloud-hosted solution seemed daunting at that point.
The Saga pattern helped us manage complex distributed transactions like order creation, acceptance, and completion/payment. Without it, we would have needed to account for numerous edge cases and engineer multiple flows to handle all types of errors.
Further Improvements
To further enhance our project, several real-world development considerations could be incorporated. For example, introducing an API gateway for our APIs and microservices would provide a unified entry point, enabling advanced features such as rate limiting, API versioning, and secure, permission-based access control.
Implementing an API gateway would have deepened our understanding of these core concepts. While we explored the fundamental capabilities of API gateways in class, integrating one into our project was considered Beyond-the-Lab (BTL) and thus not included in the initial scope.
Additionally, deploying the application to the cloud would enable a live demonstration accessible to all users. This would require further expertise in cloud deployment of microservices, as well as integration with cloud-based message brokers and saga orchestrators.