Skip to content

Conversation

@DanielMachadoVasconcelos
Copy link
Owner

Add Line Items Support to Place Order API

Summary

This PR introduces line items support to the Place Order API while maintaining full backward compatibility. The implementation follows a versioned API approach at the boundary layer, with a single, unversioned domain model.

Key Changes

🎯 API Versioning

  • V1 API (version=1.0.0 header): Maintains backward compatibility - existing clients continue to work unchanged
  • V2 API (default, no header required): New version supporting line items with automatic amount computation
  • Method overloading in PlaceOrderController handles version routing automatically

📦 Domain Model

  • Introduced LineItem value object (record) with validation
  • Updated Order domain record to include List<LineItem>
  • Updated OrderAggregate JPA entity with @OneToMany relationship to OrderLineItemEntity
  • Added OrderLineItemEntity JPA entity for persistence

🗄️ Database

  • New orders.line_items table with foreign key to orders.orders
  • Database migration: V20251115_1219__add_line_items.sql
  • Cascade delete ensures line items are removed with orders

🔄 Mapping Layer (MapStruct)

  • Request Mapping:
    • LineItemMapper: Converts LineItemRequestLineItem (domain)
    • PlaceOrderCommandMapper: Maps V1/V2 requests to PlaceOrderCommand with dependency injection via uses
  • Response Mapping:
    • LineItemResponseMapper: Single mapper for LineItemLineItemResponse (shared across endpoints)
    • PlaceOrderResponseMapper: Maps OrderPlaceOrderResponseV1/V2
    • SearchOrderResponseMapper: Maps OrderSearchOrderResponse
    • OrderAggregateMapper: Maps OrderAggregateOrder (including line items)

📝 DTOs

  • Request DTOs (com.ead.payments.orders.place.request):
    • PlaceOrderRequestV1: Renamed from PlaceOrderRequest (backward compatible)
    • PlaceOrderRequestV2: New version with List<LineItemRequest>
    • LineItemRequest: Request DTO for line items
  • Response DTOs:
    • PlaceOrderResponseV1: Response without line items
    • PlaceOrderResponseV2: Response with List<LineItemResponse>
    • SearchOrderResponse: Updated to include List<LineItemResponse>
    • LineItemResponse: Single shared response DTO in com.ead.payments.orders.response (used by both place and search endpoints)

🎨 Package Structure

  • Reorganized DTOs into request and response sub-packages
  • Consolidated LineItemResponse to a single location (com.ead.payments.orders.response)
  • All mappers use dependency injection via MapStruct's uses parameter

🧪 Testing

  • Updated existing tests to use PlaceOrderRequestV1 and PlaceOrderResponseV1
  • Added integration test: shouldAllowToPlaceAnOrderWhenLineItemsAreProvided() verifying:
    • V2 endpoint accepts line items
    • Amount is computed from line items
    • Line items are returned in response
  • All existing tests pass (backward compatibility verified)

📊 Observability

  • Added version tags to @Observed annotations for metrics tracking
  • V1: {"version", "1.0.0"}
  • V2: {"version", "2.0.0"}

Technical Details

Amount Computation

  • V1: Uses provided amount directly
  • V2: Computes amount from line items (sum(unitPrice * quantity)) if not provided
  • Validation ensures computed total matches provided amount (if provided)

Validation

  • Line item validation in LineItem record constructor
  • Order-level validation in OrderAggregate constructor
  • Request validation via Jakarta Bean Validation annotations

Backward Compatibility

  • ✅ V1 clients: No changes required, continue using version=1.0.0 header
  • ✅ V1 orders: Created with empty lineItems list
  • ✅ V2 orders: Created with provided line items, amount computed if not provided

Migration Notes

For API Clients

V1 Clients (No Changes Required)

POST /orders
Headers: version=1.0.0
Body: { "currency": "USD", "amount": 1000 }

V2 Clients (New)

POST /orders
Body: {
  "currency": "USD",
  "lineItems": [
    { "name": "Product A", "quantity": 2, "unitPrice": 500, "reference": "SKU-001" },
    { "name": "Product B", "quantity": 1, "unitPrice": 1000, "reference": "SKU-002" }
  ]
}

