Skip to content
Yeonuk Hwang
Go back

Journey to Serverless (Part 1): The Initial Server-Based Architecture

Table of Contents

Open Table of Contents

1. Introduction

In July 2024, I received a project request from a client in the advertising industry. They needed a system to manage influencers for their advertising campaigns—a tool to search, update, and add influencer information efficiently. Their previous internal system was no longer maintained, forcing employees to manage everything manually using spreadsheets. Having worked together on several projects before, they reached out to me for help.

This project began as a Server-Based Architecture but was recently migrated to a Serverless Microservice architecture using AWS Lambda as a main compute resource. This series documents the journey from initial implementation to Serverless migration, sharing lessons learned and insights gained along the way.

This first part covers the initial requirements, architectural decisions, and implementation details of the server-based system.

2. Requirements Analysis

Based on client meetings, here are the core requirements:

2.1. Functional Requirements

The system centers on influencer management across two platforms:

  1. Basic CRUD Operations: Add, update, delete, and search influencers
  2. Multi-Platform Support: The company manages influencers on two main platforms (Platform A and Platform B), with each influencer potentially having accounts on both platforms
  3. Platform-Specific Operations: All CRUD operations must be performed on a per-platform basis
  4. Cross-Platform Reference: When viewing a Platform A account, users should see associated Platform B account names of the same influencer, and vice versa
  5. Automated Metrics Collection: Both platforms provide metrics (e.g., daily visitor count, follower count) to measure account activity and influence. These metrics should be collected automatically (daily or at regular intervals) and kept up-to-date
  6. Excel Integration:
    • Bulk import influencers via Excel file upload
    • Export search results to Excel format

2.2. Non-Functional Requirements

  1. Cost Optimisation: As an internal tool with limited users and low expected traffic, minimising both development and operational costs was the top priority
  2. IP-Based Access Control: The system should only be accessible from the company’s office IP address. VPN access was excluded to reduce costs

3. Architecture Design Decisions

3.1. Technology Stack

Based on the requirements and constraints, I made the following technology choices:

Infrastructure: AWS

Infrastructure as Code: AWS CDK

Frontend: React + Vite

Backend: Nest.js

Database: PostgreSQL on RDS

Deployment: AWS S3 + CloudFront + ECS

Core Design Principle: Prioritise cost optimisation, even if it means some compromises on availability and security aspects.

3.2. System Architecture

Architecture Diagram

The diagram above shows the final architecture. Below, I’ll explain each component in detail.

3.3. Infrastructure Components

3.3.1. Networking

The custom VPC is divided into four subnets:

App Subnet (Public Subnet)

I initially planned a Private Subnet for backend servers, but the metric collection requirement created a challenge.

The Metric Collection Problem: The Nest.js backend running on ECS includes a cron job that executes daily to collect metrics from external platforms. This cron job is a background task within the same EC2 instance where the main application runs. Since it needs to make outbound requests to external platform APIs, the EC2 instances require internet connectivity.

Why Not Private Subnet with NAT Gateway? A Private Subnet with NAT Gateway would be the most secure approach, but NAT Gateways incur hourly charges. Given the cost-optimisation priority, I decided to use a Public Subnet with an Internet Gateway instead—accepting the trade-off of having public IPs on the backend servers in exchange for lower operational costs.

Implementation:

Public Subnet

Contains resources that accept incoming requests from the internet:

DB Subnet (Isolated Subnet)

Completely isolated from external internet:

Spare Subnet

Reserved for future scalability but currently unused.

3.3.2. Compute Layer

Backend (ECS on EC2)

To maximise free tier usage:

Secrets Management

Environment variables managed through AWS Secrets Manager (securely stores sensitive data like API keys and database credentials).

3.3.3. Database Layer

PostgreSQL on RDS (t4g.micro)

Data Modeling

The data model evolved significantly based on real usage:

Initial Design (1:1 Relationships)

To handle cross-platform references, I started with a three-table structure:

Evolution to 1:N Relationships

Initially, I designed the data model assuming each influencer had at most one account per platform. However, post-launch feedback revealed a different reality: many influencers managed multiple accounts on the same platform for different niches, brands, or purposes.

This required a data model refactor from 1:1 to 1:N relationships:

3.3.4. Frontend Distribution

S3 + CloudFront

3.3.5. Domain Management and SSL/TLS

Domain Management

To avoid Route53 costs, we used the client’s existing domain hosting service instead of setting up a new Route53 hosted zone. The client’s existing domain points to both ALB (backend) and CloudFront (frontend) via CNAME records.

SSL/TLS

3.3.6. CI/CD Pipeline

GitHub Actions

Automated deployment for both frontend and backend.

Security Evolution

I initially used IAM User credentials in GitHub Secrets. Later, I migrated to OIDC (OpenID Connect) with IAM Roles:

4. Implementation Challenges and Solutions

4.1. Challenge 1: New Feature Requests During Development

Two major features were added:

Platform A Comment Analysis

  1. Retrieve comment data from Platform A posts
  2. Export analysis results to Excel including:
    • Comment content
    • Commenter metrics (e.g., daily visitor count, same as Platform A profiles)
    • Relevant URLs identified in comments

Platform A Post Verification

  1. Upload Excel with Platform A profile URLs and post links
  2. Download validation results including:
    • Platform A metrics (for each profile URL in the uploaded Excel)
    • Post validity status (confirms the post is still published and publicly accessible without authentication—not deleted or restricted)

Solution: Implemented as separate modules within the Nest.js application using a modular monolith structure. The existing Platform A metric collection logic was extracted into a shared module that both the Comment Analysis and Post Verification features could reuse, avoiding code duplication and ensuring consistent metric collection across features.

4.2. Challenge 2: Platform B Metrics Collection

Platform B metrics could be collected less frequently, but accessing them from cloud infrastructure was unreliable. The platform’s API uses strict IP-based rate limiting.

The Challenge: The existing EC2-based cron job (running in the App Subnet) was hitting rate limits. I initially hypothesised that Lambda functions, by spinning up new execution contexts, might receive different IP addresses and bypass the rate limiting.

Attempted Solutions

1. Lambda Functions

Hypothesis: Each Lambda invocation with a new context might get assigned a different IP address, allowing us to distribute requests and avoid rate limiting.

Result: Proved unsuccessful. While Lambda does use AWS IP ranges, we discovered that AWS’s IP addresses face much stricter rate limiting than residential IPs. The platform’s API applies aggressive rate limiting to all AWS IP ranges.

2. Proxy Services

Third-party proxy services (which use residential IPs) could provide more generous rate limiting, but they require ongoing subscription costs, contradicting the cost-minimisation priority.

Result: Discarded due to ongoing costs.

Key Discovery: During testing, we found that the office network’s residential IP faced significantly less strict rate limiting compared to AWS IP ranges.

Final Solution: Desktop Application

Since the office network’s residential IP allowed reliable access, I built an Electron desktop app that runs locally:

Trade-offs

Advantages: Zero cost, leverages office network’s stable IP, reliable access

Disadvantages: Manual trigger (not automated), requires employee action

Since Platform B updates less frequently, this trade-off was acceptable.

Deployment: Built with Electron and distributed via S3.

5. Conclusion

This post covered the requirements and server-based architecture for the influencer management system. As a solo project, I prioritised cost optimisation and maximising AWS free tier usage.

6. Next in the Series

Part 2: The Cost Crisis and Path to Change covers the critical moment when AWS Free Tier ended:


Series Navigation


Share this post:

Previous Post
Journey to Serverless (Part 2): The Cost Crisis and Path to Change