Video I referred: https://youtu.be/_oDpUs65OiA?si=bsAP6ZWiJvcJX6pj
This guide outlines the exact chronological order and simple code-level steps followed to construct the CreatorStore backend application.
- First, we create a fresh Spring Boot project using Spring Initializr.
- Second, we add the required starter dependencies for Web, JPA, PostgreSQL, Validation, and Lombok.
- Third, we create a
.envfile at the root folder to hide our private database password. - Fourth, we add configuration lines inside our
DemoApplication.javamain method to load this.envfile before the server boots up.
- First, we create a package named
entities. - Second, we create a Java class named
Product.javainside this package. - Third, we add class-level annotations like
@Entityand@Table(name="products")to map this class directly to a database table. - Fourth, we add Lombok annotations like
@Getter,@Setter, and@Builderto generate boilerplate code automatically. - Fifth, we add field-level validation restrictions like
@NotBlankfor the name and@DecimalMinfor the price to prevent bad data.
- First, we create a package named
repositories. - Second, we create an interface named
ProductRepository.javathat extendsJpaRepository. This gives us built-in database methods like.save()and.findAll(). - Third, we create a package named
service. - Fourth, we create a class named
ProductService.javamarked with the@Serviceannotation to hold our business logic. - Fifth, we write five simple CRUD methods inside this service class to create, read, update, and delete products from the database.
- First, we create a package named
controllers. - Second, we create a class named
ProductController.javamarked with@RestControllerand@RequestMapping("/api/products"). - Third, we inject our
ProductServicedependency into this class using Lombok's@RequiredArgsConstructor. - Fourth, we write endpoints for POST, GET, PUT, and DELETE HTTP verbs.
- Fifth, we use the
@RequestBodyannotation to capture incoming raw JSON payloads and convert them into Java objects. - Sixth, we add the
@Validannotation to enforce our field restrictions and reject bad client requests with a 400 error.
- First, we create a package named
dtoto separate our raw database entities from client request payloads. - Second, we create a lightweight class named
OrderItemRequest.javato accept only aproductIdand aquantityfrom the customer. - Third, we create a main request class named
OrderRequest.javato accept thecustomerName,customerEmail, and a nestedList<OrderItemRequest>. - Fourth, we add the
@Validannotation on the nested list so our validation checks cascade down into every single item row in the payload.
- First, we go back to the
entitiespackage to build our relational database layout. - Second, we create the parent class named
Order.javato store global checkout attributes liketotalPrice,status, and tracking timestamps. - Third, we create the child junction class named
OrderItem.javato store individual snapshot line items. - Fourth, we map a
@ManyToOnerelationship fromOrderItemback to both the parentOrderentity and the targetProductentity.
- First, we create an interface named
OrderRepository.javainside our repository package. - Second, we open our service package and create a brand-new class named
OrderService.java. - Third, we inject both
OrderRepositoryandProductRepositoryasfinalvariables at the top of the class. - Fourth, we create a checkout method named
createOrderand mark it with the@Transactionalannotation for safety. - Fifth, we write a
forloop inside this method to iterate through the client's requested item list. - Sixth, inside the loop, we fetch the live product row, verify inventory stock levels, deduct stock quantities, and calculate dynamic prices.
- Seventh, we save the modified product stock changes and construct individual
OrderItemrecords using the Builder design pattern. - Eighth, we calculate the grand total price and save the complete order chain securely to our database.
- First, we open our controllers package and create a class named
OrderController.java. - Second, we map the top-level route path to
"/api/orders"using the@RequestMappingannotation. - Third, we map a
@PostMappingroute to hand over incomingOrderRequestpayloads to our checkout service logic. - Fourth, we map a generic
@GetMappingroute to run a SQL select query and fetch our complete global order history tracking list. - Fifth, we map a specific
@GetMapping("/{id}")route to search for a single unique order row using an explicit path variable lookup binding.