
This project was part of the Sparta Java-Spring Boot Backend Boot Camp.
📣 A Note to Hiring Team: You can skip right to Chapter 5 for code reviews.
🍱 Table of Contents
- Project Overview
- System Architecture
- Development Workflow - How we worked together
- My Role
- Code Implementation
- Wrapping Up
ℹ️ Project Overview

TodayEats is an AI-powered delivery platform built with Spring Boot, designed to explore real-world backend architecture and infrastructure design beyond simple CRUD development.
Scalable and maintainable backend system
- PostgreSQL, Redis, Docker, AWS, and GitHub Actions for CI/CD automation.
This project is built on top of a modular monolithic architecture to reduce operational complexity, but we kept scalability in mind when designing the system.
You can check the README file in the repository for more information.
GitHub: https://github.com/GolemOnce/Sparta-TodayEats
⚙️ System Architecture

- Only Backend - Spring Boot
- Spring Web, Spring Data JPA, PostgreSQL Driver, Spring Security, Lombok, JUnit5 & Mockito
- Database - PostgreSQL
- AI - Gemini AI Studio API
- Infrastructure - Docker, AWS
- DevOps - GitHub Actions
- Caching - Redis
- Documentation - Swagger / OpenAPI
🏗️ Development Workflow - How we worked together
This bootcamp was online, and the camp decided to use Zap as a communication platform.
Thanks to Zap, we were able to communicate without latency or delay whenever we wanted to talk.
Our team also made sure to put all the documents in one place, so no member missed anything and on schedule for development.

As you can see in the photo, we have documented every detail of the development and the code on a single Notion page.
The team followed the Agile process, but we tried to stick to the initial plan.
Thanks to AI, the actual coding stage didn't take us so long; it was mostly planning and structuring the whole project together.

We also made sure to use the GitHub Kanban board (in the project tab) and the Git Issues to share the coding process and how far each person got.


We even had our own template (Issue and Pull Request) to keep everything looking clean for each member.

My Role

My primary responsibilities in this project included implementing the Menu domain and integrating the Gemini AI API.
I also designed the AI request logging flow to track prompts and responses for better monitoring and future improvements.
🔤 Code Implementation
Menu Domain
1. Query design based on "who is viewing"

I split them into purpose-specific JPQL queries.
findOwnerMnuesByStoredIdfindOrderableMenusByStoreIdfindVisibleMunusByStoreAndCategory
=> A key design decision was treating "hidden" and "sold out" as two separate status fields.
Full Code Review Available Here
2. Revalidation of ownership at the service layer

For every mutating operation (update, delete, status change), I added a validateStoreOwner() check in the service layer that compares the store's actual owner against the requester's ID.
Full Code Review Available Here
3. Soft delete + Cascading deletion



Menus use soft deletion via inheriting from BaseEntity, recording deletedAt / deletedBy instead of physically removing rows.
Full Code Review Available Here
AI Domain
1. Gemini API integration with granular failure handling

GeminiClient calls the Gemini API directly via RestTemplate. Rather than treating all failures the same, I split failure handling into three distinct cases:
ResourceAccessException(network timeout) →AI_TIMEOUT(504)- Any other call-level exception (invalid key, server error, etc.) →
AI_API_ERROR(502) - A response comes back, but
candidates/content/parts/textis missing or empty at any level →AI_RESPONSE_EMPTY(502)
Full Code Review Available Here
2. Prompt shaping + a double safeguard on response length

Rather than forwarding the store owner's raw prompt to Gemini as-is, I appended an instruction — " 답변을 최대한 간결하게 50자 이하로" ("keep the answer under 50 characters, as concise as possible") — to the prompt before sending it.
Full Code Review Available Here
2. Storing the raw prompt and the shaped prompt separately

The AiRequestLogEntity stores three fields separately: the original user prompt, the shaped prompt actually sent to the API (requestPrompt), and the response.
Full Code Review Available Here
4. Role-based access control

The AI description generation endpoint is restricted to @PreAuthorize("hasRole('OWNER')"), so only store owners can trigger it.
Full Code Review Available Here
Testing
I wrote unit tests mocking GeminiClient and AiRequestLogRepository to verify that the prompt is correctly combined with the appended instruction before being sent, and that the response gets persisted to the log correctly.
Full Code Review Available Here
Wrapping Up
This project was my first time working with other programmers; I learned to write code that collaborates with people, follow workflows, and integrate AI.

