While building HarvestIQ, I started paying much more attention to how backend architecture affects the way a product can be built, deployed, and scaled.
HarvestIQ is an agriculture platform designed to help smallholder farmers access post-harvest intelligence, including market prices and buyer opportunities.
One of the interesting parts of the project is that a farmer can interact with the system through USSD, without requiring a smartphone or an internet connection.
The backend is built around a serverless architecture using AWS services.
This article breaks down how that architecture works and what I have learned from building it.
What does "serverless" actually mean?
The name can be misleading.
Serverless does not mean that there are no servers.
The servers still exist. AWS manages the underlying infrastructure, while I focus on writing and deploying the application code.
Instead of maintaining an always-running backend server, different parts of the application can run when they are needed.
For HarvestIQ, this works particularly well because many operations are event-driven.
A farmer makes a request.
A function executes.
Data is retrieved or updated.
The function finishes.
Another event can trigger another function later.
HarvestIQ's Serverless Architecture
The simplified architecture looks like this:
Farmer
|
USSD
|
v
Africa's Talking
|
v
AWS Lambda
/ \
/ \
v v
DynamoDB Response
EventBridge
|
v
AWS Lambda
|
v
Price / Alert
Logic
|
v
SNS
|
v
SMS
|
v
Farmer
Each AWS service has a specific responsibility.
- AWS Lambda handles application logic.
- DynamoDB stores application data.
- EventBridge triggers scheduled operations.
- SNS handles notifications.
- Africa's Talking provides the USSD and SMS connectivity layer.
Let's look at each part.
1. USSD as the Entry Point
One of the design goals of HarvestIQ is accessibility.
A farmer should not necessarily need:
- A smartphone
- A mobile application
- Mobile data
- A web browser
A basic mobile phone with cellular connectivity can be enough.
The farmer interacts with HarvestIQ through a USSD menu.
For example:
HarvestIQ
1. Check Market Price
2. List Produce
3. View Buyer Demand
4. Set Price Alert
The USSD request is handled through the Africa's Talking USSD gateway and passed to the backend.
This is where AWS Lambda comes in.
2. AWS Lambda
AWS Lambda is the compute layer of the application.
Instead of maintaining a traditional server that is continuously running, I can deploy functions that execute in response to events.
The basic idea is:
Event
|
v
Lambda Function
|
v
Execute Code
|
v
Return Result
For example, when a farmer sends a USSD request:
Farmer
|
v
USSD Request
|
v
AWS Lambda
|
v
Process Request
|
v
Return USSD Response
The Lambda function can also communicate with other AWS services.
For example:
Lambda
|
+----> DynamoDB
|
+----> SNS
This means the application logic does not have to live on one large traditional backend server.
3. DynamoDB
HarvestIQ uses Amazon DynamoDB as its database.
This is where application data can be stored and retrieved by Lambda functions.
For example, the system can store information related to:
- Farmers
- Produce listings
- Market prices
- Buyer demands
- Price alerts
A simplified interaction might look like this:
USSD Request
|
v
Lambda
|
v
DynamoDB
|
v
Retrieve Data
|
v
Lambda
|
v
USSD Response
For example, when a farmer requests the current price of maize, Lambda can query DynamoDB and return the relevant information through the USSD session.
4. EventBridge for Scheduled Jobs
Not everything in HarvestIQ starts with a farmer.
Some operations need to happen automatically.
For example, market prices may need to be updated periodically.
This is where Amazon EventBridge becomes useful.
Instead of keeping a server running and writing a program that constantly waits for the next scheduled operation, EventBridge can trigger a Lambda function according to a schedule.
Conceptually:
EventBridge
|
| Scheduled Event
v
Lambda
|
v
Run Price Update
|
v
DynamoDB
For example:
8:00 AM -> Lambda runs
10:00 AM -> Lambda runs
12:00 PM -> Lambda runs
2:00 PM -> Lambda runs
The exact schedule depends on the application requirements.
This is one of the things I found interesting about event-driven architecture: the system does not need to continuously run code just to wait for something to happen.
5. Understanding Amazon SNS
This was one of the concepts I found particularly interesting.
Amazon SNS (Simple Notification Service) is a publish/subscribe messaging service.
A simple way to think about SNS is as a notification or broadcast layer.
Instead of having every part of the application know exactly how to deliver a notification, an application can publish a message to an SNS topic.
Conceptually:
Application
|
v
SNS Topic
/ \
/ \
SMS Other Subscribers
In HarvestIQ, this can be useful for price alerts.
Suppose a farmer sets a target price:
Produce: Maize
Target Price: ₦85,000
Later, the system checks the current market price.
If the target condition is satisfied, the application can publish a notification through SNS.
The flow becomes:
EventBridge
|
v
Price-check Lambda
|
v
DynamoDB
|
v
Target condition reached
|
v
Lambda publishes notification
|
v
SNS
|
v
SMS notification
|
v
Farmer
The important distinction is that SNS is not the component deciding whether the target price has been reached.
The application logic does that.
Lambda determines that the condition has been satisfied, then publishes the notification to SNS.
SNS handles the notification delivery to its configured subscriber.
That separation of responsibilities makes the architecture easier to reason about.
A Complete Example
Let's put everything together.
Imagine a farmer wants to know the current price of maize.
Step 1: The farmer sends a USSD request
Farmer
|
v
USSD
Step 2: The request reaches the backend
USSD
|
v
Africa's Talking
|
v
AWS Lambda
Step 3: Lambda retrieves the data
Lambda
|
v
DynamoDB
DynamoDB returns the relevant market information.
Step 4: Lambda generates the response
DynamoDB
|
v
Lambda
|
v
USSD Response
|
v
Farmer
The entire interaction can happen without the farmer installing an application or using mobile data.
Now consider a different scenario: a scheduled price update.
EventBridge
|
v
Lambda
|
v
Fetch / Process Price Data
|
v
DynamoDB
And if a price alert condition is reached:
Price-check Lambda
|
v
Target reached
|
v
SNS
|
v
SMS
|
v
Farmer
This is the part of the architecture that makes the system event-driven.
Why Use Serverless?
There are several reasons this architecture made sense for HarvestIQ.
1. Less infrastructure management
I don't have to maintain an always-running application server myself.
AWS manages much of the underlying infrastructure.
2. Event-driven execution
Different parts of the application can execute in response to specific events.
For example:
USSD request
↓
Lambda
or:
Scheduled event
↓
Lambda
or:
Business condition
↓
Notification
3. Automatic scaling
Lambda can handle multiple invocations without me manually provisioning individual servers for each request.
The underlying infrastructure is managed by AWS.
4. Efficient use of compute
For workloads that are intermittent or event-driven, there is less reason to maintain an application server that is continuously running just waiting for requests.
Serverless Is Not Perfect
Serverless is not automatically the best architecture for every application.
There are trade-offs.
Cold Starts
A function that has not been invoked recently may experience additional startup latency depending on the runtime and configuration.
For some applications, that latency matters.
Execution Limits
Lambda functions are designed for bounded workloads rather than indefinitely running processes.
That means some workloads are better suited to other architectures.
Distributed Debugging
A traditional application might look like:
Client
|
v
Backend
|
v
Database
A serverless application can involve many managed services:
USSD
|
v
Gateway
|
v
Lambda
|
+----> DynamoDB
|
+----> SNS
|
+----> Other Services
EventBridge
|
v
Lambda
When something fails, understanding exactly where the failure occurred can require proper logging, monitoring, and tracing.
Vendor Lock-in
Using managed services deeply can also make an application more dependent on a particular cloud provider.
That is an architectural decision worth considering before building at scale.
What I Learned
Building HarvestIQ changed how I think about backend architecture.
The biggest lesson for me is that serverless is less about "not having servers" and more about changing who manages the infrastructure and how application components are executed.
I also learned to think in terms of events.
Instead of asking:
"What server should always be running?"
I started asking:
"What event should cause this piece of code to run?"
For HarvestIQ, those events can be:
Farmer makes a USSD request
↓
Lambda executes
Scheduled update occurs
↓
Lambda executes
Price condition is reached
↓
Notification is published
That shift in thinking is probably the most valuable thing I have taken from building the project.
Conclusion
HarvestIQ started as an attempt to solve a real agricultural problem, but building it has also become an opportunity for me to learn more about cloud architecture and distributed systems.
The combination of Lambda, DynamoDB, EventBridge, and SNS gives the platform a relatively lightweight event-driven backend while allowing me to focus more on the application itself rather than server management.
I'm still learning, and there are several areas I want to explore further, particularly observability, security, cost optimization, and designing more robust distributed systems.
For me, this is one of the interesting parts of building software: the project solves one problem while teaching you how to solve the next one better.
Top comments (0)