Default Behavior: Requests without version header default to V2

Database Migration

  • Migration runs automatically on application startup (Flyway)
  • No manual intervention required
  • Existing orders remain unchanged (line items will be empty for V1 orders)

Files Changed

Domain Layer

  • src/main/java/com/ead/payments/orders/LineItem.java (new)
  • src/main/java/com/ead/payments/orders/Order.java (updated)
  • src/main/java/com/ead/payments/orders/OrderAggregate.java (updated)
  • src/main/java/com/ead/payments/orders/OrderLineItemEntity.java (new)
  • src/main/java/com/ead/payments/orders/OrderPlacedEvent.java (updated)

API Layer

  • src/main/java/com/ead/payments/orders/place/request/PlaceOrderRequestV1.java (renamed)
  • src/main/java/com/ead/payments/orders/place/request/PlaceOrderRequestV2.java (new)
  • src/main/java/com/ead/payments/orders/place/request/LineItemRequest.java (new)
  • src/main/java/com/ead/payments/orders/place/response/PlaceOrderResponseV1.java (new)
  • src/main/java/com/ead/payments/orders/place/response/PlaceOrderResponseV2.java (new)
  • src/main/java/com/ead/payments/orders/response/LineItemResponse.java (new, shared)
  • src/main/java/com/ead/payments/orders/search/SearchOrderResponse.java (updated)

Mapping Layer

  • src/main/java/com/ead/payments/orders/place/mapping/LineItemMapper.java (new)
  • src/main/java/com/ead/payments/orders/place/mapping/PlaceOrderCommandMapper.java (updated)
  • src/main/java/com/ead/payments/orders/place/mapping/PlaceOrderResponseMapper.java (new)
  • src/main/java/com/ead/payments/orders/mapping/LineItemResponseMapper.java (new)
  • src/main/java/com/ead/payments/orders/mapping/OrderAggregateMapper.java (updated)
  • src/main/java/com/ead/payments/orders/search/mapping/SearchOrderResponseMapper.java (new)

Controllers & Services

  • src/main/java/com/ead/payments/orders/place/PlaceOrderController.java (updated)
  • src/main/java/com/ead/payments/orders/search/SearchOrderController.java (updated)
  • src/main/java/com/ead/payments/orders/place/PlaceOrderCommand.java (updated)
  • src/main/java/com/ead/payments/orders/search/SearchOrderService.java (updated)

Database

  • src/main/resources/db/migration/V20251115_1219__add_line_items.sql (new)

Tests

  • src/test/java/com/ead/payments/orders/place/PlaceOrdersControllerTest.java (updated)
  • Updated all test files to use new DTOs

Testing

  • ✅ All existing tests pass
  • ✅ V1 backward compatibility verified
  • ✅ V2 endpoint tested with line items
  • ✅ Integration test verifies line items in response
  • ✅ Database migration tested

Related Documentation

  • See docs/OBJECT_TREE.md for complete object relationship diagram
  • See PlanToIntroduceLineItems.md for detailed design rationale

@github-actions
Copy link

github-actions bot commented Nov 15, 2025

Test Results

34 tests  +1   18 ✅  - 15   3s ⏱️ -32s
14 suites ±0    0 💤 ± 0 
14 files   ±0   16 ❌ +16 

For more details on these failures, see this check.

Results for commit 0594513. ± Comparison against base commit 10385ac.

This pull request removes 2 and adds 3 tests. Note that renamed tests count towards both.
com.ead.payments.inventory.InventoryControllerTest ‑ Should reduce the product stock when a order is placed
com.ead.payments.orders.place.PlaceInvalidOrderControllerTet ‑ should not allow to place the order when the order is invalid
com.ead.payments.inventory.InventoryControllerTest ‑ initializationError
com.ead.payments.orders.place.PlaceInvalidOrderControllerTest ‑ should not allow to place the order when the order is invalid
com.ead.payments.orders.place.PlaceOrdersControllerTest ‑ Should allow to place an order when line items are provided

♻️ This comment has been updated with latest results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants