The Online Bookstore API provides a robust platform for managing an online book retail system.
It handles book inventory management, enabling seamless tracking and updating of book stock.
The API supports purchase processing and dynamic book pricing, ensuring accurate transactions and flexible pricing strategies.
Additionally, it manages customer loyalty points, rewarding repeat customers with points for purchases, which can be redeemed for discounts or perks, enhancing user engagement and retention.
- Oracle Java 21.0.7 (2025-04-15 LTS)
- Apache Maven 3.9.9
- Spring Boot 3.4.5
- OpenAPI 3.0.4
-
Clone the repository:
git clone https://github.com/pedrazamiguez/online-bookstore-api.git cd online-bookstore-api/online-bookstore
-
Build and test the entire project using the Maven Wrapper:
./mvnw clean install -U
This command will compile the code, run tests, and package the application into a JAR file.
-
Run the application using the Maven Wrapper:
./mvnw -pl online-bookstore-boot spring-boot:run
Alternatively, you can run the packaged JAR file directly:
java -jar online-bookstore-boot/target/online-bookstore-boot-0.0.1-SNAPSHOT.jar
The application will start on http://localhost:8080.
-
Swagger UI: http://localhost:8080/swagger-ui
Use this interface to explore and test the API endpoints. -
Postman Collection: (Alternative to Swagger) A Postman collection is included in the
dist/postman
directory: OnlineBookstoreAPI.postman_collection.json.
You can import it into Postman to quickly test the API endpoints. -
Authentication: All endpoints are secured with Basic Auth.
Use one of the test users below to authenticate:Username Role Password bob
USER 12345678 alice
USER 12345678 admin
ADMIN 12345678 superadmin
ADMIN 12345678 ADMIN users can perform both USER and ADMIN operations.
-
H2 Database Console: http://localhost:8080/h2-console
- Driver Class:
org.h2.Driver
- JDBC URL:
jdbc:h2:mem:bookstoredb
- User Name:
sa
- Password: (leave blank)
A dataset is preloaded with sample data for testing purposes. You can view and manipulate the data directly in the H2 console.
- Driver Class:
This project was built as a showcase, prioritizing simplicity and clarity over production-grade complexity. Below are some key decisions and design choices made during development:
- Encourages separation of concerns between business logic, data access, and APIs.
- Provides long-term scalability and maintainability, especially valuable when business rules evolve frequently.
- API contract designed first using OpenAPI.
- OpenAPI Generator was used to rapidly scaffold REST controllers and client/server interfaces.
- This approach allows for faster iteration and smoother collaboration with front-end or third-party consumers.
- In-Memory Authentication: Users are stored in the application configuration for simplicity.
- H2 Database: Chosen for fast, in-memory storage that resets with every restart.
- No Caching Layer: Deliberately omitted to focus on core API behavior.
- No External Services: No real payment, messaging, or third-party integrations are used.
In a production setting, the following components would be implemented or replaced:
- JWT or OAuth2 for authentication and authorization
- PostgreSQL or MongoDB for persistent data storage
- Redis for caching
- ElasticSearch for advanced search features
- Microservice Architecture with service discovery and API gateway
The API uses the Strategy Pattern to calculate the subtotal of a book depending on its type.
Each book type (NEW_RELEASE
, REGULAR
, OLD_EDITION
) has a specific pricing strategy encapsulated in its own class.
This approach was chosen for the following reasons:
- Maintainability: Business rules for pricing can evolve independently for each book type without altering core logic.
- Extensibility: New strategies (e.g., promotional discounts, dynamic pricing) can be added without modifying existing code.
- Encapsulation: Keeps the pricing logic out of the data layer and within a well-defined, testable context.
Although this logic could have been embedded into the database (e.g., storing discount percentages alongside book types), the Strategy Pattern allows for more flexible and scalable behavior. For example, future discount rules might depend on publisher, publication year, customer status, or other properties — all of which can be more easily handled in code than in static database rules.
public interface SubtotalPriceService {
String getBookTypeCode();
PayableAmount calculateSubtotal(OrderItem orderItem);
}
This design choice aligns with the overall Hexagonal Architecture, isolating domain logic from infrastructure and making it easier to evolve the system over time.
The core logic for completing a purchase is encapsulated in the PerformPurchaseUseCase
, which orchestrates the
following steps:
-
Order Retrieval
The order associated with the currently authenticated user is retrieved. -
Order Validation
Ensures the order contains at least one item. If not, an error is returned. -
Stock Verification
For each order line, the system checks whether there is sufficient stock available. -
Price Calculation & Discounts
Applies pricing strategies depending on the book type (NEW_RELEASE
,REGULAR
,OLD_EDITION
), calculating subtotals and applying relevant discounts. -
Simulated Payment Processing
Mimics the behavior of an external payment provider. In a production system, this would involve real-world integrations. -
Simulated Shipping Process
Represents the logistics step — preparing the order for shipping. In a real-world app, this would involve warehouse and courier system integrations. -
Order Placement
The order status is updated (e.g., fromCREATED
toPURCHASED
) and all necessary database records are updated accordingly. -
Inventory Update
Book stock quantities are adjusted based on the purchased items. -
Loyalty Points Calculation
The customer’s loyalty points are updated based on the purchase value and configured rules.
Architectural Notes
This orchestration is handled using the Chain of Responsibility Pattern:
- Each step (e.g., stock check, payment, shipping) is encapsulated in its own processor.
- These processors are linked in a chain, with each one performing its task and passing control to the next.
- This design promotes modularity, reusability, and separation of concerns, making it easier to handle failures, retries, or alternate flows (e.g., different payment providers or loyalty systems).
By abstracting each operation as an independent handler, you gain flexibility to plug in, replace, or skip steps dynamically depending on business rules or order properties.
- Pagination and filtering for list endpoints
- Order history and purchase analytics
- Stock management system to track book locations across shelves, sections, or warehouses
- Real-time inventory synchronization to prevent overselling and streamline restocking operations
- Integration with external book metadata providers (e.g., ISBN DB)
- Loyalty program rules engine for advanced reward systems
- Implementation of customer loyalty point redemption logic
- Expansion of end-to-end integration tests across critical flows