A full-stack web application built with Spring Boot on the backend and React.js on the frontend, containerized with Docker, deployed on AWS, and automated with GitHub Actions CI/CD.
- Software Architecture
- Tech Stack
- Getting Started
- Build & Packaging
- Docker & Jib
- Database Setup
- AWS Deployment
- CI/CD Pipeline
- Testing
- React Project Structure
- Command Cheat Sheet
- Resources & Links
| Layer | Technology |
|---|---|
| Backend API | Spring Boot 2.7.6 |
| Frontend | React.js (Hooks & Functional Components) |
| Build Tool | Maven (Maven Wrapper ./mvnw) |
| Database | PostgreSQL on Docker / AWS RDS |
| ORM | Spring Data JPA |
| Containerization | Docker + Jib |
| Cloud Deployment | AWS Elastic Beanstalk + RDS |
| CI/CD | GitHub Actions |
| Monitoring | Slack Notifications |
| Testing | JUnit 5, AssertJ, H2 In-memory DB |
- Spring Boot β REST API, Validation, Data JPA
- PostgreSQL β Production database (Docker locally, RDS on AWS)
- Lombok β Reduces boilerplate Java code
- Java Faker β Generates fake data for integration tests
- H2 β In-memory database used during testing
- React.js β Functional components with Hooks (
useState,useEffect) - Ant Design β Enterprise-level UI component library
- React Bootstrap β Bootstrap-based UI components
- Axios / Unfetch β HTTP clients for calling the backend API
- Maven Profiles β Bundle FE+BE, build Docker images, push to DockerHub
- Jib β Builds optimized Docker images without a Docker daemon
- Docker Compose β Multi-container local setup
- AWS Elastic Beanstalk β Application hosting with auto-scaling and load balancing
- GitHub Actions β CI/CD automation (build, test, deploy)
- Slack β Deployment notifications
| Tool | Version |
|---|---|
| Java | 11 |
| Maven | via ./mvnw wrapper |
| Node.js | v14.21.1 |
| npm | 6.14.17 |
| Docker | Latest |
| PostgreSQL | via Docker (postgres:alpine) |
1. Start the database:
docker network create db
docker run --name db -p 5432:5432 --network=db \
-v "$PWD:/var/lib/postgresql/data" \
-e POSTGRES_PASSWORD=password \
-d postgres:alpine2. Run the backend:
./mvnw clean install
java -jar target/spring-boot-full-stack-professional-0.0.1-SNAPSHOT.jar3. Run the frontend (separately in development):
cd src/frontend
npm install
npm start
# or on a specific host:
HOST=0.0.0.0 npm startThe backend runs on http://localhost:8080 and the frontend on http://localhost:3000.
Instead of running the backend and frontend separately, you can bundle them into a single JAR file using Maven.
- Maven runs
npm installandnpm run buildvia thefrontend-maven-plugin - The React
./builddirectory is copied into the Spring Boot./resources/staticdirectory viamaven-resources-plugin - A single JAR file is generated in
./target/
| Profile | Command | Description |
|---|---|---|
bundle-backend-and-frontend (default) |
./mvnw clean install |
Builds FE + BE into one JAR |
jib-build-docker-image-and-push-it-to-docker-hub |
see below | Builds JAR + Docker image + pushes to DockerHub |
jib-build-local-docker-image |
see below | Builds JAR + local Docker image |
# Show all active profiles
./mvnw help:active-profiles
# Bundle FE+BE and push to DockerHub
./mvnw clean install -P bundle-backend-and-frontend \
-P jib-build-docker-image-and-push-it-to-docker-hub \
-Dapp.image.tag=1
# Bundle FE+BE and create local Docker image
./mvnw clean install -P bundle-backend-and-frontend \
-P jib-build-local-docker-image \
-Dapp.image.tag=latestJib builds optimized Docker and OCI images for Java applications without requiring a Docker daemon.
# Build local Docker image (JAR must already exist)
./mvnw jib:dockerBuild -Djib.to.image=fullstack:v1
# Build local Docker image (also creates JAR first)
./mvnw clean install jib:dockerBuild -Djib.to.image=fullstack:v1
# Build and push to DockerHub
./mvnw clean install jib:build -Djib.to.image=bdostumski/spring-react-fullstack:v1
# Build and push to DockerHub with explicit credentials
./mvnw clean install jib:build \
-Djib.to.image=bdostumski/spring-react-fullstack:latest \
-Djib.to.auth.username=bdostumski \
-Djib.to.auth.password=my-secret-password# Images
docker image ls # list images
docker image rm name:tag # delete image
docker pull image:version # pull image from remote
# Containers
docker ps # show running containers
docker ps -a # show all containers
docker rm -f container_name # force remove running container
# Run
docker run --name fullstack -p 8080:8080 fullstack:v1
docker run --rm -p 8080:8080 bdostumski/springboot-react-fullstack:latest
# Auth
docker login# 1. Create a Docker network
docker network create db
# 2. Run Postgres container
docker run --name db -p 5432:5432 --network=db \
-v "$PWD:/var/lib/postgresql/data" \
-e POSTGRES_PASSWORD=password \
-d postgres:alpine
# 3. Connect to the database interactively
docker run -it --rm --network=db postgres:alpine psql -h db -U postgres
# 4. Remove network when done
docker network rm dbβΉοΈ This local setup is for development/testing. Production uses AWS RDS.
docker run -it --rm postgres:alpine psql \
-h aa9320n4muma7h.celswdmxhcr1.eu-west-1.rds.amazonaws.com \
-U syscomz -d postgresNote: To connect externally, configure the RDS Security Group to allow your IP address as an inbound rule.
Create application-dev.properties and set the environment variable:
SPRING_PROFILES_ACTIVE=dev
Elastic Beanstalk automatically provisions:
- Load Balancer β distributes incoming traffic
- Auto-Scaling β adjusts EC2 instances based on load
- EC2 β actual virtual servers running the application
- ECS Cluster β container orchestration
- Remove the
SPRING_PROFILES_ACTIVE=devenvironment variable from your IDE - Login to Docker:
docker login - Build and push the image:
./mvnw clean install \ -P bundle-backend-and-frontend \ -P jib-build-docker-image-and-push-it-to-docker-hub \ -Dapp.image.tag=3
- Upload
./elasticbeanstalk/docker-compose.yamlto your Elastic Beanstalk environment
- In Elastic Beanstalk β Configuration β Database β select PostgreSQL (
db.t2.micro) - To allow external connections: go to RDS β DB Instances β your instance β Security β modify the Security Group inbound rules
π‘ To avoid AWS charges, terminate the environment when not in use and restore it when needed.
Push Code β GitHub Actions
βββ Pull Request β BUILD WORKFLOW (CI)
βββ Merge to Main β DEPLOYMENT WORKFLOW (CI/CD)
Triggered on every pull request. Runs on Ubuntu runner.
| Step | Action |
|---|---|
| 1 | Checkout code |
| 2 | Setup Java |
| 3 | Setup PostgreSQL |
| 4 | mvn clean package (build + compile + run all tests) |
| 5 | β "OK TO MERGE" status β reviewer can approve and merge |
Triggered when a PR is merged into main. Runs on Ubuntu runner.
| Step | Action |
|---|---|
| 1 | π£ Slack: "CI/CD is ongoing..." |
| 2 | Checkout code |
| 3 | Setup Java |
| 4 | Generate build number (e.g. 1.0.1) |
| 5 | Docker login to DockerHub |
| 6 | mvn clean package + Jib build image |
| 7 | π£ Slack: "Pushed bdostumski/image-name:1.0.1 to Docker Hub" |
| 8 | Update docker-compose.yaml in Elastic Beanstalk |
| 9 | π£ Slack: "Deployment started..." |
| 10 | Deploy to AWS |
| 11 | π£ Slack: "The new app is up and running!" |
β οΈ If any step fails, the workflow stops at that step and does not continue.
- Go to api.slack.com/apps β Create App β From scratch
- Enable Incoming Webhooks β Add New Webhook to Workspace β select channel β Allow
- Copy the Webhook URL and add it as a GitHub repository secret
- Add DockerHub credentials and AWS credentials as GitHub repository secrets
- In AWS Console β IAM β User Groups β create group with
AdministratorAccess-AWSElasticBeanStalkpolicy - Create a new user β assign to the group β generate Access Key (Programmatic access)
- Add
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYas GitHub repository secrets
Testing flows from the repository layer β service layer. Each layer mocks the units already tested below it.
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
| Component | Description |
|---|---|
| JUnit Platform | Foundation for launching testing frameworks on the JVM |
| JUnit Jupiter | New programming model and extension model for writing tests in JUnit 5 |
| JUnit Vintage | Provides a TestEngine for running JUnit 3 & JUnit 4 based tests |
| Tool | Purpose |
|---|---|
| AssertJ | Rich assertions, helpful error messages, better readability than JUnit 5 assertions |
| H2 In-memory Database | Used as the test database (no real DB needed) |
| Java Faker | Generates random fake data for integration tests |
| Failsafe Plugin | Runs integration tests (Surefire runs unit tests) |
- Unit Tests β test all positive and negative paths per layer; mock dependencies from lower layers
- Integration Tests β test positive paths only (negative cases already covered by unit tests)
| Path | Type | Description |
|---|---|---|
./node_modules |
Folder | All npm dependencies |
./.gitignore |
File | Files intentionally untracked by Git |
./package.json |
File | Project metadata and dependency list |
./README.md |
File | Project documentation (this file) |
./yarn.lock |
File | Ensures consistent dependency versions across machines |
| Path | Type | Description |
|---|---|---|
./public |
Folder | Root folder served as the React app |
./public/favicon.ico |
File | App icon used in index.html |
./public/index.html |
File | Template file served when app starts |
./public/logo192.png, logo512.png |
Files | React logos (192Γ192 and 512Γ512 px) |
./public/manifest.json |
File | App metadata for PWA / mobile home-screen |
./robots.txt |
File | Rules for web crawlers and scrapers |
| Path | Type | Description |
|---|---|---|
./src |
Folder | Core React application code |
./src/App.css |
File | Styles for App.js component |
./src/App.js |
File | Root React component |
./src/App.test.js |
File | Basic test for the root component (Jest) |
./src/index.css |
File | Global app styles |
./src/index.js |
File | Renders root component and registers service workers |
./src/logo.svg |
File | SVG React logo |
./src/serviceWorker.js |
File | Pre-caches scripts to improve performance |
./src/setupTests.js |
File | Sets up and invokes tests via npm run test |
useStateHook β tracks state (data/properties) in functional componentsuseEffectHook β performs side effects: fetching data, updating the DOM, timers- Axios / Unfetch β HTTP clients for fetching data from the Spring Boot backend
| Command | Description |
|---|---|
npm install |
Install all dependencies from package.json into node_modules |
npm install --save [dependency@version] |
Install a new dependency |
npm run build |
Build the FE application (creates ./build directory) |
npm start |
Start the FE development server |
HOST=0.0.0.0 npm start |
Start FE on a specific host |
npm -g i npx |
Install npx globally |
npx create-react-app frontend |
Create a new React app |
npx create-react-app@4.0.3 frontend |
Create React app with a specific version |
npm install --save antd@4.13.0 |
Install Ant Design with a specific version |
npm i -S unfetch |
Install Unfetch |
npm install --save @ant-design/icons |
Install Ant Design icons |
| Command | Description |
|---|---|
java -jar file.jar |
Run a JAR file from the ./target folder |
| Command | Description |
|---|---|
./mvnw clean |
Delete the ./target folder |
./mvnw install |
Create ./target folder and build the project using the active profile |
./mvnw clean install |
Combine clean + install |
./mvnw help:active-profiles |
Show all active Maven profiles |
| Command | Description |
|---|---|
docker login |
Login to Docker Hub |
docker image ls / docker images |
Show all local images |
docker ps |
Show running containers |
docker ps -a |
Show all containers |
docker rm -f container |
Force remove a running container |
docker run --name name -p 8080:8080 image:version |
Run a container |
docker run --rm --name name -p 8080:8080 image:version |
Run and auto-remove when stopped |
docker pull image:version |
Pull an image from a remote repository |
./mvnw jib:dockerBuild -Djib.to.image=name:version |
Build a local Docker image with Jib |
./mvnw clean install jib:dockerBuild -Djib.to.image=name:version |
Build JAR then local Docker image |
./mvnw clean install jib:build -Djib.to.image=bdostumski/name:v1 |
Build and push to DockerHub |
docker network create db
docker network rm db
docker run --name db -p 5555:5432 --network=db \
-v "/path/to/database-dir:/var/lib/postgresql/data" \
-e POSTGRES_PASSWORD=password \
-d postgres:alpine
docker run -it --rm --network=db postgres:alpine psql -h db -U postgres
docker run -it --rm postgres:alpine psql \
-h aa9320n4muma7h.celswdmxhcr1.eu-west-1.rds.amazonaws.com \
-U amigoscode -d postgres| Resource | Description |
|---|---|
| Spring Framework | Application framework and IoC container for Java |
| Spring Initializr | Spring project generator |
| React.js | JavaScript library for building UIs |
| Node.js | Open-source JavaScript runtime environment |
| Create React App (npm) | CRA on npm |
| Create React App (GitHub) | CRA repository |
| Ant Design | UI design system for enterprise-level products |
| Ant Design + CRA | Using Ant Design with Create React App |
| React Bootstrap | Bootstrap components for React |
| Unfetch | Minimal 500b fetch polyfill |
| Axios | Promise-based HTTP client for browser and Node.js |
| CORS | Cross-Origin Resource Sharing explained |
| Resource | Description |
|---|---|
| frontend-maven-plugin | Automates bundling FE and BE projects into one |
| maven-resources-plugin | Copies ./build from FE into ./resources/static in BE |
| Docker Hub | Docker container registry |
| Jib | Containerize Java apps without Docker daemon |
| Jib FAQs | Fixes for common Jib issues |
| Docker Compose | Tool for defining and running multi-container apps |
| Docker Compose v3 Reference | Compose file version 3 spec |
| Draw.io | Diagramming tool |
| Resource | Description |
|---|---|
| AWS Free Tier | AWS registration and free tier |
| Terminate Elastic Beanstalk Environment | How to terminate an EB environment |
| Restore Elastic Beanstalk Environment | How to restore a terminated EB environment |
| Docker Image: Postgres | Official Postgres image on Docker Hub |
| Resource | Description |
|---|---|
| GitHub Actions | Automate your workflow from idea to production |
| Slack | Team messaging system |
| Slack Incoming Webhooks | Build your own Slack app for notifications |
| Slack Webhook Guide | Fix for Slack message issues |
| Mockaroo | Generate fake data based on production data |
| JUnit 5 | Testing framework for Java and the JVM |
| AssertJ | Fluent assertion library for Java |
| H2 Database | In-memory database for testing |
| Failsafe Plugin | Maven plugin for running integration tests |
| Java Faker | Generates random fake data for tests |
π *This project was built as part of a professional full-stack learning course covering Spring Boot, React, Docker, AWS, and CI/CD automation.*s














