This repository contains a sample of a Video Streaming API. This API is responsible for handling video-related functionalities, such as uploading and serving videos metadata.
This project uses the following technologies:
- Java 17: The programming language used to implement the API.
- Maven: The build tool used to manage dependencies and build the project.
- Spring Boot: A framework for building Java applications that simplifies the development process.
- OpenAPI: Used for documenting the API and generating API documentation.
- Swagger: A tool for generating interactive API documentation based on OpenAPI specifications.
- PostgreSQL: The database used to store the video metadata and other related information.
- Docker: Used for containerization, making it easier to deploy and run the application in different environments.
- Kubernetes: Used for orchestrating the deployment of the application in a cloud-native environment.
- Tilt: A tool for managing the development environment, allowing for easy setup and configuration of the project.
-
Docker Desktop OR Orbstack (Alternative to Docker Desktop, recommended for MacOS users)
Note: Using Orbstack? Remember to enable Kubernetes in the settings area of Orbstack.
With tilt, you can run the project in a local environment with a single command in the repository root: tilt up.
Tilt will take care of building the Docker images, deploying them to Kubernetes, and setting up the local environment.
Note: The first time you run
tilt up, it may take a while to build the images and deploy the services, as it will download all the necessary dependencies and build the Docker images. Subsequent runs will be much faster, since this project uses a Docker caching strategy.
Once Tilt is running, you can access the application through the Tilt UI, which is available at http://localhost:10350.
You can also access the individual services through the links provided in the Tilt UI:
- App: The main application service, which handles the API requests, is available at
http://localhost:8080. - Documentation: The API documentation is available at
http://localhost:8080/docs. It's generated using OpenAPI. - Swagger UI: The interactive API documentation is available at
http://localhost:8080/api.html.
This project is built on Hexagonal Architecture and DDD[https://en.wikipedia.org/wiki/Domain-driven_design], which allows for a clean separation of concerns and makes it easier to test and maintain the codebase.
The architecture is divided into three main layers:
- Application: Contains the application services that orchestrate the domain logic and handle use cases.
- Domain: Contains the core business logic and domain entities.
- Infrastructure: Contains the implementation details, such as database access, external services, and other technical concerns.
The application layer is responsible for handling the client interface. It contains:
-
Controllers: Handle incoming HTTP requests and map them to the appropriate application services. Each endpoint in the controller is responsible for a specific use case, such as uploading a video, processing a video, or retrieving video metadata. Note that the controllers implements interfaces for Port and Adapter pattern (usign the dtos declared under
/requestsand/responses). -
DTOs: Data Transfer Objects used to transfer data between the application layer and the domain layer. DTOs are used to encapsulate the data sent and received by the API endpoints, ensuring a clear contract between the client and the server. Also, they are used to validate the data sent by the client (using the @Valid notation), ensuring that the data is in the correct format and meets the required constraints.
-
Exceptions: Custom exceptions used to handle errors and provide meaningful error messages to the client. These exceptions are thrown when an error occurs in the application layer, such as when a video upload fails or when a video cannot be found.
-
Handlers: Handlers are responsible for processing the requests and responses. They act as a
Portbetween the request and the business logic, for each use case and interact with the domain layer to perform the necessary operations, usually by managing domain services. Handlers are designed to be reusable and can be used in multiple controllers.
The domain layer contains the core business logic and domain entities. It is responsible for defining the rules and behaviours of the application. The domain layer includes:
-
Entities: The core business objects that represent the main concepts of the application (such as
Video). Entities are defined with their properties and behaviours, encapsulating the business logic related to them. -
Repositories: Interfaces that define the contract for accessing and manipulating the domain entities. Repositories are used to abstract the data access layer and provide a clean interface for the application layer to interact with the domain entities. The repositories act as a
Portbetween the domain services and the infrastructure layer, allowing the application to be independent of the underlying data storage technology or the specific implementation details (Such as distinct entities for PostgreSQL and the domain entity). -
Domain Services: Services that encapsulate the business logic and operations related to the domain entities. Domain services are used to implement complex business rules and operations that cannot be easily expressed within a single entity. They interact with the repositories to perform operations on the domain entities and ensure that the business rules are enforced.
It is responsible for providing the necessary infrastructure to support the application layer and domain layer. The infrastructure layer includes:
-
Databases: The JPA repositories that interact with the database. The infrastructure layer provides the implementation details for the repositories defined in the domain layer, allowing the application to persist and retrieve data from the database.
-
Entities: The JPA entities that map the domain entities to the database tables. These entities are used to define the structure of the data stored in the database and provide the necessary annotations for JPA to manage the persistence of the data.
The video upload functionality, rather than being done using a multipart request, is done using a POST request to the /videos endpoint, with a JSON body containing the video content field, which is a mock for the file content.
A new video entity is created and returns id (that should be used in the following endpoints) and content.
The video publish functionality is implemented using a PATCH request to the /videos/{id}/publish endpoint. This endpoint is responsible for publishing the video, which involves updating the video status to PUBLISHED after performing the necessary validations and checks (such as checking that the video is in DRAFT status, has all the required metadata, and contains content).
The video metadata update functionality is implemented using a PATCH request to the /videos/{id}/metadata endpoint. This endpoint allows you to update the video metadata, including the title, synopsis, and cast. The request body should contain the fields to be updated, and the response will return the updated video metadata.
The video playback functionality is implemented using a GET request to the /videos/{id}/play endpoint. This endpoint is responsible for serving the video content to the client. It retrieves the video by its ID and returns the video content in the response. Also, it adds a view count to the video, incrementing the number of views for that video.
The video loading functionality is implemented using a GET request to the /videos/{id}/load endpoint. This endpoint retrieves the video metadata by its ID and returns the video details, such as the title, synopsis, cast, and status. It does not return the video content itself, but rather the metadata associated with the video.
The video deletion functionality is implemented using a DELETE request to the /videos/{id} endpoint. This endpoint is responsible for soft-deleting the video by its ID. It excludes the video from the list of available videos, but does not remove it from the database. This allows for potential recovery of the video in the future if needed.
The video list functionality is implemented using a GET request to the /videos endpoint. This endpoint retrieves a list of all videos, including their metadata such as title, synopsis, cast, status, and view count. The response is not paginated yet (a future improvement) and allows for filtering by Title, Director, Genre, Year of release, and cast. Which can be done using query parameters, such as ?title=example&director=example&genre=example&year=2023&cast=example.
The query is case-insensitive, allowing for flexible searching. The response will return a list of videos that match the specified criteria.
The get video functionality is implemented using a GET request to the /videos/{id} endpoint. This endpoint retrieves video metadata by its ID and returns the video details, including title, synopsis, cast, status, and view count.
The API uses a consistent error-handling mechanism to provide meaningful error messages to the client. When an error occurs, the API returns a JSON response with the following structure:
{
"message": "Error message describing the issue",
"id": "unique-error-id",
}The message field contains a human-readable error message, while the id field contains a unique identifier for the error, which can be used for debugging purposes.
The Error Handling implements a custom exception handler that catches exceptions thrown by the application and maps them to appropriate HTTP status codes. For example:
- If a video is not found, it returns a
404 Not Foundstatus with a message indicating that the video was not found. - If a video upload fails due to a malformed request, it returns a
400 Bad Requeststatus with a message indicating the reason for the failure. - If an internal server error occurs, it returns a
500 Internal Server Errorstatus with a generic error message.