Imported from Jbyanx/GardenKeep (
AGENTS.md). Install upstream withnpx skills add Jbyanx/GardenKeep. Copyright stays with the author.
AGENTS Guide
Scope and Entry Point
- Primary module is
garden-keep-api/; run Maven commands from that directory. - Java 21 + Spring Boot 3.5.13 (
garden-keep-api/pom.xml).
Architecture (Ports and Adapters)
- Code is organized as hexagonal layers under
com.jbyanx.gardenkeep:domain/model: core rules (Crop,GrowthStage,CropType).application/port/in: use-case API (RecordWateringUseCase).application/port/out: persistence abstraction (CropRepositoryPort).application/service: orchestration (CropWateringService).infrastructure/adapter/in/web: REST entrypoint (CropController) + DTO (WateringRequest).infrastructure/adapter/out/persistence: active adapter (PostgresCropRepositoryAdapter), legacy stub (InMemoryCropRepositoryAdapter), JPA entity (CropEntity), mapper (CropPersistenceMapper), and Spring Data repository (JpaCropRepository).infrastructure/config: manual@Beanwiring (BeanConfiguration).
- Dependency direction is inward: controllers/adapters call ports; domain has no Spring imports.
Main Data Flow (Watering)
- HTTP
POST /api/v1/crops/{id}/waterinCropControlleracceptsWateringRequest. - Controller calls
RecordWateringUseCase.waterCrop(id, soilDryAtSecondKnuckle). CropWateringServiceloads crop viaCropRepositoryPort.findByIdand throwsIllegalArgumentExceptionif missing.- Domain rule lives in
Crop.waterPlant(...); wet soil (false) throwsIllegalStateException. - On success, service persists through
CropRepositoryPort.save.
Project-Specific Conventions
- Business messages and many comments are in Spanish; preserve the current language style when touching nearby code.
- Domain methods accept time as a parameter (
Crop.waterPlant(..., LocalDateTime wateringTime)) to keep tests deterministic. - Spring wiring is explicit in
infrastructure/config/BeanConfiguration(manual@Beanfor service,@Qualifier("postgresCropRepositoryAdapter")selects the active adapter). - Active persistence adapter is
PostgresCropRepositoryAdapterbacked by PostgreSQL via Spring Data JPA. The in-memory adapter (InMemoryCropRepositoryAdapter) is kept as a reference/fallback but is not wired. - Database connection is configured through environment variables (
DB_USERNAME,DB_PASSWORD) and Spring profiles; thedevprofile targetslocalhost:55432.
Build, Test, and Run
- Use the Maven Wrapper from the module directory:
cd garden-keep-api
./mvnw test
./mvnw spring-boot:run
./mvnw -Dtest=CropWateringServiceTest test
- JaCoCo coverage report is generated during the
testphase atgarden-keep-api/target/site/jacoco/index.html. - A
docker-compose.ymlat the module root spins up PostgreSQL 15 on port55432:
cd garden-keep-api
docker compose up -d
Quick Integration Check
- Start the database with Docker Compose, then run the app, and POST to any existing crop UUID:
curl -X POST "http://localhost:8080/api/v1/crops/{cropId}/water" \
-H "Content-Type: application/json" \
-d '{"soilDryAtSecondKnuckle": true}'
- Request/response contract is currently simple string responses from
CropController(no global exception mapper yet).
Extension Hotspots
- To swap the database backend, create a new adapter implementing
CropRepositoryPortunderinfrastructure/adapter/out/...and update the@QualifierinBeanConfiguration;application/servicestays unchanged. - If introducing new use cases, mirror the existing pattern: new
application/port/ininterface → service implementation → web adapter translation layer.