A movie catalogue with a Spring Boot backend and a TypeScript React frontend, kept as two folders in one repository.
The backend exposes a REST API over a document store and models movies and their reviews as separate collections, with the review write path behind its own endpoint so the client never posts into the movie document directly. The frontend consumes it as a plain fetch client and renders a browse grid plus a detail page with an embedded trailer.
Most of the learning was on the Java side, and it was mostly about inversion of control: dependencies arrive through constructor parameters instead of being constructed where they are used, and repository interfaces are implemented by the framework at runtime from their method names alone. Coming from JavaScript, code that works without an implementation you can point at takes a while to trust.
Built in 2022 while learning the Spring ecosystem.
A movie catalogue split into a Spring Boot REST backend and a TypeScript React frontend, kept as two applications in one repository and wired together with Docker Compose.
Features#
- Browse a paginated movie grid
- Movie detail page with poster art, backdrops, trailers and cast
- Reviews attached to a movie, submitted from the detail page
- Header slideshow of featured titles
- Search input over the catalogue
- Integration tests against the API layer
Stack#
Backend — Java · Spring Boot · Spring Data repositories · Maven
Frontend — React · TypeScript · Create React App · React Router
Infra — Docker + Docker Compose (frontend on :3000, API on :8080)
Architecture#
The backend is deliberately layered rather than flat — the controller never touches persistence directly:
api/MoviesController # HTTP surface, request/response only
└── facades/MovieFacade # orchestration, the use case
├── services/MovieDBService
├── mappers/MovieMapper # entity → DTO
└── reposotiry/MovieRepository
model/Movie · dtos/MovieDto · dtos/MoviePageEach layer has one reason to change. The mapper exists so the persistence model and the JSON contract can drift apart without breaking either: adding a column does not silently widen the API response, and renaming a DTO field does not touch the schema.
The frontend mirrors that separation — services/MovieService and
services/api.ts own every network call, and models/Movie.ts and
models/MoviePage.ts type the responses, so no component constructs a URL.
movies-backend/ # Spring Boot API
movies-frontend/ # React client
docker-compose.yml # both, togetherRunning locally#
Everything at once:
git clone https://github.com/Vette1123/Full-Stack-Spring-Boot-Movie-App.git
cd Full-Stack-Spring-Boot-Movie-App
docker compose up --buildFrontend on http://localhost:3000, API on http://localhost:8080.
Separately, if you want hot reload:
cd movies-backend && ./mvnw spring-boot:run # configure your datasource first
cd movies-frontend && npm install && npm startTests#
cd movies-backend && ./mvnw testMoviesControllerIT and AbstractControllerIT exercise the API through the
full Spring context rather than mocking the layers underneath — slower than
unit tests, and the only kind that would have caught a broken bean wiring or a
mapper that silently drops a field.
Notes#
Built in 2022 while learning the Spring ecosystem. The part that took longest
to trust, coming from JavaScript, was inversion of control: dependencies arrive
as constructor parameters instead of being constructed where they are used, and
MovieRepository is an interface the framework implements at runtime from
the method names alone. Code that works without an implementation you can open
and read takes a while to stop feeling like magic.
Built by Mohamed Gado · 2022