Skip to content

Feature Implementation Plan: Unify API Schemas and DTOs

📋 Todo Checklist

  • [ ] Clarify the roles of ApiSchema and DTO files in the project's documentation.
  • [ ] Synchronize ApiSchema/Roaster.php with DTO/Api/RoasterDTO.php.
  • [ ] Synchronize ApiSchema/CoffeeBean.php with DTO/Api/CoffeeBeanDTO.php.
  • [ ] Audit all other ApiSchema and DTO pairs for inconsistencies.
  • [ ] Update CoffeeBeanRepository::findByRequest to ensure it returns CoffeeBeanDTOs.
  • [ ] Final Review and Testing.

🔍 Analysis & Investigation

The Role of ApiSchema vs. DTO

A core point of confusion is the presence of two sets of similar-looking classes. Their roles are distinct: - src/DTO/Api/*.php (Data Transfer Objects): These classes define the actual structure of data returned by the API. They are the source of truth for what the frontend receives. - src/ApiSchema/*.php (OpenAPI Schema Definitions): These classes are purely for documentation. They use attributes to describe the API's JSON response shape for generating the OpenAPI (Swagger) documentation.

The frontend is supposed to use the data structure defined by the DTO. The ApiSchema should be a perfect mirror of that DTO to ensure the documentation is accurate.

Identified Inconsistencies

The key issue is that the ApiSchema definitions have drifted from the DTO definitions, leading to incorrect API documentation.

  1. Roaster vs. RoasterDTO: As identified previously, the main inconsistency was caused by some endpoints returning raw entities instead of DTOs. The ApiSchema/Roaster.php and DTO/Api/RoasterDTO.php are actually quite similar, but the documentation was being applied to inconsistently serialized objects.
  2. CoffeeBean vs. CoffeeBeanDTO: There are significant differences here:
    • Missing in Schema: ApiSchema/CoffeeBean.php is missing nonStandardFlavorNotes, originType, singleOrigin, and status.
    • Incorrect Nullability: description, url, suitableForEspresso, and suitableForFilter have incorrect nullability settings in the schema compared to the DTO.
    • Incorrect Nested Types: The schema incorrectly points to other ApiSchema classes (e.g., Roaster::class) instead of describing the structure of the nested DTOs (e.g., RoasterDTO).

This audit reveals a systemic problem: the documentation does not reliably reflect the actual API responses.

📝 Implementation Plan

Prerequisites

No special prerequisites are required.

Step-by-Step Implementation

  1. Step 1: Synchronize ApiSchema/Roaster.php

    • File to modify: src/ApiSchema/Roaster.php
    • Changes needed: Review every property in DTO/Api/RoasterDTO.php and ensure ApiSchema/Roaster.php matches it exactly in terms of property names, types, and nullability. The nested crawlConfigs property should be explicitly defined to match the RoasterCrawlConfigDTO structure, not just a reference to another schema class.
  2. Step 2: Synchronize ApiSchema/CoffeeBean.php

    • File to modify: src/ApiSchema/CoffeeBean.php
    • Changes needed:
      • Add the missing properties: nonStandardFlavorNotes (as an array of strings), originType (string), singleOrigin (boolean), and status (string).
      • Correct the nullability for description, url, suitableForEspresso, and suitableForFilter to match the DTO.
      • Update all ref: new Model(...) properties to point to the correct DTO's schema definition (e.g., the roaster property should reference the now-corrected ApiSchema/Roaster.php).
  3. Step 3: Audit and Synchronize All Other API Entities

    • Files to modify: All files in src/ApiSchema/ and potentially src/DTO/Api/.
    • Changes needed: Systematically go through every DTO in src/DTO/Api/ and ensure its corresponding ApiSchema file is an exact match. This includes, but is not limited to:
      • Country vs. CountryDTO
      • Price vs. PriceDTO
      • Region vs. RegionDTO
      • And all others.
  4. Step 4: Correct the Return Value in CoffeeBeanRepository

    • File to modify: src/Repository/CoffeeBeanRepository.php
    • Changes needed: As per the original plan, ensure the executeCoffeeBeanQuery method uses the entityToDtoMapper to map the final list of entities to CoffeeBeanDTOs before returning them. This is the crucial step that ensures the API actually returns the correctly structured data that the documentation now describes.
    // src/Repository/CoffeeBeanRepository.php
    // ... inside executeCoffeeBeanQuery method ...
    
    $entities = $entitiesQb->getQuery()->getResult();
    
    // Ensure this mapping is performed
    $coffeeBeanDtos = $this->entityToDtoMapper->mapCoffeeBeansToDtos($entities);
    
    return [
        'items'      => $coffeeBeanDtos,
        'totalItems' => $totalItems,
    ];
    

Testing Strategy

  1. API Documentation Review:

    • After making the changes, regenerate the OpenAPI documentation.
    • Thoroughly review the schemas for CoffeeBean, Roaster, and other entities in the Swagger UI. Verify that all properties, types, and nullability constraints match the DTO definitions.
  2. API Endpoint Testing:

    • Make a GET request to /api/coffee-beans.
    • Confirm that the structure of the roaster object and other nested objects within each coffee bean in the response now perfectly matches the structure returned by their direct endpoints (e.g., /api/roasters/{id}).
    • Verify that all fields documented in the updated schema are present in the response, and that nullability rules are respected.

🎯 Success Criteria

  • The API's OpenAPI (Swagger) documentation is 100% accurate and reflects the true structure of the JSON responses for all endpoints.
  • The /api/coffee-beans endpoint returns a roaster object that is identical in structure to the one returned by /api/roasters/{id}.
  • There is a clear and documented understanding that DTOs define the API's true shape, and ApiSchema files are for documenting that shape.