404 Not Found

404 Not Found


nginx

Phase 1 Comprehensive Exercise: Building the Basic Framework of OrderFlow

Putting Knowledge into Practice—This lesson brings together all the concepts covered in the first five lessons to build the complete foundational framework for the OrderFlow e-commerce order management system.

1. What You'll Learn


2. A True Story of Teamwork

(1) Pain Point: Knowledge is fragmented and cannot be put into practice

Alice had finished learning about Spring Boot's auto-configuration, REST APIs, configuration management, and other topics, but when it came time to actually build the OrderFlow project, she didn't know where to start. Each topic was taught in isolation, so she wasn't sure how to switch between H2 for the dev environment and MySQL for the prod environment, or how to integrate REST APIs with configuration management.

(2) Solutions to the Comprehensive Exercises

This lesson ties all the concepts together into a complete project—from creating the project, configuring the environment, and writing APIs to customizing the Starter—to simulate a real-world development workflow.

(3) Revenue

Once Alice has completed the comprehensive exercises, the OrderFlow project skeleton can serve directly as the foundation for subsequent courses, boosting development efficiency by a factor of three.


3. Creating a Project and Configuring the Environment

(1) Project Initialization

(1) ▶ Example: Creating a Project Using Spring Initializr

BASH
curl https://start.spring.io/starter.zip \
  -d type=maven-project \
  -d language=java \
  -d bootVersion=3.2.5 \
  -d groupId=com.orderflow \
  -d artifactId=orderflow-service \
  -d packageName=com.orderflow \
  -d javaVersion=17 \
  -d dependencies=web,data-jpa,h2,mysql,validation,lombok \
  -o orderflow-service.zip && unzip orderflow-service.zip

Output:

TEXT
{"status":"ok","data":{}}

(2) Multi-Environment Configuration

100%
graph TD
    A["application.yml<br/>Common"] --> B["application-dev.yml<br/>H2 + Debug"]
    A --> C["application-prod.yml<br/>MySQL + WARN"]
    B --> D["Merged: dev profile"]
    C --> E["Merged: prod profile"]
Configuration Item dev prod
Database H2 In-Memory MySQL
ddl-auto create-drop validate
Log Level DEBUG WARN
Port 8080 8080

4. Product and Order REST API

(1) Project Package Structure

TEXT
com.orderflow/
├── OrderFlowApplication.java
├── config/
│   └── OrderFlowProperties.java
├── controller/
│   ├── ProductController.java
│   └── OrderController.java
├── model/
│   ├── Product.java
│   └── Order.java
└── repository/
    ├── ProductRepository.java
    └── OrderRepository.java

(1) ▶ Example: Product DTO and Controller

JAVA
// model/Product.java
package com.orderflow.model;

public record Product(
    Long id,
    String name,
    BigDecimal price,
    Integer stock
) {}

// controller/ProductController.java
package com.orderflow.controller;

import com.orderflow.model.Product;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/v1/products")
public class ProductController {

    private final Map<Long, Product> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);

    @PostMapping
    public ResponseEntity<Product> create(@RequestBody Product product) {
        Long id = seq.getAndIncrement();
        Product saved = new Product(id, product.name(), product.price(), product.stock());
        store.put(id, saved);
        return ResponseEntity.status(HttpStatus.CREATED).body(saved);
    }

    @GetMapping
    public Collection<Product> list() {
        return store.values();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Product> get(@PathVariable Long id) {
        Product p = store.get(id);
        return p != null ? ResponseEntity.ok(p) : ResponseEntity.notFound().build();
    }
}

Output:

TEXT
// Execution Successful

(2) ▶ Example: Order DTO and Controller

JAVA
// model/Order.java
package com.orderflow.model;

import java.time.Instant;

public record Order(
    Long id,
    Long productId,
    Integer quantity,
    String status,
    Instant createdAt
) {}

// controller/OrderController.java
package com.orderflow.controller;

import com.orderflow.model.Order;
import com.orderflow.config.OrderFlowProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    private final Map<Long, Order> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);
    private final OrderFlowProperties props;

    public OrderController(OrderFlowProperties props) {
        this.props = props;
    }

    @PostMapping
    public ResponseEntity<Order> create(@RequestBody OrderRequest req) {
        if (req.quantity() > props.maxItemsPerOrder()) {
            return ResponseEntity.badRequest().build();
        }
        Long id = seq.getAndIncrement();
        Order order = new Order(id, req.productId(), req.quantity(), "PENDING", Instant.now());
        store.put(id, order);
        return ResponseEntity.status(HttpStatus.CREATED).body(order);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Order> get(@PathVariable Long id) {
        Order o = store.get(id);
        return o != null ? ResponseEntity.ok(o) : ResponseEntity.notFound().build();
    }

    public record OrderRequest(Long productId, Integer quantity) {}
}

Output:

TEXT
// Execution Successful

5. ConfigurationProperties Business Configuration

(1) ▶ Example: OrderFlowProperties

