This project is a grocery store application built with a microservices architecture.
This application is designed using the microservices architectural style, where the system is decomposed into small, independent services. Each microservice is responsible for a specific business capability and can be developed, deployed, and scaled independently. This approach offers several benefits:
- Separation of Concerns: Each service encapsulates a specific domain or functionality (e.g., product management, cart, order processing, summary/receipt).
- Independent Deployment: Services can be updated or redeployed without affecting the entire system.
- Scalability: Individual services can be scaled based on demand.
- Technology Diversity: Each service can use the most appropriate technology stack or database for its needs.
- Resilience: improving overall system reliability.
- Product Service: Manages the product catalog and exposes product-related APIs.
- Cart Service: Handles shopping cart operations for users.
- Order Service: Manages order creation and processing.
- Summary Service: Generates purchase summaries and receipts.
All services communicate via REST APIs and are containerized for easy orchestration with Docker Compose. Each service has its own database, codebase, and can be tested and deployed independently.
- Java 21
- Maven
- Docker
- Docker Compose
The project uses PostgreSQL databases for each microservice, which are managed with Docker Compose. To start the databases, run the following command from the root of the project:
docker-compose up -d
For each microservice, you will need to create an application.properties
file in the src/main/resources
directory. You can do this by copying the application.properties.example
file:
cp microservices/<service-name>/src/main/resources/application.properties.example microservices/<service-name>/src/main/resources/application.properties
Note: The example files are pre-configured with the correct database credentials for the Docker Compose setup, so you won't need to make any changes to them.
You can run each microservice using the following Maven command:
mvn spring-boot:run -pl microservices/<service-name>
For example, to run the cart-service
:
mvn spring-boot:run -pl microservices/cart-service
The services will be available at the following ports:
- cart-service: 8081
- order-service: 8082
- product-service: 8083
- summary-service: 8084
git clone <repo-url>
cd clean-code-grocellery-app
docker-compose up
Access services at:
- Cart: http://localhost:8081
- Order: http://localhost:8082
- Product: http://localhost:8083
- Summary: http://localhost:8084
Service | Base URL | Swagger UI |
---|---|---|
Cart | http://localhost:8081 | http://localhost:8081/swagger-ui.html |
Order | http://localhost:8082 | http://localhost:8082/swagger-ui.html |
Product | http://localhost:8083 | http://localhost:8083/swagger-ui.html |
Summary | http://localhost:8084 | http://localhost:8084/swagger-ui.html |
Variable | Description | Default Value |
---|---|---|
POSTGRES_USER | DB username | grocellery |
POSTGRES_PASSWORD | DB password | grocellerypass |
POSTGRES_DB | DB name | grocery |
test-cart-service-secret | JWT secret for cart | dummy-cart-secret |
test-order-service-secret | JWT secret for order | dummy-order-secret |
test-product-service-secret | JWT secret for product | dummy-product-secret |
test-summary-service-secret | JWT secret for summary | dummy-summary-secret |
graph TD
CartService --> CartDB
OrderService --> OrderDB
ProductService --> ProductDB
SummaryService --> SummaryDB
Prometheus --> CartService
Prometheus --> OrderService
Prometheus --> ProductService
Prometheus --> SummaryService
Grafana --> Prometheus
To run all tests for a service:
mvn test -pl microservices/cart-service -Dspring.profiles.active=test
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000 (default login:)
- Product management with validation
- Shopping cart operations
- Flexible discount system
- Receipt generation
- Java 21
- JUnit 5 for testing
- Maven for build automation
- GitHub Actions for CI/CD
The application follows clean code principles with:
- Domain objects:
Product
,CartItem
- Core business logic: [
ShoppingCart
](src/main/java/grocery/Shopping
Planned enhancements with Spring Boot:
- RESTful API endpoints for cart operations
- Database integration with Spring Data JPA
- Product catalog management
- User authentication and authorization
- Shopping history and order tracking
- Discount rules management interface
- Web-based shopping interface
- Containerization with Docker
- Add Spring Boot dependencies to pom.xml
- Create service layer for business logic
- Develop repository layer for data persistence
- Implement REST controllers for API endpoints
- Add Spring Security for authentication
- Design database schema for products, orders, and users
- Create Docker configuration
- Implement unit and integration testing
This project is available under the MIT License.
Each microservice exposes interactive API documentation via Swagger UI. You can access these endpoints whether running the services locally or inside Docker containers (as long as the ports are mapped):
- cart-service: http://localhost:8081/swagger-ui.html or http://localhost:8081/swagger-ui/index.html
- order-service: http://localhost:8082/swagger-ui.html or http://localhost:8082/swagger-ui/index.html
- product-service: http://localhost:8083/swagger-ui.html or http://localhost:8083/swagger-ui/index.html
- summary-service: http://localhost:8084/swagger-ui.html or http://localhost:8084/swagger-ui/index.html
If the /swagger-ui.html
path does not work, try /swagger-ui/index.html
.
You can also view the raw OpenAPI spec at:
http://localhost:<service-port>/v3/api-docs
Each service exposes a health endpoint via Spring Boot Actuator:
- cart-service: http://localhost:8081/actuator/health
- order-service: http://localhost:8082/actuator/health
- product-service: http://localhost:8083/actuator/health
- summary-service: http://localhost:8084/actuator/health
If you get an empty reply or 401 error, make sure the service is running and that your security configuration allows unauthenticated access to /actuator/health
.
-
Ensure the service is running and mapped to the correct port (see
docker-compose ps
). -
If running inside Docker, make sure you are accessing the correct host port.
-
If you get an empty reply from
/actuator/health
, check your security configuration to allow public access to actuator endpoints. -
Check service logs with
docker logs <container-name>
for errors.
All microservices are secured with HTTP Basic authentication.
The Swagger UI and OpenAPI documentation endpoints are publicly accessible without authentication.
The product-service is preloaded with the following demo products for showcase purpose.
All microservices use JWT (JSON Web Token) authentication for securing APIs. Each service requires a unique JWT secret, which should be set via environment variables or configuration files. Never commit real secrets to version control.
- Each service should have a unique value for `JWT_SECRET`.
- These files are ignored by git (see `.gitignore`).
### Production Secrets
- Set `JWT_SECRET` as an environment variable or in a secure config file (never commit secrets).
- Example for Docker Compose:
```yaml
environment:
- JWT_SECRET=${JWT_SECRET}
- All Swagger UI and OpenAPI endpoints are accessible without authentication.
- In tests, a test-specific security config disables authentication for controller tests, so you do not need to provide tokens in test code.
- To test authentication logic, create dedicated integration/security tests.
- To rotate a secret, update the value in your environment or test properties and restart the service.