Skip to content

Part 7 of 7 · Supplier lead time watcher series ~7 min read

Engineering reference: the supplier lead time watcher architecture

The first six posts are for the person deciding whether to build this. This one is for the person building it. Same system, no analogies: the services by name, the functions, the two tables, the calendar handling, and how the baseline is frozen.

Key takeaways

  • Single region, single account. Every resource is regional; nothing is global except the IAM roles.
  • 3 Lambda functions, each with its own execution role. No shared role, no wildcards on resources.
  • 2 DynamoDB tables, each keyed so the concurrency story is a condition expression rather than a lock.
  • One Bedrock model, called once, with a JSON schema it must fill or leave null.
  • Nothing always-on: no instance, no container, no provisioned capacity.

The system, by service name

The supplier lead time watcher drawn with AWS service namesThree boxes across the top outside the AWS account. Purchase orders, carrying five timestamps. Goods receipts, with dates and quantities. And Alerts and reports, a few a month. Inside the account, three groups. EventBridge firing on receipt and an API serving reports on request. Three Lambda functions named measure, distribute and watch. And two DynamoDB tables named observations and baselines. A note gives the region as us-east-1, one account, and states that baselines are frozen and re-based only by a named person.AWS ACCOUNTPurchase ordersfive timestampsGoods receiptsdates and quantitiesAlerts and reportsa few a monthEventBridge + APIon receipt,report on requestLambda x3measure, distribute, watchDynamoDB x2observations, baselinesingroundsoutus-east-1. One account. Baselines are frozen and re-based only by a named person.
Fig 1. The same shape as Part 1 with the service names filled in. Nothing here is new; it is the same three groups, named.
  • Compute
  • Database
  • Networking
  • Analytics

Region and account

  • Region: us-east-1. Chosen because SES inbound receipt rules exist in only a subset of regions and this one has the widest Bedrock model availability. If your data has to stay elsewhere, check both constraints before moving: inbound SES is the binding one.
  • Account: one. This is a small system, and a separate account per environment costs more in wiring than it saves. A dev and a prod stack in the same account, with distinct resource prefixes, is the right size here.
  • Everything is regional. The only global resources are the IAM roles and policies. There is no CloudFront, no global table and no cross-region replication, because nothing here has a latency or durability requirement that would justify them.

Lambda inventory

FunctionTriggerDoesTimeout / memory
lt-measureGoods receipt eventComputes the four segments in the supplier’s working days; applies exclusion rules30s / 512 MB
lt-distributeDynamoDB stream on observationsRebuilds the current distribution for that supplier and product60s / 1024 MB
lt-watchDynamoDB stream on observationsRuns the four drift gates against the frozen baseline; sends at most one alert per supplier-product per month60s / 512 MB

Splitting this into separate functions is not about modularity. It is that only one of them needs Bedrock permissions and only one is reachable from the public internet, and neither of those is true if it is one handler behind a router.

IAM, scoped

RoleAllowedOn
lt-measure-roledynamodb:Query, dynamodb:PutItemRead-only on purchase orders; writes observations
lt-distribute-roledynamodb:Query, dynamodb:UpdateItemObservations and baselines
lt-watch-roledynamodb:Query, ses:SendEmailRead-only; one verified identity

No role has a Resource: “*” on anything that writes, and every GetSecretValue grant names a single secret arn. That is why there is more than one secret rather than one JSON blob with everything in it.

DynamoDB schemas

Table: observations

PK   supplier#sku      S   hartley#brg-4mm
SK   received_at       S   2026-08-14
     raised_at         S   2026-08-01
     approved_at       S   2026-08-05
     sent_at           S   2026-08-05T18:40:00Z
     acknowledged_at   S   2026-08-06
     internal_days     N   4    — raised to sent
     ack_days          N   1    — sent to acknowledged
     fulfil_days       N   8    — acknowledged to received
     total_days        N   13   — raised to received
     excluded          S   null | expedited | backorder | amended
     partial           BOOL true if measured to completion of quantity

All day counts are the supplier’s working days, using their calendar
and cut-off. `excluded` rows are stored and left out of distributions.

Table: baselines

PK   supplier#sku      S
     period            S   2025-01-01..2025-12-31 — frozen, stated
     n                 N   31
     median_days       N   6
     p90_days          N   8
     min_days          N   4
     max_days          N   11
     ack_median_days   N   1
     rebased_by        S   a named person
     rebased_at        S

A rolling baseline would absorb the drift it exists to detect, which
is why re-basing is a deliberate act with a name attached.

Inbound and outbound

  • Purchase orders are read, never written. This system has no write access to the purchasing system at all.
  • Acknowledgement timestamps come from the supplier’s reply landing in a mailbox. It is the cheapest signal here and the most useful.
  • Supplier calendars and cut-offs are per-supplier configuration, entered once. Without them, every measurement is disputable.
  • Exclusion rules run at measurement time and store the reason, so a distribution can always be explained by pointing at what was left out.

The model call

  • There is no model in this system. Everything here is date arithmetic and percentiles over small samples.
  • The tempting use is forecasting the next lead time. At twenty observations per supplier-product, a forecast adds confidence and no information over the observed percentile.
  • A second tempting use is classifying why a supplier slipped. The answer comes from asking them, and Part 5 is about why that conversation is the output.
  • Reading acknowledgement emails to extract a promised date is a defensible use, and the promised date is stored as a separate field from the observed one, never merged.
  • The cost page assumes none, which is why the whole bill is fixed.

Things worth knowing before you build it

  • Freeze the baseline. A rolling comparison window makes slow drift invisible, which is the one failure this system exists to prevent.
  • Use the supplier’s working calendar and cut-off time. Otherwise the first supplier who disputes a number will be right.
  • Store excluded observations rather than discarding them. “Why is this order not in the figures” needs an answer.
  • Report the sample count next to every percentile. A p90 from six orders and one from sixty look identical on a dashboard and mean different things.
  • Propose reorder point changes; never apply them. A silent working capital change across forty products is nobody’s decision.

That is the whole system. Seven posts, one diagram at a time, and nothing in it that needs a server.

All posts