🚀 Week 7–8 · Final Project
Capstone Project
Build a full-stack AI-powered web application, containerise it with Docker, and deploy it on Azure Kubernetes Service. This is your showcase piece for job interviews.
1 · What You Will Build — SmartTask AI
🧠 The Big Picture
Think of it like a personal to-do app, but smarter. You type a task like "prepare for technical interview" and the AI suggests sub-tasks, estimates effort, and even recommends resources — just like a senior developer would advise a fresher.
Core Features
| Feature | What It Does | Technologies Used |
|---|---|---|
| User Auth | Register + Login with JWT tokens | Spring Security, JWT |
| Task CRUD | Create, read, update, delete tasks | Spring Boot REST API |
| AI Suggestions | Break any task into smaller steps using AI | LangChain4j + OpenAI / Azure OpenAI |
| Dashboard | View all tasks with filters and status | React + TypeScript |
| Cloud Deploy | Publicly accessible URL on Azure | Docker + AKS |
| CI/CD Pipeline | Auto-build + deploy on every git push | GitHub Actions |
✅ Why this project works for interviews
It touches every technology in the course: Java backend, REST API, JPA + PostgreSQL, React frontend, AI integration, Docker, Kubernetes, CI/CD. When an interviewer asks "show me something you built," this is your answer.
2 · System Architecture
The system has three main layers: a React frontend, a Spring Boot backend API, and an AI service. Everything runs as Docker containers orchestrated by Kubernetes.
High-Level Diagram
🌐 Browser (User)
↓ HTTPS
🔀 NGINX Ingress Controller (AKS)
↙ /api/*
↘ /*
🌱 Spring Boot API
Port 8080 · 2 replicas
Port 8080 · 2 replicas
⚛️ React Frontend
NGINX · Port 80 · 1 replica
NGINX · Port 80 · 1 replica
↓ JDBC ↓ OpenAI API
🐘 PostgreSQL
Azure DB or K8s Pod
Azure DB or K8s Pod
🤖 OpenAI / Azure OpenAI
External API (gpt-4o-mini)
External API (gpt-4o-mini)
Data Flow — "AI Suggest Sub-tasks"
- User types task title → clicks "Get AI Suggestions"
- React sends
POST /api/tasks/{id}/suggestto Spring Boot - Spring Boot calls LangChain4j with the task title + a system prompt
- LangChain4j sends request to OpenAI GPT-4o-mini
- AI returns a list of sub-tasks as JSON
- Spring Boot saves sub-tasks to PostgreSQL, returns them to React
- React displays them as a checklist under the task
3 · Week-by-Week Delivery Plan
📅 You have 2 weeks (Week 7 + Week 8)
Each week has a clear goal and a demo checkpoint. This structure is exactly how real software teams work in sprints. Follow it strictly — don't jump ahead.
Week 7 · Day 1–2
Project Setup
- Create GitHub repo
- Spring Boot init (Initializr)
- React + Vite + TypeScript
- docker-compose with PostgreSQL
- README with architecture diagram
Week 7 · Day 3–5
Core Backend API
- User, Task, SubTask entities + JPA
- CRUD endpoints (REST)
- JWT auth (register / login)
- Postman collection tested
- Unit tests with JUnit
Week 8 · Day 1–3
Frontend + AI
- React pages: Login, Dashboard, TaskDetail
- Axios API calls with JWT header
- LangChain4j integration + prompt
- AI suggestions endpoint working
- Tailwind / CSS styling
Week 8 · Day 4–5
Deploy + Polish
- Dockerfiles for API + frontend
- Push images to Azure Container Registry
- AKS deployment YAML files
- GitHub Actions CI/CD pipeline
- Live demo rehearsal + portfolio update
4 · Full Tech Stack
| Layer | Technology | Why |
|---|---|---|
| Backend Language | Java 21 | Industry standard for enterprise apps |
| Backend Framework | Spring Boot 3.x | Auto-configuration, massive ecosystem |
| Database ORM | Spring Data JPA + Hibernate | Write less SQL, use Java objects |
| Database | PostgreSQL 16 | Production-grade relational DB |
| Authentication | Spring Security + JWT | Stateless auth for REST APIs |
| AI Library | LangChain4j | Spring-native LLM integration |
| Frontend | React 18 + TypeScript | Most popular frontend stack today |
| Build Tool (FE) | Vite | Fastest React dev server |
| HTTP Client (FE) | Axios | Simple promise-based API calls |
| Containerisation | Docker + Docker Compose | Consistent local + prod environment |
| Orchestration | Azure Kubernetes Service (AKS) | Cloud-managed K8s cluster |
| Container Registry | Azure Container Registry (ACR) | Store Docker images in Azure |
| CI/CD | GitHub Actions | Free, integrated with GitHub |
| Version Control | Git + GitHub | Industry standard |
5 · Backend — Spring Boot API
Project Structure
smarttask-api/ ├── src/main/java/com/campustoai/smarttask/ │ ├── SmartTaskApplication.java // @SpringBootApplication │ ├── config/ │ │ ├── SecurityConfig.java // JWT filter, CORS config │ │ └── AiConfig.java // LangChain4j bean setup │ ├── model/ │ │ ├── User.java // @Entity │ │ ├── Task.java // @Entity, @ManyToOne User │ │ └── SubTask.java // @Entity, @ManyToOne Task │ ├── repository/ │ │ ├── UserRepository.java │ │ ├── TaskRepository.java │ │ └── SubTaskRepository.java │ ├── service/ │ │ ├── AuthService.java │ │ ├── TaskService.java │ │ └── AiSuggestionService.java // Calls LangChain4j │ └── controller/ │ ├── AuthController.java // POST /api/auth/register, /login │ ├── TaskController.java // CRUD /api/tasks │ └── AiController.java // POST /api/tasks/{id}/suggest └── src/main/resources/ └── application.properties
Task Entity
@Entity @Table(name = "tasks") public class Task { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String title; private String description; @Enumerated(EnumType.STRING) private TaskStatus status = TaskStatus.TODO; // TODO, IN_PROGRESS, DONE @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; @OneToMany(mappedBy = "task", cascade = CascadeType.ALL) private List<SubTask> subTasks = new ArrayList<>(); @CreationTimestamp private LocalDateTime createdAt; // Getters + Setters (or use Lombok @Data) }
Task Controller
@RestController @RequestMapping("/api/tasks") public class TaskController { private final TaskService taskService; public TaskController(TaskService taskService) { this.taskService = taskService; } @GetMapping public List<TaskDTO> getAllTasks(@AuthenticationPrincipal UserDetails user) { return taskService.getTasksForUser(user.getUsername()); } @PostMapping @ResponseStatus(HttpStatus.CREATED) public TaskDTO createTask(@RequestBody CreateTaskRequest req, @AuthenticationPrincipal UserDetails user) { return taskService.createTask(req, user.getUsername()); } @PutMapping("/{id}") public TaskDTO updateTask(@PathVariable Long id, @RequestBody UpdateTaskRequest req) { return taskService.updateTask(id, req); } @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteTask(@PathVariable Long id) { taskService.deleteTask(id); } }
6 · Frontend — React + TypeScript
Project Structure
smarttask-ui/ ├── src/ │ ├── api/ │ │ └── axiosInstance.ts // Base URL + JWT header interceptor │ ├── pages/ │ │ ├── LoginPage.tsx │ │ ├── DashboardPage.tsx // List of all tasks │ │ └── TaskDetailPage.tsx // Single task + AI suggestions │ ├── components/ │ │ ├── TaskCard.tsx │ │ ├── SubTaskList.tsx │ │ └── Navbar.tsx │ ├── types/ │ │ └── index.ts // Task, SubTask, User interfaces │ └── App.tsx // React Router routes ├── Dockerfile // Build + serve with NGINX └── vite.config.ts
Axios Instance with JWT Header
// src/api/axiosInstance.ts import axios from 'axios'; const api = axios.create({ baseURL: import.meta.env.VITE_API_URL ?? 'http://localhost:8080', }); // Automatically attach the JWT token to every request api.interceptors.request.use((config) => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); export default api;
Dashboard Page — Fetch All Tasks
// src/pages/DashboardPage.tsx import { useEffect, useState } from 'react'; import api from '../api/axiosInstance'; import { Task } from '../types'; export default function DashboardPage() { const [tasks, setTasks] = useState<Task[]>([]); useEffect(() => { api.get<Task[]>('/api/tasks') .then(res => setTasks(res.data)) .catch(err => console.error(err)); }, []); return ( <div className="dashboard"> <h1>My Tasks</h1> {tasks.map(task => ( <div key={task.id} className="task-card"> <h3>{task.title}</h3> <span className={`status ${task.status.toLowerCase()}`}>{task.status}</span> </div> ))} </div> ); }
7 · AI Feature — Sub-task Suggestions
Spring Boot Side — AI Service
// AiSuggestionService.java @Service public class AiSuggestionService { private final TaskAssistant assistant; // LangChain4j @AiService public List<String> suggestSubTasks(String taskTitle) { String response = assistant.breakDownTask(taskTitle); // Response is a numbered list like "1. Research topic\n2. Create outline\n..." return Arrays.stream(response.split("\\n")) .filter(line -> !line.isBlank()) .map(line -> line.replaceAll("^\\d+\\.\\s*", "")) .collect(Collectors.toList()); } }
LangChain4j AI Service Interface
// TaskAssistant.java @AiService public interface TaskAssistant { @SystemMessage(""" You are a productivity coach helping software developers. When given a task title, break it into 5 clear, actionable sub-tasks. Return ONLY a numbered list. No extra text or explanation. """) String breakDownTask(@UserMessage String taskTitle); }
LangChain4j Dependency (pom.xml)
<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> <version>0.32.0</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId> <version>0.32.0</version> </dependency>
application.properties — AI Config
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
langchain4j.open-ai.chat-model.model-name=gpt-4o-mini
langchain4j.open-ai.chat-model.temperature=0.3
langchain4j.open-ai.chat-model.max-tokens=500
💡 Saving API costs
Use
gpt-4o-mini instead of gpt-4o — it's 10x cheaper and fast enough for simple task breakdowns. Set max-tokens=500 to cap cost per call.
8 · Deployment on Azure Kubernetes Service
Step 1 — Dockerfiles
# Backend Dockerfile (multi-stage) FROM eclipse-temurin:21-jdk-alpine AS builder WORKDIR /app COPY . . RUN ./mvnw clean package -DskipTests FROM eclipse-temurin:21-jre-alpine WORKDIR /app COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]
# Frontend Dockerfile FROM node:20-alpine AS builder WORKDIR /app COPY package*.json . RUN npm ci COPY . . RUN npm run build FROM nginx:alpine COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80
Step 2 — Push Images to ACR
# Login to Azure az login az acr login --name smarttaskacr # Tag and push backend image docker build -t smarttask-api ./smarttask-api docker tag smarttask-api smarttaskacr.azurecr.io/smarttask-api:v1 docker push smarttaskacr.azurecr.io/smarttask-api:v1 # Tag and push frontend image docker build -t smarttask-ui ./smarttask-ui docker tag smarttask-ui smarttaskacr.azurecr.io/smarttask-ui:v1 docker push smarttaskacr.azurecr.io/smarttask-ui:v1
Step 3 — Kubernetes Deployment YAML
# k8s/api-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: smarttask-api spec: replicas: 2 selector: matchLabels: app: smarttask-api template: metadata: labels: app: smarttask-api spec: containers: - name: api image: smarttaskacr.azurecr.io/smarttask-api:v1 ports: - containerPort: 8080 env: - name: SPRING_DATASOURCE_URL valueFrom: secretKeyRef: name: smarttask-secrets key: db-url - name: OPENAI_API_KEY valueFrom: secretKeyRef: name: smarttask-secrets key: openai-key
Step 4 — GitHub Actions CI/CD
# .github/workflows/deploy.yml name: Build and Deploy on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Log in to Azure uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Build and push backend image run: | az acr build --registry smarttaskacr \ --image smarttask-api:${{ github.sha }} \ ./smarttask-api - name: Deploy to AKS uses: azure/k8s-deploy@v4 with: resource-group: smarttask-rg name: smarttask-aks manifests: k8s/ images: smarttaskacr.azurecr.io/smarttask-api:${{ github.sha }}
⚠️ Cost Warning
AKS clusters cost money even when idle. After your demo, run
az aks stop --name smarttask-aks --resource-group smarttask-rg to pause the cluster. Use the Azure Free Trial $200 credit wisely.
9 · Submission Checklist
Before your final demo, verify every item below:
- GitHub repo is public with a detailed README (screenshots, architecture diagram, how to run locally)
- Application runs with
docker compose upon a fresh machine - All 4 CRUD endpoints tested in Postman (collection exported + committed to repo)
- JWT authentication works — cannot access tasks without a valid token
- AI sub-task suggestion endpoint returns real results from OpenAI
- React frontend connects to backend API (no CORS errors in browser console)
- Docker images pushed to Azure Container Registry
- Application deployed and accessible via public AKS IP or domain
- GitHub Actions workflow runs green on push to main
- At least 5 unit tests passing (
mvn testshows green) - Secrets (API keys, DB passwords) are in environment variables — NOT hardcoded in code
- LinkedIn post drafted: project name, tech stack, live link, GitHub link
10 · Demo Guide & Portfolio Tips
5-Minute Demo Script
| Time | What to Show | What to Say |
|---|---|---|
| 0:00 – 0:30 | Open live app URL in browser | "This is SmartTask AI — a full-stack app I built and deployed on Azure Kubernetes." |
| 0:30 – 1:30 | Register new user, login, create a task | "The backend is Spring Boot with JWT authentication. User credentials are stored securely hashed with BCrypt." |
| 1:30 – 2:30 | Click "Get AI Suggestions" | "Here I integrated OpenAI via LangChain4j. The AI breaks down any task into actionable sub-steps." |
| 2:30 – 3:30 | Show GitHub Actions pipeline (green) | "Every push to main automatically builds Docker images and deploys to AKS. That's CI/CD in action." |
| 3:30 – 5:00 | Show architecture diagram in README | "The architecture uses React frontend, Spring Boot API, PostgreSQL, all containerised. Walk through each layer." |
How to Add This to Your Resume
✅ Resume bullet point template
SmartTask AI — Full-Stack Developer (Personal Project, Month Year)
• Built a task management web app with AI-powered sub-task suggestions using Spring Boot, React, LangChain4j, and GPT-4o-mini
• Designed REST API with JWT auth, Spring Data JPA, and PostgreSQL; deployed on Azure Kubernetes Service
• Set up CI/CD pipeline with GitHub Actions, reducing manual deployment effort by 100%
• Tech Stack: Java 21, Spring Boot 3, React 18, TypeScript, Docker, AKS, LangChain4j, PostgreSQL
SmartTask AI — Full-Stack Developer (Personal Project, Month Year)
• Built a task management web app with AI-powered sub-task suggestions using Spring Boot, React, LangChain4j, and GPT-4o-mini
• Designed REST API with JWT auth, Spring Data JPA, and PostgreSQL; deployed on Azure Kubernetes Service
• Set up CI/CD pipeline with GitHub Actions, reducing manual deployment effort by 100%
• Tech Stack: Java 21, Spring Boot 3, React 18, TypeScript, Docker, AKS, LangChain4j, PostgreSQL
LinkedIn Post Template
📢 Share Your Project
"Excited to share my capstone project — SmartTask AI! 🚀
Over the last 2 weeks I built a full-stack AI-powered task manager using:
✅ Spring Boot + JWT authentication
✅ React + TypeScript frontend
✅ LangChain4j + OpenAI GPT-4o-mini for AI suggestions
✅ Deployed on Azure Kubernetes with GitHub Actions CI/CD
Live link: [your-aks-ip]
GitHub: [your-repo-url]
#Java #SpringBoot #React #Azure #Kubernetes #AI #BuildInPublic"
"Excited to share my capstone project — SmartTask AI! 🚀
Over the last 2 weeks I built a full-stack AI-powered task manager using:
✅ Spring Boot + JWT authentication
✅ React + TypeScript frontend
✅ LangChain4j + OpenAI GPT-4o-mini for AI suggestions
✅ Deployed on Azure Kubernetes with GitHub Actions CI/CD
Live link: [your-aks-ip]
GitHub: [your-repo-url]
#Java #SpringBoot #React #Azure #Kubernetes #AI #BuildInPublic"
Common Demo Pitfalls — Avoid These
| Mistake | Fix |
|---|---|
| API key exposed in GitHub repo | Always use environment variables + add .env to .gitignore |
| App crashes during live demo | Test the exact demo flow 3 times before the interview |
| Can't explain a technology you used | Understand every line of code you wrote — interviewers will dig deep |
| No README | README is the first thing interviewers check — write it like a product launch page |
| Hardcoded credentials in DB | Use Kubernetes Secrets for all sensitive config |
★ Interview Q&A
1 Walk me through the architecture of your capstone project.
"My project SmartTask AI has three layers. The React TypeScript frontend makes API calls to a Spring Boot backend running on port 8080. The backend authenticates users with JWT, stores data in PostgreSQL using Spring Data JPA, and calls OpenAI via LangChain4j for AI suggestions. Everything runs as Docker containers on Azure Kubernetes Service. GitHub Actions builds and deploys the images on every push to main."
- Always describe it from user's perspective outward to infrastructure
- Mention how layers communicate (REST, JDBC, etc.)
- Shows you understand the whole system, not just your part
2 Why did you choose Kubernetes over just running Docker directly?
"Docker alone is great locally, but in production you need self-healing, scaling, and zero-downtime deployments. If the API container crashes, Kubernetes automatically restarts it. I configured 2 replicas for the backend — if one pod fails, traffic automatically goes to the other. Rolling updates let me deploy new code without any downtime."
- Key point: Kubernetes solves problems that Docker Compose alone can't handle in production
3 How did you handle secrets like the OpenAI API key in Kubernetes?
"I used Kubernetes Secrets to store the OpenAI API key and database password. In the deployment YAML, I reference the secret using
secretKeyRef — the pod gets the value as an environment variable at runtime. Secrets are base64-encoded in etcd. For extra security in a real team environment, I'd integrate Azure Key Vault."
- Never hardcode secrets in code or Docker images — instant red flag in interviews
- Bonus: mention that base64 is encoding, not encryption — show deeper understanding
4 How does JWT authentication work in your Spring Boot app?
"When a user logs in, the server validates their credentials and signs a JWT token with a secret key. The token contains the user's email and expiry time. The client stores this in localStorage and sends it in the Authorization header as 'Bearer <token>' with every request. Spring Security's filter chain intercepts each request, validates the token signature, and extracts the user identity — no database call needed for auth."
- Emphasize: JWT is stateless — server doesn't store sessions
- Know the difference: Authentication (who are you?) vs Authorization (what can you do?)
5 What would you improve if you had more time?
This is a self-awareness question. Good answers show you can critically evaluate your own work:
- "I would add WebSocket support so AI suggestions stream in word by word (like ChatGPT) instead of waiting for the full response"
- "I would add Redis caching so repeated AI suggestions for the same task title don't cost extra API calls"
- "I would implement refresh tokens to improve security — currently JWT tokens expire and users get logged out"
- "I would write integration tests using Testcontainers to test the real database, not an in-memory one"
6 What challenges did you face and how did you solve them?
Interviewers want to hear about real problems — not "it all went smoothly." Good examples:
- CORS errors: React on port 5173 calling Spring Boot on 8080 — fixed by adding
@CrossOriginor a global CORS config in SecurityConfig - JWT in Docker: Tokens signed with a hardcoded secret — moved to environment variable so it's consistent across containers
- AI response parsing: OpenAI sometimes returned a numbered list with extra whitespace — added regex cleaning in the service layer
- K8s image pull errors: AKS couldn't pull from ACR — fixed by attaching the ACR to the AKS cluster with
az aks update --attach-acr