“Designed the architecture. Set up the infrastructure. Led the team.”
A 6-person team project to build a real-time coordination platform for flood rescue operations in Vietnam. As Tech Lead, my responsibility was architecture design, technology selection, full infrastructure setup, CI/CD pipeline, module skeleton (frontend + backend), and sprint management via Jira. The team builds features on top of the foundation I set up.
Flood rescue operations in Vietnam suffer from coordination failures: rescue requests get lost, teams can't be tracked in real-time, relief supplies are distributed without visibility, and there's no unified operations view for coordinators.
Citizens have no way to submit structured rescue requests or track status. Coordinators receive requests through informal channels (phone, Zalo) with no geographic data.
Coordinators can't see where rescue teams are in real-time. Assignment is done by phone call without proximity awareness.
Relief supply managers have no visibility into inventory levels, distribution, or when to restock.
5-layer architecture: Client → Nginx API Gateway → 6 Spring Boot Microservices → RabbitMQ + Kafka → MySQL per service. Full DevOps automation via GitHub Actions → Docker Hub → VPS.

Sprint 1 (4 Mar – 11 Mar): 6 of 10 tasks DONE, 1 IN PROGRESS. Core infrastructure and service skeletons are live.
Three decisions that defined the entire system design:
Why: A 6-person team starting with 6 independent microservices means 6× the DevOps complexity before any business logic is validated. We validate domain boundaries in Sprint 1 as a modular monolith, then extract services once the seams are clear.
Why: RabbitMQ gives exactly-once delivery for rescue task assignments — critical when one team must be assigned to exactly one request. Kafka gives a replayable audit log of all GPS positions and status changes — essential for post-incident analysis and reporting.
Why: Sharing a database across services creates invisible coupling that's painful to remove later. Dedicated MySQL per service enforces the contract at the infrastructure level from the start.
Each service owns its domain, its database, and its API. Communication via RabbitMQ (task events) and Kafka (audit streams). Nginx as reverse proxy. GitHub Actions → Docker Hub → SSH VPS on push to main.
Every push to main triggers build → Docker image push → SSH deploy to VPS. Zero manual steps.
name: Deploy to VPS
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build & push Docker image
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/flood-rescue:latest .
echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login \
-u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
docker push ${{ secrets.DOCKERHUB_USERNAME }}/flood-rescue:latest
- name: Deploy to VPS via SSH
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /opt/flood-rescue
docker compose pull
docker compose up -d --remove-orphansWhat I personally designed and built before the team started feature development: