Files
mercadodevida/work/artifacts/F-138/implementer.md
2026-08-22 10:22:25 +02:00

3.2 KiB

F-138 — Implementer evidence

What

Closed the GET /pricing/variants/:id 404 race: PricingService.seedVariantPrice (pricing owns its table) is injected into CreateProductVariant and called best-effort right after variant creation. The single constructor covers all 3 call sites (product autovariant, lazy variant GET, explicit POST variant).

Design recap (architect-approved)

  • PricingRepository + PricingService gain seedVariantPrice(variantId): Promise<void>.
  • PgPricingRepository.seedVariantPrice: INSERT pricing_variant_prices(variant_id, net_unit_amount_cents, offer_cents, cost_cents, vat_rate) VALUES ($1, 0, NULL, NULL, 'general') ON CONFLICT (variant_id) DO NOTHING — same column set as setVariantPrice (currency/created_at/updated_at via DDL defaults). Idempotent + parameterized.
  • CreateProductVariant ctor += pricing: PricingServicePort (type-only import of pricing public index → R1-clean); after variants.create, best-effort await this.pricing.seedVariantPrice(variant.id) (try/catch: variant already persisted; seed failure must not fail/roll back variant creation).
  • catalog.routes.ts: CatalogRoutesDeps += pricing; new CreateProductVariant(repository, variants, deps.pricing).
  • build-app.ts: hoist const pricing = createPricingService(deps.pool) above registerCatalogRoutes so it can be passed in; pricing routes + cart keep using it (unchanged).

Files changed (source)

  • pricing/domain/ports.ts — +seedVariantPrice on PricingService + PricingRepository.
  • pricing/application/pricing-service.ts — +seedVariantPrice delegate.
  • pricing/infrastructure/pg-pricing-repository.ts — +seedVariantPrice impl.
  • catalog/application/variant-use-cases.ts — import PricingServicePort; ctor + execute seed (best-effort).
  • catalog/api/catalog.routes.tsCatalogRoutesDeps += pricing: PricingServicePort; ctor passes deps.pricing.
  • app/build-app.ts — hoist pricing const; pass to registerCatalogRoutes deps.

Tests

  • NEW catalog/tests/variant-use-cases.test.ts (3 tests, runnable no DB): FakeProductRepository + FakeProductVariantRepository + FakePricingService — asserts seed-called-with-variant.id, no-seed-when-product-missing, seed-failure-doesn't-break-creation.
  • catalog.itest.ts AC itest (skipIf !hasDb): POST /products/:id/variantspricing_variant_prices row exists with net=0/vat=general/currency=EUR.
  • Collateral (fake fakes updated to satisfy new port method): pricing/tests/pricing-service.test.ts repo() factory, cart/tests/cart-service.test.ts + checkout/tests/checkout-service.test.ts PricingServicePort fakes — added seedVariantPrice: async () => undefined no-op.

Verification (fresh run)

  • npx tsc --noEmit0 errors (strict, noUncheckedIndexedAccess).
  • npx vitest run (targeted: variant/pricing/cart/checkout/product/build-app) → 16/16 pass.
  • npm test209 passed | 57 skipped (was 206 → +3 new; no regression in cart/checkout/pricing/build-app).
  • scripts/check-module-boundaries.mjs src0 NEW R1 violations. (Pre-existing R1 on security.routes.ts:14log-broadcaster is out of F-138 scope; git diff proves untouched.)
  • ./scripts/verify.shVERIFY_OK (pre-close).