JAVA
// config/OrderFlowProperties.java
package com.orderflow.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;

@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
    int maxItemsPerOrder,
    Duration orderTimeout,
    String defaultCurrency
) {}

Output:

TEXT
// Execution Successful
YAML
# application.yml
spring:
  application:
    name: orderflow-service
  profiles:
    active: dev

orderflow:
  max-items-per-order: 100
  order-timeout: 30m
  default-currency: USD

6. Excluding Auto-Configuration and Custom Starters

(1) ▶ Example: Excluding Unwanted Automatic Configurations

JAVA
@SpringBootApplication(exclude = {
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class
})
@EnableConfigurationProperties(OrderFlowProperties.class)
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

Output:

TEXT
// Execution Successful
⚠️ Note: DataSource is excluded in this phase because we are not using a real database for the time being. This exclusion must be removed after JPA is introduced in Phase 2.


7. Organizing Postman Collections

(1) ▶ Example: Postman Test Script

BASH
# Product API tests
curl -X POST http://localhost:8080/api/v1/products \
  -H "Content-Type: application/json" \
  -d '{"name":"Laptop","price":999.99,"stock":50}'

curl http://localhost:8080/api/v1/products

curl http://localhost:8080/api/v1/products/1

# Order API tests
curl -X POST http://localhost:8080/api/v1/orders \
  -H "Content-Type: application/json" \
  -d '{"productId":1,"quantity":3}'

curl http://localhost:8080/api/v1/orders/1

# Config verification
curl http://localhost:8080/api/v1/health

Output:

TEXT
{"status":"ok","data":{}}
Test Scenario API Expected Status Code
Create Product POST /api/v1/products 201
Query Product List GET /api/v1/products 200
Query a Single Product GET /api/v1/products/1 200
Create Order POST /api/v1/orders 201
Excessive Orders POST /api/v1/orders (qty=200) 400

8. Comprehensive Example: Complete Basic Framework for OrderFlow

JAVA
// OrderFlowApplication.java
package com.orderflow;

import com.orderflow.config.OrderFlowProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties(OrderFlowProperties.class)
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

// OrderFlowProperties.java
package com.orderflow.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;

@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
    int maxItemsPerOrder,
    Duration orderTimeout,
    String defaultCurrency
) {}
YAML
# application.yml
spring:
  application:
    name: orderflow-service
  profiles:
    active: dev

orderflow:
  max-items-per-order: 100
  order-timeout: 30m
  default-currency: USD

---
# application-dev.yml
spring:
  datasource:
    url: jdbc:h2:mem:orderflow_dev
    username: sa
    password:
  h2:
    console:
      enabled: true
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true

logging:
  level:
    com.orderflow: DEBUG

---
# application-prod.yml
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:3306/orderflow
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

logging:
  level:
    com.orderflow: WARN
💻 Output:

TEXT
# Dev mode startup
$ java -jar orderflow-service.jar --spring.profiles.active=dev
Started OrderFlowApplication in 3.2 seconds

# Prod mode startup
$ java -jar orderflow-service.jar --spring.profiles.active=prod
Started OrderFlowApplication in 5.1 seconds

❓ FAQ

Q Can the project structure from the Phase 1 exercises be used in subsequent lessons?
A Yes. The framework built in this lesson serves as the foundation for Phases 2-5. Phase 2 will add JPA, a service layer, validation, and more, gradually enhancing the project.
Q Why is DataSource auto-configuration temporarily excluded?
A In this phase, we're using an in-memory map to simulate data storage. Once Spring Data JPA is introduced in Phase 2, a real data source will be required; at that point, you can remove the exclusion.
Q How should configuration differences between dev and prod be managed?
A Place common configurations in application.yml and environment-specific configurations in application-{profile}.yml. Always inject sensitive information (passwords, keys) using environment variables.
Q How can I verify that the Profile is active?
A The following 1 profile is active: "dev" will be printed in the startup log. You can also inject @Value("${spring.profiles.active}") into the Controller and return it.
Q What are the limitations of using record as a DTO?
A record is immutable and is not suitable for JPA entities (which require a parameterless constructor and mutable fields). However, it is very well-suited for use as request/response DTOs in the controller layer.
Q How should a Postman Collection be organized?
A Group resources by category (Products, Orders), add assertions (status codes, response fields) to each request, and use environment variables to manage the base URL.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty: ⭐): Complete all the code for this lesson, ensure that the dev environment starts successfully, and verify that all curl test commands pass.

  2. Advanced Exercise (Difficulty ⭐⭐): Add the PUT /api/v1/products/{id} and DELETE /api/v1/products/{id} endpoints, add the GET /api/v1/orders list endpoint to the OrderController, and implement filtering using the status query parameter.

  3. Challenge (Difficulty: ⭐⭐⭐): Create a orderflow-spring-boot-starter that wraps the OrderFlow health check endpoint (which returns the application name, version, profile, and JVM information), integrate it into the OrderFlow project, and verify that the auto-configuration takes effect.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