This is a E-Commerce Backend Platform intended to demonstrate a clean, layered backend architecture. While the initial setup is straightforward, the system is built with a Scalable First mindset, allowing for easy adding new features.
- Containerized Environment: Fully Dockerized for consistent deployment across any infrastructure.
- Database Isolation: Decoupled PostgreSQL instance with persistent volume mapping.
- Environment-Driven: All configurations are managed via
.envfor security and flexibility.
- Runtime: Java 17
- Framework: Spring Boot 3+
- Web: Spring Web (RESTful API)
- Security: Spring Security (Securing the application with basic authentication)
- Data: Spring Data JPA (Hibernate)
- Database: PostgreSQL
- DevOps: Docker & Docker Compose
- Build Tool: Maven
- Security: Integration of Spring Security with JWT for stateless authentication (DONE).
- Migrations: Liquibase for version-controlled database schema management.
- Messaging: Spring SMTP for automated order confirmation and registration emails (DONE).
- Caching: Redis integration for product catalog performance.
./scripts/buildContainer.shdocker compose up- After running containers deployment
- go to your browser
- Category Management.
- Product Management.
- Customer registration.
- Customer Login
- Securing Endpoints with JWT Access Tokens.
- Using HATEOS for linking relative endpoints.
- Sending emails with Spring SMTP.
- Sending Welcome mail after new customer registration.
- Cart Management.
- Checkout (Place Order).
- Sending Welcome Email after customer registration.
- Sending confirmation email after order checkout.
- User can login and JWT Access Token.
- OpenAPI Documentation.
- Using GlobalExceptionHandler for centralized exception handling
- Creating initial exceptions for Clear and Documented API responses
- Initial required entities for the project
- Fetching data lazily to avoid fetching unnecessary data
- Using @NameEntityGraph to efficiently fetch data from the Database.
- Initial JPA repository.
- Using JpaRepository for Paging and Sorting & other JPA features.
- Using the naming convention IServiceName to separate the service from it's implementation better than using ServiceNameImp Convention making the code cleaner and robust.
- Using Spring HATEOS to add links to relevant operations.
- Since PostgreSQL gives you the choice of creating foreign key indexes, we created foreign key indexes on the most selective columns.
sequenceDiagram
autonumber
title Spring Security Basic Authentication Flow (Authenticated Request)
actor Client
participant Basic_Auth_Filter as Basic Authentication Filter
participant Auth_Manager as Authentication Manager
participant Auth_Provider as AuthenticationProvider
participant User_Details_Service as UserDetailsService (JPA)
participant Security_Context_Holder as Security Context Holder
participant Controller as Secured Controller (Order/Cart)
Client->>Basic_Auth_Filter: GET /api/orders (Authorization: Basic base64)
Note over Client, Basic_Auth_Filter: Request contains credentials in Header.
activate Basic_Auth_Filter
Basic_Auth_Filter->>Basic_Auth_Filter: Extract Username & Password
Basic_Auth_Filter->>Auth_Manager: authenticate(Token)
activate Auth_Manager
Auth_Manager->>Auth_Provider: authenticate(Token)
activate Auth_Provider
Note right of Auth_Provider: The standard Provider for UserDetails.
Auth_Provider->>User_Details_Service: loadUserByUsername(username)
activate User_Details_Service
User_Details_Service-->>Auth_Provider: return UserDetails (from DB)
deactivate User_Details_Service
Auth_Provider->>Auth_Provider: Check password
Auth_Provider-->>Auth_Manager: Authentication (Authenticated=true)
deactivate Auth_Provider
Auth_Manager-->>Basic_Auth_Filter: Authentication Object
deactivate Auth_Manager
Basic_Auth_Filter->>Security_Context_Holder: setAuthentication(auth)
Note right of Security_Context_Holder: User is now globally "Logged In" for this thread.
Basic_Auth_Filter->>Controller: Proceed to Method or Next filter
deactivate Basic_Auth_Filter
activate Controller
Controller-->>Client: 200 OK (Response DTO)
deactivate Controller
sequenceDiagram
autonumber
title Spring Boot: General Global Exception Handling (RuntimeException)
actor Client
participant Dispatcher_Servlet as Dispatcher Servlet
participant Controller as RestController
participant Service as Service
participant Global_Handler as Global Exception Handler (@RestControllerAdvice)
Client->>Dispatcher_Servlet: HTTP Request (POST/GET/PUT/DELETE)
activate Dispatcher_Servlet
Dispatcher_Servlet->>Controller: Route to Controller Method
activate Controller
Controller->>Service: Execute Business Logic
activate Service
Note over Service: [Transaction Started]
alt Business Rule Violation / Data Error
Service-->>Service: Logic fails (e.g., Null, Conflict, Logic Error)
Note right of Service: Throws RuntimeException (or Subclass)
Note over Service: [Transaction Rollback Triggered (If exists)]
Service-->>Controller: Propagate Exception
deactivate Service
Controller-->>Dispatcher_Servlet: Propagate Exception
deactivate Controller
Note over Dispatcher_Servlet: Exception detected by DispatcherServlet
Dispatcher_Servlet->>Global_Handler: Intercept Exception
activate Global_Handler
Note right of Global_Handler: Search for @ExceptionHandler(RuntimeException.class)
Global_Handler->>Global_Handler: Build ErrorResponse DTO
Global_Handler-->>Dispatcher_Servlet: ResponseEntity (Body + HTTP Status)
deactivate Global_Handler
Dispatcher_Servlet-->>Client: Structured Error JSON (e.g., 4xx or 500)
%% else Success Flow
%% Service-->>Controller: Return Data/DTO
%% Note over Service: [Transaction Committed] (if Exists)
%% Controller-->>Client: 200 OK / 201 Created
end
deactivate Dispatcher_Servlet
sequenceDiagram
autonumber
title E-Commerce: Add to Cart (Transactional Flow with Controller-Level Error Handling)
participant Client
participant Controller as CartController
participant Service as CartService (Transactional)
participant Helper as CartHelper
participant Context as SecurityContextHolder
participant DB as Database Repository
Client->>Controller: POST /api/cart/addToCart (DTO)
activate Controller
Controller->>Service: addItemToCart(request)
activate Service
Note over Service: [Transaction Start]
Service->>Helper: fetchContextCustomerWithCart()
activate Helper
Helper->>Context: getContext().getAuthentication().getPrincipal()
Context-->>Helper: SecurityUser
Helper->>DB: findWithCartByUserId(userId)
Note right of DB: Deep Fetch: Cart + Items + Products
DB-->>Helper: Customer Entity
Helper-->>Service: Customer
deactivate Helper
alt Cart is Null
Service->>Helper: createNewCart(customer)
Helper-->>Service: new Cart
end
Note over Service: Guard 1: Is Cart Locked?
alt cart.getIsLocked() == true
Note over Service: [Transaction Rollback]
Service-->>Controller: throw LockedCartException <br/> (GlobalExceptionHandler)
Controller-->>Client: 409 Conflict
end
Service->>Helper: findValidateItemExistenceInCartByProductId(...)
alt Item Exists
Service->>Service: updateCartItem (Increment Qty)
else Item is New
Service->>Helper: fetchValidateProductAvailabilityById(id)
activate Helper
Helper->>DB: findById(productId)
DB-->>Helper: Product Entity
Note over Helper: Guard 2: isAvailable == false?
alt Product.getIsAvailable() == false
Helper-->>Service: throw ProductUnavailableException
Note over Service: [Transaction Rollback]
Service-->>Controller: throw ProductUnavailableException <br/> (GlobalExceptionHandler)
Controller-->>Client: 404 Not Found
end
Helper-->>Service: Valid Product
deactivate Helper
Service->>Service: Create & Add New CartItem
end
Note over Service: [Transaction Commit]
Note right of Service: Dirty check: Updates DB if needed
Service->>Controller: CartResponse (Mapped DTO)
deactivate Service
Controller-->>Client: 200 OK
deactivate Controller
sequenceDiagram
autonumber
title E-Commerce: Checkout & Place Order
participant Client
participant Controller as OrderController
participant Service as OrderService
participant CartService as CartService
participant Helper as OrderHelper
participant DB as Database Repositories
participant EmailService as EmailService
Client->>Controller: POST /api/orders/checkout (OrderRequest)
activate Controller
%% Transactional checkout starts
Controller->>Service: checkout(request)
Note over Service: [Transaction Start]
activate Service
%% Fetch Context
Service->>Helper: getContextCustomerWithAddressesAndCart()
activate Helper
Note right of Helper: Fetches SecurityUser & Deep Fetches Customer Graph
Helper-->>Service: Customer Entity
deactivate Helper
%% Transactional placeOrder starts
Service->>Service: placeOrder(request, customer)
activate Service
%% Guard 1: Address
Service->>Service: Stream Searching for Customer address.
Note over Service: Guard 1: Address Exists?
alt Address Not Found
Service-->>Service: Optional.empty()
Note over Service: [Transaction Rollback]
Service-->>Controller: throw ResourceNotFoundException <br/> (GlobalExceptionHandler)
Controller-->>Client: 404 Not Found
end
%% Guard 2: Empty Cart
Note over Service: Guard 2: Is Cart Empty?
alt cart == null || cartItems.isEmpty()
Note over Service: [Transaction Rollback]
Service-->>Controller: throw EmptyCartException <br/> (GlobalExceptionHandler)
Controller-->>Client: 400 Bad Request
end
%% Guard 3: Locked Cart
Note over Service: Guard 3: Is Cart Locked?
alt cart.getIsLocked() == true
Note over Service: [Transaction Rollback]
Service-->>Controller: throw LockedCartException <br/> (GlobalExceptionHandler)
Controller-->>Client: 409 Conflict
end
%% Lock Cart
Service->>CartService: cartService.saveAndFlush(cart) (isLocked = true)
CartService ->>DB: saveAndFlush
%% Create Order Items & Guard 4: Availability
Service->>Service: createOrderItems(cart.getCartItems())
Note over Service: Guard 4: Product Available?
alt Product.getIsAvailable() == false
Note over Service: [Transaction Rollback]
Service-->>Controller: throw ProductUnavailableException <br/> (GlobalExceptionHandler)
Controller-->>Client: 404 Not Found
end
Note right of Service: Calculates subtotals, creates OrderItems
%% Build and Save Order
Service->>Service: Build Order (set address, sum subtotals)
Service->>DB: orderRepository.saveAndFlush(order)
DB-->>Service: Saved Order Entity
%% Clear Cart
Service->>CartService: clearCart(cart)
activate CartService
Note right of CartService: Empties cart items (batch deletion)
CartService-->>Service: return
deactivate CartService
Service-->>Service: OrderResponse (Mapped DTO)
Note over Service: [Transaction Commit]
deactivate Service
%% Send Email (Outside Transaction)
Service->>Helper: sendOrderConfirmationEmail(...)
activate Helper
Helper-)EmailService: sendEmailAsync(to, subject, body)
Note right of EmailService: ? Asynchronous Execution (Fire & Forget)
Helper-->>Service: return (Non-blocking)
deactivate Helper
Service-->>Controller: OrderResponse DTO
deactivate Service
Controller-->>Client: 201 CREATED
deactivate Controller
View the OpenAPI Specification File
- Author: Abdalla Samir Khalifa
- Role: Systems Analyst & Backend Developer
- Contact:
- GitHub: @AbdallaSamirKhalifa
- Email: abdallasamirkhalifa@gmail.com
- Linkedin: Abdalla Khalifa

