This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is the Product Catalog Service, a Spring Boot microservice responsible for managing the complete product lifecycle including catalog management, inventory, product images, and pricing. It serves as the primary data source for product information in the Algashop platform.
Key Responsibilities:
- Product and category management (create, update, list, retrieve)
- Product inventory/stock management with movement tracking
- Product images upload and storage (S3/LocalStack)
- Pre-signed URLs for secure image access
- Product availability and pricing information
- Domain events publishing for product state changes
- REST API for product queries and information retrieval
Technology Stack:
- Java 25, Gradle 9.2.1, Spring Boot 4.0.x
- MongoDB (replica set) for product and inventory data storage
- Spring Security OAuth2 (Resource Server)
- Redis (caching layer)
- AWS S3 integration (LocalStack for local development) for image storage
- Spring Cloud Contract (contract-driven testing)
- TestContainers (integration test databases)
- Domain events pattern for state changes
The codebase follows a layered architecture with strong domain principles:
model/ → Domain layer (aggregates, value objects, business logic)
application/ → Application layer (use cases, services, event handlers)
infrastructure/ → Infrastructure layer (persistence, storage, messaging, security)
presentation/ → Presentation layer (REST controllers, DTOs, request/response handling)
Key Layers:
-
Domain Layer (
domain/model/)- Aggregates: Core business entities
Product— Product aggregate root with name, description, price, category, stock informationProductCategory— Category for organizing products
- Value Objects: Immutable domain concepts
ProductId— Typed product identifier (UUID)QuantityInStockAdjustment— Stock quantity changesStockMovement— Track inventory movement (add, remove, sell)Image— Product image metadata and S3 references
- Domain Services: Cross-aggregate business logic
StockService— Manages inventory adjustments and movements
- Domain Events: Published when product state changes
ProductAddedEvent— New product createdProductListedEvent— Product listed in catalogProductDelistedEvent— Product removed from catalogProductPlacedOnSaleEvent— Product marked on saleProductPriceChangedEvent— Product price updatedProductRestockedEvent— Product stock increasedProductSoldOutEvent— Product out of stock
- Repositories (Ports): Persistence contracts
ProductRepository— Product persistenceStockMovementRepository— Stock movement history
- Exceptions: Domain-specific exceptions
ProductNotFoundException— Product not found in catalogDomainException— Base exception class
- Aggregates: Core business entities
-
Application Layer (
application/)- Application Services: Use case orchestration
ProductApplicationService— Create, update, list, retrieve productsCategoryApplicationService— Manage product categoriesProductStorageApplicationService— Handle image uploadsUploadApplicationService— Generate pre-signed upload URLs
- Event Handlers: Respond to domain events
- DTOs & Mappers: Data transfer and transformation
- Message Publishing: Handle event publication to external systems
- Application Services: Use case orchestration
-
Infrastructure Layer (
infrastructure/)- Persistence: MongoDB repositories and configuration
MongoConfig— MongoDB connection and replica set setup- Product and category persistence adapters
SpringDataAuditingConfig— Automatic audit field tracking
- Storage: S3/LocalStack integration for image storage
S3ProductImageStorage— Upload/retrieve images from S3- Pre-signed URL generation
- Caching: Redis-based product caching
- Query result caching for frequently accessed products
- Security: OAuth2 token validation
- Current user extraction from security context
- Message Publishing: Domain event publishing
- Utilities: Locale configuration, message resolution
- Persistence: MongoDB repositories and configuration
-
Presentation Layer (
presentation/)- REST Controllers: HTTP endpoints
ProductController— Product CRUD and queriesCategoryController— Category managementProductImagesController— Image management endpointsUploadRequestController— Pre-signed upload URL generation
- Exception Handling: Centralized error response formatting
- DTOs: Request/response models
- REST Controllers: HTTP endpoints
- OAuth2 Resource Server: Validates incoming JWT tokens from authorization-server
- Scope-based access: Endpoints protected by OAuth2 scopes (e.g.,
SCOPE_products:read,SCOPE_products:write) - Spring Security method-level annotations for fine-grained access control
- Domain events are published when products transition state (listed, delisted, placed on sale, etc.)
- Application services listen to domain events and trigger side effects
- Events are used for asynchronous operations (notifications, analytics, synchronization with other services)
- Example: When
ProductListedEventfires, caches may be invalidated or notifications sent
The application uses layered profiles:
base— Common configurationdevelopment-env— Local development overridesdocker-env— Docker Compose overrides (MongoDB URLs, Redis configuration, S3 endpoints, etc.)production-env— Production settings
Activate via SPRING_PROFILES_ACTIVE=docker or in application.yml.
cd microservices/product-catalog
# Compile and run all tests (unit + integration)
./gradlew build
# Compile only
./gradlew classes
# Run unit tests only
./gradlew test
# Run integration tests (marked with *IT.java)
./gradlew integrationTest
# Run Spring Cloud Contract tests
./gradlew contractTest
# Run all test types (unit + integration + contract)
./gradlew check
# Build runnable JAR
./gradlew bootJar
# Build multi-platform Docker image (linux/arm64, linux/amd64)
./gradlew dockerBuild
# Run a single test class
./gradlew test --tests "com.algaworks.algashop.product.catalog.presentation.ProductControllerTest"
# Run with specific Spring profile
./gradlew build -Pprofile=docker-envStart infrastructure (MongoDB, Redis, LocalStack, etc.):
cd ../.. # Go to monorepo root
docker compose -f docker-compose.tools.yml up -dRun the application:
# From product-catalog directory
./gradlew bootRun
# With specific profile
SPRING_PROFILES_ACTIVE=docker ./gradlew bootRunThe server starts on port 8083 (configured in application.yml).
Required /etc/hosts entries (if not already set):
127.0.0.1 algashop-mongodb-1 algashop-mongodb-2 algashop-mongodb-3
127.0.0.1 algashop-localstack s3.algashop-localstack algashop-product-image.algashop-localstack
127.0.0.1 authorization-server
src/main/java/com/algaworks/algashop/product/catalog/
├── domain/
│ └── model/
│ ├── IdGenerator.java # ID generation utility
│ ├── DomainException.java # Base domain exception
│ ├── DomainEntityNotFoundException.java # Not found exception
│ ├── DomainEventPublisher.java # Event publishing
│ ├── category/
│ │ ├── ProductCategory.java # Category aggregate
│ │ └── [related classes]
│ └── product/
│ ├── Product.java # Product aggregate root
│ ├── ProductId.java # Typed product ID
│ ├── Image.java # Product image metadata
│ ├── ProductRepository.java # Repository port
│ ├── QuantityInStockAdjustment.java # Stock adjustment value object
│ ├── StockMovement.java # Stock movement tracking
│ ├── StockMovementRepository.java # Stock movement persistence port
│ ├── StockService.java # Stock management domain service
│ ├── ProductNameProjection.java # Product projection for queries
│ ├── ProductAddedEvent.java # Domain events
│ ├── ProductListedEvent.java
│ ├── ProductDelistedEvent.java
│ ├── ProductPlacedOnSaleEvent.java
│ ├── ProductPriceChangedEvent.java
│ ├── ProductRestockedEvent.java
│ ├── ProductSoldOutEvent.java
│ ├── ProductNotFoundException.java # Product-specific exception
│ └── [related classes]
├── application/
│ ├── ApplicationMessagePublisher.java # Event publishing
│ ├── PageModel.java # Pagination model
│ ├── ResourceNotFoundException.java # Application-level exception
│ ├── category/
│ │ ├── CategoryApplicationService.java
│ │ ├── CategoryQueryService.java
│ │ ├── [DTOs, handlers]
│ │ └── [related classes]
│ ├── product/
│ │ ├── ProductApplicationService.java # Product creation/update
│ │ ├── ProductQueryService.java # Product queries
│ │ ├── [DTOs, handlers]
│ │ └── [related classes]
│ ├── storage/
│ │ ├── ProductStorageApplicationService.java
│ │ ├── [related classes]
│ │ └── [DTOs]
│ ├── upload/
│ │ ├── UploadApplicationService.java # Pre-signed URL generation
│ │ ├── [related classes]
│ │ └── [DTOs]
│ ├── security/
│ │ └── CurrentUserProvider.java # Extract current user from security context
│ └── utility/
│ └── [utility classes]
├── infrastructure/
│ ├── async/
│ │ └── [async processing]
│ ├── cache/
│ │ ├── CacheConfiguration.java # Redis cache setup
│ │ └── [cache adapters]
│ ├── listener/
│ │ └── [domain event listeners]
│ ├── locale/
│ │ └── [localization support]
│ ├── message/
│ │ └── [message resolution]
│ ├── persistence/
│ │ ├── MongoConfig.java # MongoDB configuration
│ │ ├── SpringDataAuditingConfig.java # Audit field setup
│ │ ├── category/
│ │ │ └── [category persistence adapters]
│ │ ├── dataload/
│ │ │ └── [data loading utilities]
│ │ └── product/
│ │ ├── ProductMongoRepository.java # Spring Data MongoDB repository
│ │ ├── ProductRepositoryAdapter.java # Adapter implementing repository port
│ │ ├── StockMovementMongoRepository.java
│ │ ├── StockMovementRepositoryAdapter.java
│ │ └── [related classes]
│ ├── security/
│ │ └── [OAuth2 configuration]
│ ├── storage/
│ │ ├── S3ProductImageStorage.java # S3 integration
│ │ ├── PresignedUrlManager.java # Pre-signed URL generation
│ │ └── [AWS-related classes]
│ └── utility/
│ └── [utility functions]
├── presentation/
│ ├── ApiExceptionHandler.java # Global exception handler
│ ├── UnprocessableContentException.java # Validation exception
│ ├── ProductQuantityModel.java # Request model for quantities
│ ├── ProductController.java # Product REST endpoints
│ │ # GET /api/v1/products
│ │ # POST /api/v1/products
│ │ # PUT /api/v1/products/{productId}
│ │ # GET /api/v1/products/{productId}
│ ├── CategoryController.java # Category REST endpoints
│ │ # GET /api/v1/categories
│ │ # POST /api/v1/categories
│ ├── ProductImagesController.java # Image management endpoints
│ │ # POST /api/v1/products/{productId}/images
│ │ # DELETE /api/v1/products/{productId}/images/{fileName}
│ ├── UploadRequestController.java # Pre-signed URL endpoints
│ │ # POST /api/v1/upload-requests
│ └── [related classes]
└── ProductCatalogApplication.java
MongoDB collections are created on startup:
Collections:
product— Product documents with name, description, price, category, stockproduct-category— Product categoriesstock-movements— Audit trail of inventory movements
Key Features:
- Replica set support for transactions (docker-compose.yml configures 3-node replica set)
- Automatic createdAt/updatedAt fields via Spring Data Auditing
- TTL indexes for temporary collections if needed
To access MongoDB locally:
# Connect via Mongo shell (after starting docker-compose.tools.yml)
mongosh --host localhost:27017 --username admin --password admin
use algashop
db.product.find()Test structure mirrors source structure:
src/test/java/com/algaworks/algashop/product/catalog/
├── domain/
│ └── model/
│ ├── product/
│ │ └── ProductTest.java
│ └── ...
├── application/
│ ├── product/
│ │ └── ProductApplicationServiceTest.java
│ └── ...
└── presentation/
└── ProductControllerTest.java
-
Unit tests (
*Test.java): Test application services, domain logic, value objects- Use
@SpringBootTestfor tests needing Spring context - Use plain JUnit for pure domain logic
- Mock external dependencies (HTTP clients, repositories, storage)
- Use
-
Integration tests (
*IT.java): Use TestContainers for embedded MongoDB- Test persistence layer with real database
- Test application services with real repositories
- Use
@DataMongoTestfor repository-focused tests
-
Contract tests (
*ContractTest.java): Spring Cloud Contract verifier tests- Define contracts for API endpoints
- Auto-generate consumer stubs
Example:
@SpringBootTest
class ProductApplicationServiceTest {
@Test
void shouldCreateProductWithValidData() { ... }
@Test
void shouldThrowExceptionWhenProductNotFound() { ... }
}Aggregates: Self-contained units with invariants
Product— Owns product details, pricing, stock informationProductCategory— Category metadata and relationships
Value Objects: Immutable, no identity
ProductId— Typed ID instead of raw UUIDQuantityInStockAdjustment— Stock adjustment with validationImage— Product image metadata
Domain Events: Important state changes
- Published when product state changes (listed, delisted, price changed, restocked, etc.)
- Application services subscribe and handle side effects
- Example: When
ProductRestockedEventfires, cache is invalidated
Domain Services: Cross-aggregate logic
StockService— Manages inventory adjustments and tracks movements
Products transition through states:
CREATED → LISTED → ON_SALE / DELISTED
Status transitions enforce business rules (e.g., cannot go from ON_SALE back to CREATED).
- Stock tracked through
StockMovementrecords (audit trail) - Adjustments recorded for add, remove, sell operations
- Separate queries for stock availability
- Events published when stock levels change (restocked, sold out)
- Product images stored in S3/LocalStack
- Pre-signed URLs generated for secure access
- Async image processing if needed
- Metadata tracked in MongoDB
- Redis caches frequently accessed products
- Cache invalidation on product updates
- Configurable TTL per cache entry
- Ordering Service (port 8080)
- Queries products for shopping cart and order creation
- Calls
GET /api/v1/products/{productId}via ProductCatalogAPIClient
None directly, but integrates with:
- Authorization Server (port 8081) — OAuth2 token validation
- AWS S3 (LocalStack in development) — Image storage
- MongoDB Spring Data — Reactive/non-reactive data access
- Spring Security OAuth2 — Token validation
- AWS SDK — S3 integration
- Spring Cloud Contract — Contract-driven testing
- TestContainers — Embedded MongoDB for testing
- Lombok — Boilerplate reduction
- ModelMapper — DTO/Entity mapping
- Domain events: Published on product state changes
- Stock tracking: Movement history and audit trail
- Image management: S3/LocalStack integration with pre-signed URLs
- Caching layer: Redis caching for product queries
- Pagination: Page-based product listing with filters
- Add field to
Productaggregate indomain/model/product/ - Update MongoDB document mapping (if using custom serialization)
- Create application service method to update the property
- Add REST endpoint in
ProductController - Add validation using Bean Validation annotations
- Write unit test for service method
- Write integration test with MongoDB
- Create new domain event class (e.g.,
ProductDiscountedEvent) - Add event publishing logic to
Productaggregate - Create event handler in application layer if side effects needed
- Write tests for event publishing and handling
- Create storage adapter interface (e.g.,
ProductImageStorage) - Implement S3 adapter in
infrastructure/storage/ - Inject into application service
- Add mock implementation for testing
- Write integration test with LocalStack
- Add query method to
ProductRepositoryport - Implement in MongoDB adapter
- Create application service method combining multiple repositories if needed
- Add REST endpoint with pagination
- Write integration test querying the database
- Create event class (e.g.,
ProductLaunchDateSetEvent extends DomainEvent) - Publish from aggregate:
DomainEventPublisher.publishEvent(...) - Create event handler in application layer
- Subscribe handler via Spring
@EventListeneror@TransactionalEventListener - Test event is published and handler executes
- Product search using Elasticsearch or MongoDB text search
- Product recommendations based on purchase history
- Product reviews and ratings
- Bulk product import/export
- Advanced inventory forecasting
- Product variants (size, color, etc.)
- Real-time product availability sync with 3rd-party retailers
- Analytics dashboard for product performance
- Internationalization for product descriptions and categories
- Product digital assets/documentation