Pharmacy scanner / backend

Where the modules
could go deeper.

20/09/2026 · be/ · chore/backend-cleanup · 2f03afb1 · read-only review
moduleseamleaking transport or repeated policydeep module
01
Strongmocklocal-substitutable

Deepen Scan Invoice continuation

Before · worker borrows HTTP machinery
flowchart TD
 J["Document job worker"] --> R["Synthetic HTTP request
actor context + durable flag"] R --> H["Scan Invoice continuation
inside HTTP handler"] H --> E["OCR · config · draft · quota"] H --> W["ResponseWriter + JSON"] W --> J classDef leak fill:#fff6f2,stroke:#b34b3d,color:#8e382e; class R,W leak; linkStyle 0,1,3,4 stroke:#b34b3d,stroke-width:1.7px;

Both owners use this detour. Pharmacy preserves an invoice conflict through a private ResponseWriter field.

After · processing stays inside Document jobs
flowchart TD
 H["HTTP adapter: submit choice / wait"] --> J["Document job scheduler + worker"]
 J -. existing processor seam .-> C["Deep Scan Invoice continuation module
replay · OCR · config · draft · quota"] C --> A["Provider and persistence adapters"] classDef deep fill:#24302d,stroke:#175b44,stroke-width:4px,color:#ffffff; class C deep;

Deepen each owner's continuation implementation; retain its specific Distributor or Supplier rules.

Problem

The worker must understand an HTTP request, middleware context, response bytes and status codes to run an in-process continuation.

Solution

Concentrate continuation behavior behind the existing processor seam and keep transport encoding in its adapter.

Deletion test

Delete the synthetic request and ResponseWriter modules; their transport complexity disappears once continuation returns its result directly.

Seam and adapters

Keep the Document job interface. Existing production and fake OCR adapters justify the internal seam; PostgreSQL tests exercise durable effects.

The interface is the test surface

Retain replay, quota, actor-scope and duplicate-invoice outcomes at the processor interface; keep HTTP submission and waiting checks at the HTTP adapter.Existing evidence: scan_resume_handler_test.go:364,376,416 · pharmacy_document_job_processor_test.go:54 · distributor replay recovery at scan_resume_handler.go:471.

Preserves ADR 0023. HTTP continues the same durable job; the deep module does not introduce a second scheduler or direct HTTP processing path.
02
Strongmocklocal-substitutable

Concentrate Trusted classification orchestration

Before · the same sequence in two callbacks
Pharmacy Scan Invoice
Reserve identity
Resolve route
Choose callback context
Sequence product lines
Accept facts + classification
Map results + enqueue
Distributor Scan Invoice
Reserve identity
Resolve route
Choose callback context
Sequence product lines
Accept facts + classification
Map results + enqueue

Product-photo processing repeats routing and acceptance setup too, but has different input and result needs.

After · one module owns the shared sequence
Pharmacy adapter
Distributor adapter
scan-processing seam
Trusted classification
orchestration module
Line order and per-product sequencing
Lease-fenced fact acceptance
Specialist route precedence and result mapping

Catalog, Medicine, Pharmacy FMCG, Device and Surgical modules retain their current responsibilities.

Problem

Application setup repeats rules for route precedence, worker-context lifetime, acceptance callbacks and queue scheduling across the two owners.

Solution

Move the shared sequence into one module; keep owner-specific identity access in adapters and review product-photo reuse separately.

Deletion test

Moving callbacks into separate files only moves complexity. Delete the repeated orchestration once one implementation handles both owner adapters.

Seam and adapters

Two real owner adapters justify the seam. The catalog interface still owns classification receipts and atomic acceptance; specialist modules own their facts.

The interface is the test surface

Move the repeated-product, bounded-concurrency and failure-recovery scenarios to the module interface; retain a small application-wiring check and the catalog acceptance tests.Existing evidence: internal/app/scan_specialist_budget_test.go:22,83,171 creates a full Application; scan_classification_test.go:16 checks both owners; modules/catalog/classification_test.go:112 checks replay.

Preserves ADRs 0021, 0023 and 0034. Keep ProductKind storage ownership, Document job lease fencing, and classification-plus-master acceptance in one transaction.
03
Worth exploringlocal-substitutable

Remove shallow Purchase read modules

Before · three calls to the same validator
HTTP adapterparse + normalize
Shallow Purchase read modulenormalize + forward
Purchase query modulenormalize + query
PostgreSQLscoped Medicine / FMCG reads

The shallow modules expose query-module types and add no distinct purchase behavior.

After · callers enter a validated read module
HTTP adapter · parse and render
existing purchase-read seam
Deep Purchase read module
Validate every caller's query
Tenant scope · period rules · composition
PostgreSQL implementation

Pharmacy and distributor keep separate reads and Purchase origin semantics.

Problem

Each middle module repeats normalization already performed by its caller and callee, then forwards the same repository query type.

Solution

Remove the two pass-through modules and redundant business validation calls; keep validation mandatory at each Purchase read entry point.

Deletion test

Deleting these modules removes constructors and repeated calls; no distinct purchase rule needs to move elsewhere.

Seam and adapters

Reuse the existing read seam. Run behavior tests against real test PostgreSQL; keep a fake adapter only for HTTP parsing and status checks.

The interface is the test surface

Keep tenant-isolation, mixed-purchase membership, period and intake tests through the read interface; delete duplicate pass-through tests only after their assertions survive there.Existing evidence: pharmacy and distributor handlers/purchases_report_integration_test.go cover membership, payment modes, counterparties and intake; both middle modules have validation and empty-period tests.

Preserves ADRs 0008 and 0021 and the existing cross-spine read allow-list. Keep validation for non-HTTP callers. The benefit is modest; do this when these reads next change.
Review evidence: current source and existing test code, relevant domain definitions and ADRs, plus read-only staging aggregates. Staging has classification rows for both owner kinds and no current Document job rows. The recommendations identify architectural friction; they do not claim a reproduced runtime failure. Backend tests were inspected, not run. No interface design or backend edits are included.
Top recommendation

Start with Scan Invoice continuation.

It removes a concrete transport detour from both worker paths, with existing replay and quota tests to protect the behavior and no change to Document job ownership.

Explore candidate 01 ↑