DEV Community

Cover image for Building a Serverless Agriculture Platform with AWS
Toluwanimi Alfred
Toluwanimi Alfred

Posted on

Building a Serverless Agriculture Platform with AWS

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

For example, when a farmer sends a USSD request:

Farmer
   |
   v
USSD Request
   |
   v
AWS Lambda
   |
   v
Process Request
   |
   v
Return USSD Response
Enter fullscreen mode Exit fullscreen mode

The Lambda function can also communicate with other AWS services.

For example:

Lambda
   |
   +----> DynamoDB
   |
   +----> SNS
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

For example:

8:00 AM  -> Lambda runs
10:00 AM -> Lambda runs
12:00 PM -> Lambda runs
2:00 PM  -> Lambda runs
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

In HarvestIQ, this can be useful for price alerts.

Suppose a farmer sets a target price:

Produce: Maize
Target Price: ₦85,000
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Step 2: The request reaches the backend

USSD
   |
   v
Africa's Talking
   |
   v
AWS Lambda
Enter fullscreen mode Exit fullscreen mode

Step 3: Lambda retrieves the data

Lambda
   |
   v
DynamoDB
Enter fullscreen mode Exit fullscreen mode

DynamoDB returns the relevant market information.

Step 4: Lambda generates the response

DynamoDB
   |
   v
Lambda
   |
   v
USSD Response
   |
   v
Farmer
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

And if a price alert condition is reached:

Price-check Lambda
       |
       v
Target reached
       |
       v
SNS
       |
       v
SMS
       |
       v
Farmer
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

or:

Scheduled event
     ↓
Lambda
Enter fullscreen mode Exit fullscreen mode

or:

Business condition
     ↓
Notification
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

A serverless application can involve many managed services:

USSD
  |
  v
Gateway
  |
  v
Lambda
  |
  +----> DynamoDB
  |
  +----> SNS
  |
  +----> Other Services

EventBridge
  |
  v
Lambda
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)