Compare Camera & Lens Specs, Size & Weight Side by Side
Compare cameras and lenses side by side: specs, size and weight, and prices in 7 currencies. Canon, Fujifilm, Leica, Nikon, Olympus, Panasonic, Sigma, Sony.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證A used-vehicle auction platform I built and run on Azure: a .NET 10 API, a React front end, and Azure SQL and Cosmos DB behind one set of ports. The architecture decision records and the test suite are served from inside the running app.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Built by one engineer with AI as a force multiplier, test driven: I specify every test before the AI writes
the first draft of the code against it, three suites of tests run once per version inside the one gate before anything rolls (the counts are in the testing section below, held to the suites
by a test), and eighty-two decision records carry the trade-off and the number behind each choice. What went
wrong is recorded too. Read how it was governed in
Built with AI, and what it all runs on, at
millisecond speeds on free-tier stores and one small container, in
Performance.檢視完整 README ↗A used-vehicle auction platform I built and run on Azure: browse 100,000 vehicles, open one, and bid against a simulated room of other bidders.
Live: theyard.stevenstout.biz, and the same build on Cosmos DB at theyard-cosmos.stevenstout.biz. The running version and commit: /api/version. My resume: /resume. Start here with ten minutes: Review in ten minutes, seven live stops in the order a reviewer asks, ending at the resume.
TheYard is a used-vehicle auction site on Azure where you can browse 100,000 vehicles and bid against a simulated room of other bidders. The same code runs on two kinds of database at once (Azure SQL Database and Azure Cosmos DB). Every version must pass one set of tests (the gate) before it ships.
What that is worth: a developer can read each choice beside the code and the test that hold it, and the organization gets a live system where no version reaches users without passing the same checks.
The suites hold 1797 xUnit tests, 589 Vitest tests at 1.0.4.132 and 196 Playwright tests. Every version reaches main through one gate, and the gate's results for 1.0.4.132 hold 4,453 test runs, with the xUnit suite booted on each store and the store-dependent browser specs run on Cosmos DB as well. I specify every test before the AI drafts the code against it.
| Suite | Framework | Count | What it covers |
|---|---|---|---|
| API | xUnit | 1797 | The bid rules, the auction schedule and every filter in Domain; the use cases in Application over hand-written fakes; the SQL and Cosmos DB adapters; and the real host booted in memory for every endpoint, the problem shape, accounts, persistence across a restart, the OpenAPI document and the served documents. |
| Frontend | Vitest | 589 at 1.0.4.132 | Presentation logic only, because the API owns the rules: status from server windows, formatting, the address bar round trip, the request cache, the account seam and the palette's contrast. |
| End to end | Playwright | 167 declared | The real stack in Chrome: the landing page, filters and Back, the sidebar and every document, the Admin tab, bids and the simulated room, accounts, the phone drawer, the keyboard path, and axe's eleven scans across nine views at WCAG 2.1 AA. |
How 4,453 is counted, from the gate's own results file for 1.0.4.132 (data/test-results.json): 589 Vitest tests, 1797 xUnit tests on SQLite and the same 1797 booted on Cosmos DB, carried forward from 1.0.4.131, the 12 that need the live Cosmos DB account, 203 browser runs on SQLite from the 196 declared tests (a loop runs one of them more than once) and 55 of those again on Cosmos DB, carried forward from 1.0.4.131, which is 4,453. The xUnit and browser counts above are read from the source; a test written after 1.0.4.132 first runs in the gate of the version that ships it. A browser test that passes only on Playwright's one retry is written 'r', never a pass, and a file holding one is refused, so every run counted here passed the first time.
The rule. Nothing reaches main without a green gate, and a red test stops the push. The ship pushes the version to a gate/<version> branch and gate.yml runs the gate there, on a GitHub runner; main moves only when it is green (ADR-098): format, lint and type checks, the SQL project, xUnit on SQLite, the eight live Cosmos DB tests, and then Vitest and the browser suite, one side after the other. Two passes run only when something they read changed: xUnit booted on Cosmos DB and the three store-dependent browser specs run when a change touches api/, infra/cosmos/ or one of those specs, and otherwise the results file carries them forward from the version whose gate ran them, marked with that version (ADR-068, the addendum of 22 September). The push is the deploy: deploy.yml builds the image and rolls the one web app that serves both sites and The Shed, and deploy-render.yml rolls the rendering service, and each checks the version, /readyz and the store before it finishes. ci.yml runs the same suites again on GitHub's runners for every push to main, beside the deploy and without holding it up, and for a pull request, which has had no gate.
The time. The target is five minutes (ADR-068), and every gate quoted here is over it: 831 seconds on 1.0.3.36 with every pass run, 657 seconds on 1.0.3.38 with the two store passes carried forward, 973 seconds on 1.0.3.39 with every pass run and 641 seconds on 1.0.3.59 with the two store passes carried forward, on a four-core laptop shared with the browser I work in. Every result ships with the version, test by test with its milliseconds; the Admin tab shows it. ADR-021 walks the three suites for a developer new to the stack.
One hundred and twenty-three decision records carry the trade-off and the number behind each choice. A decision record (ADR) is one short document per decision: the context, what was decided, what it cost, and an addendum when it stopped being true. Six to read first:
Requires Node 24 and the .NET 10 SDK, and Chrome for the browser suite. From a clean clone:
npm ci
npm start
dotnet test api/TheYard.slnx --filter "Store!=cosmos"
npm test
npm run test:e2e
npm start runs the API and the front end together and opens the browser. The xUnit filter leaves out the eight tests that need the live Cosmos DB account, the way CI does. The browser suite starts both servers itself.
Both unit suites, one command: npm run test:all runs the xUnit suite with the same filter (npm run test:api), then Vitest (npm test), each with its output captured, and prints one line per suite (the figures below are one run's, from when the command was written; the counts in the table above are today's):
TheYard - all tests
C# tests (xUnit) 943 passed 0 failed 37.3 s
TypeScript tests 419 passed 0 failed 10.6 s
ALL GREEN - 1362 tests passed
A failing suite prints everything it printed, then the summary, and the command exits non-zero, so nothing is hidden. npm run test:all:full runs the same two commands with their whole output. The browser suite is not part of it, because it needs the app running: run npm run test:e2e on its own, and it starts both servers itself.
In VS Code: open the folder, accept the extensions it recommends, press F5. The repository carries its editor setup in .vscode/ (the tasks, F5 for the API and the page, the Testing panel for all three suites, and The Shed from the same window), and Working in VS Code walks through it, with screenshots.
Built by one engineer, with AI drafting against my tests and records. I specify every test before the AI writes the first draft of the code against it, three suites of tests run once per version inside the one gate before anything rolls (the counts are at the top of this page, held to the suites by a test), and one hundred and twenty-three decision records carry the trade-off and the number behind each choice. What went wrong is recorded too. Read how it was governed in Built with AI, and what it all runs on, at millisecond speeds on free-tier stores and one small container, in Performance.
TheYard is my portfolio implementation of a used-vehicle auction platform: browse a large inventory, inspect a vehicle in detail, and place bids against a simulated room of other bidders. It began as my submission to a company's take-home hiring exercise, in a fork of their starter repository, and everything described below was built on that start (ADR: The name, and how a rename was done without losing the history, says which and when). The frontend is a React app backed by a .NET 10 REST API that owns the data, the search, and the auction rules, storing accounts and bids in Azure SQL Database or Azure Cosmos DB, both reached with the container's managed identity, so what the container holds for either store is an address and an authentication mode and nothing worth stealing.
The stores run on their free offers and the containers on trial credit, and every piece is priced in its record rather than called free; it keeps serving when a database does not: the catalogue falls back to files and the health endpoint says which store answered. The Admin tab shows the running system reporting on itself, including every SQL statement it has sent and how long the database took.
Everything about how it is built and hosted is served from inside the running app, under App Architecture, API Reference, SQL vs Cosmos DB, Performance, Diagrams, Style, Hosting, Built with AI, CI/CD and Best Practices in the sidebar. One hundred and twenty-three decision records explain each choice, and the code samples in them are read from the running build rather than pasted, so a record cannot drift from the code it describes. The shape of it:
A preview. Open the infrastructure diagram in a new page to zoom in and follow it. The data flow has its own drawing too.
On Windows the two tools install with
winget install OpenJS.NodeJS.LTS Microsoft.DotNet.SDK.10, and npm run api and npm run dev run the
two halves in separate terminals. With npm start running, open http://localhost:5173. The dev server proxies /api to the .NET API that serves
the inventory and the vehicle photos (/api/images/...). The inventory is 100,000
records, deterministically synthesized at startup from the 200-record seed dataset
(Inventory:TargetCount in api/TheYard.Api/appsettings.json), so there is no giant
file in the repo. All filtering, sorting, and paging are server-side via LINQ over GET
parameters; the inventory view opens on the top 100 by auction time (live, ending soonest first):
GET /api/vehicles?make=Ford&status=live&sort=price-asc&limit=100
Parameters: q (matches every filterable field, including derived auction status),
make, body_style, title_status, province, status,
min_condition, price_min, price_max, sort (ending-soonest, price-asc,
price-desc, condition, most-bids), limit (default 100, max 500), offset. Responses
are an envelope { total, vehicles }, each vehicle carrying server-derived auction
facts (auction_starts_at, auction_ends_at, auction_status, min_next_bid, and
sold, true once anybody has bought it).
Invalid status or sort values return 400 as RFC 9457 ProblemDetails
with the message in detail. GET /api/vehicles/{id} fetches one vehicle;
GET /api/facets feeds the filter dropdowns from the full dataset.
Bidding is server-side, validated by the domain rules, and belongs to a signed-in
account (POST /api/auth/register, /login, /logout; GET /api/auth/me):
POST /api/vehicles/{id}/bids { amount } answers accepted or won, or 400
in the same problem shape; POST /api/vehicles/{id}/buy-now; GET /api/bids (the
signed-in account's standing on every vehicle it has bid on, an empty map signed out);
DELETE /api/bids (that account's start-over, nobody else's). Bids live in the
request's store, Azure SQL Database or Azure Cosmos DB on the live sites and SQLite
locally, are read into memory once at startup, and are overlaid on vehicles before
filtering, so price filters see what the UI shows. If the API is not running, the app
shows a clear error state with a retry.
The app also serves its own documentation and health:
GET /api/docs/{slug} (every document in the sidebar, live code samples expanded at
request time), GET /api/docs/diagrams/{name} (a diagram on its own zoomable page),
GET /api/version (the build and commit the footer shows), GET /healthz and
GET /readyz (liveness and readiness), GET /api/health, GET /api/errors and
GET /api/admin/azure (the Admin tab), and GET /api/admin/activity (visitor-days per day by
kind, people, scanners and the site's own reads, with the path to the resume and the referring hosts,
naming nobody; the per-visitor rows are off).
The API describes itself. GET /api/openapi/v1.json is an OpenAPI document built from the endpoints
as they are mapped, and /api/reference renders
it as a browsable reference with a request builder. Every public operation carries an operation id,
a summary and its responses, failures included; the bearer and cookie schemes are declared and the operations
that need a session are marked; the operator endpoints under /api/admin/ are kept out of it. All
four rules are held by a test rather than by care, which is the point of ADR: The API describes
itself.
The .NET suite is measured as well as run, and published as an annotation on every CI run so it can be read without a GitHub sign-in. At 1.0.0.65 it was 89.6% of lines and 71.7% of branches; the current figure is on the latest run rather than in this paragraph. The shape matters more than the total, and it is the shape the architecture predicts:
| Project | Lines | Branches |
|---|---|---|
TheYard.Data | 100.0% | 100.0% |
TheYard.Domain | 98.8% | 97.9% |
TheYard.Migrations.Sqlite | 97.6% | 100.0% |
TheYard.Infrastructure | 93.8% | 89.3% |
TheYard.Application | 93.2% | 88.8% |
TheYard.Api | 78.2% | 64.1% |
The rules and the use cases are the parts worth being sure about. The host is lowest because two of its classes talk to Azure with a managed identity. CI has no Azure credential and is never getting one; what is worth asserting about those two is that they degrade rather than throw when the identity endpoint is not there, and that is tested (ADR: Counting what the tests cover).
To refresh the photo set from Wikimedia Commons, run node scripts/fetch_photos.mjs.
Built test driven and domain-first: every test specified before the code it holds was written, and the rules and their tests before any UI. It then grew in deliberate passes into a demonstration of how I build production systems: the .NET API in onion architecture, server-side filtering, sorting and paging over a 100,000-record synthetic dataset, server-owned bidding rules, three test suites, CI, a container, a live host, and a written architecture the code is reviewed against.
The work was pair-built with Claude Code throughout. I directed the scope and the architecture and every product decision, and I am happy to walk through the reasoning behind any line of it.
AI-assisted, verification-driven. I directed scope and architecture and the product decisions; Claude Code implemented against tests I specified first, and nothing merged on trust: every change ran the typechecker and all three suites, UI work was verified against real screenshots at desktop, tablet and mobile widths, and features were driven end to end in a headless browser before being called done. The build went domain-first (rules and tests before any UI), then grew in deliberate passes: frontend, API, scale, bidding, hosting, then the documentation and observability passes. Two adversarial reviews ran mid-stream, one by several readers at once and one as a staff engineer would read it, and their findings were fixed, tested, and in one case turned into a regression test. The living documentation is served inside the app, so the walkthrough can happen without leaving it.
current_bid is null for 112 of 200 vehicles (the ones with bid_count: 0). The
sample record shows a number, but the data is authoritative: the type is
number | null. Before any bids exist, the minimum acceptable bid is the opening ask
(no increment), a reserve cannot be met, and the UI labels the price "Starting bid".auction_start is synthetic, so each
vehicle's id hashes to an end time spread across two days before to five days after
"now", anchored to the UTC midnight that began the day, with a two to four day
duration. Windows are stable across reloads within a UTC day and re-seed at 00:00
UTC, so the inventory always shows a live mix of ended, live, and upcoming auctions.en-CA) since every listing is Canadian; one constant in
src/lib/format.ts switches it.api/TheYard.Api/wwwroot/images/CREDITS.md.src/styles/colors.css, sizes.css, typography.css, effects.css); Vitest for tests. No component,
icon, state or CSS libraries, and no router: icons are small inline SVGs and the
address bar is the application state. Four runtime dependencies: react, react-dom,
marked for rendering the served documents, and highlight.js for their code samples. The grounds and the text are Figma's Urban slate, gray
and brown, under an accent of teal, dark green and gold, on glass panels over one soft
watermark, with every text and ground pair measured against WCAG AA by a unit
test, the watermark at its worst included. How it looks, and the rules that keep it
looking that way, are the Style section in the sidebar (docs/style/COLOR-STYLE.md), with
swatches drawn from the token sheet and the rules held by StyleRulesTests in the gate, and IBM Plex Sans, the one face since 1.0.3.19, served from the site's own /assets, with a system fallback; no external asset.api/): TheYard.Data
(the pure data records, no dependencies), TheYard.Domain (photo selection, auction
schedule, filter, bid and standing rules), TheYard.Application (one handler per
request, each a query or a command, over Stock, BidBook and Room, each behind a reading and a writing
interface, the auction's ports, and the operator's ports beside them), TheYard.Infrastructure (the EF Core
adapters over Azure SQL Database or SQLite, the JSON readers that seed them, the synthetic scale-up), TheYard.Api (host,
handlers in Endpoints/, every registration in Composition/, static images, the
served documents, observability). OnionTests holds the direction in the build. Filtering is LINQ over GET parameters, including
auction status; all auction math lives in Domain and travels on the wire, so the
browser only formats. src/lib/data.ts is the frontend's single data seam.GET /api/vehicles, applied server-side
with LINQ, and the browser's address bar mirrors the same parameters, so any filtered
view is shareable and bookmarkable. Opening a vehicle is GET navigation too
(?vehicle={id} pushes a history entry): the browser's Back button closes the detail,
Forward reopens it, and a cold load of a vehicle URL deep-links straight to it.
Where: src/lib/inventory.ts (URL and filter serialization), src/app/hooks/useAddressBar.ts
(pushState and popstate), api/TheYard.Api/Features/Vehicles/VehicleQueryParams.cs (binding),
api/TheYard.Domain/Shared/Auction/VehicleFilter.cs (the LINQ predicate).src/lib/data.ts (cache, peekVehicles), src/app/hooks/useInventory.ts (the debounced
fetch effect), api/TheYard.Api/Shared/Wiring/RequestPipeline.cs (cache headers).{ total, vehicles } with limit and offset, a landing page of
the top 100 by auction time, and Load More to walk deeper.
Where: api/TheYard.Application/Shared/Auction/Stock.cs (Search),
api/TheYard.Infrastructure/Shared/Auction/SyntheticVehicleSource.cs (the 100k expansion),
src/app/hooks/useInventory.ts (loadMore).api/TheYard.Domain
(unit-tested without hosting anything), status recomputation in
src/lib/auction.ts (unit-tested without rendering anything). Components stay thin.reserve_state on every vehicle, so the seller's number is not on the wire
and the panel shows what the server said.useNow) drives every countdown and status so that a card
and its detail view can never disagree about liveness./assets/* is cached for a year, and everything that can change under
the same address says no-cache. Photos keep a one-day rule.data/vehicles.json itself stays untouched, and the frontend renders whatever image
URLs the API returns, as it would in production.detail and a trace
identifier; a React error boundary turns a render crash into a page with a way out and
reports it to the Admin tab.current_bid: 22800, but 112 of the 200 real records have current_bid: null.
Profiling the data before writing the types caught it; the fix rippled into the type
(number | null), the minimum-bid rule (first bid meets the opening ask), and the
"Starting bid" labels.Infinity winning a buy-now, is refused by the model binder before the rules
see it, and the rules take an integer.min_next_bid crossed its buy_now_price triggered a
legitimate instant win the test did not expect; the test now documents both outcomes as
correct.api/**/obj killed the dev server with EBUSY; fixed by excluding api/** from the
watcher in vite.config.ts.npm start raced its own browser tab. Vite opens the browser in about 0.4 s while
the API takes seconds to boot, so first paint could show a dead-API error. The initial
load now retries without showing an error for up to 30 s, and the fix carries a regression test written
from the actual bug report.API (1797 xUnit tests, separate TheYard.Tests project): one suite per onion layer.
Domain (photo gallery determinism and make preference, FNV-1a known vectors, auction
schedule bounds and boundaries, every filter rule, bid rules including increment tiers
and buy-now precedence), application (Stock, BidBook and the handlers with in-memory
fakes standing in for the file adapters), infrastructure (snake_case deserialization, the
synthetic 100k expansion's invariants, the real dataset and manifest), and integration
tests that boot the real host in memory (WebApplicationFactory) to verify endpoints,
filtering, sorting and paging parameters, the problem shape on every 400 and on a crash, the full bid
lifecycle, static image serving, cache headers, the document catalog, the live-sample
expander, the diagram pages, the changelog, and a persistence suite that places a bid,
disposes the application, starts a second one against the same database file and reads
the bid back, one test that points the connection string at a path which cannot be
opened to prove the site still serves its inventory when the store does not come up, and
two that hold the photo manifest and the image directory to the naming that responsive
images rely on, and an account suite that registers two people, has them outbid each
other, restarts the application and signs the first one back in to find their bid where
they left it, while checking that the token never appears in a response body and that a
wrong password says exactly what an unknown address says. Run with npm run test:api.
Frontend (589 Vitest tests at 1.0.4.132): presentation logic only, since the API owns the rules.
Status recomputation from server windows, reserve states, formatting and countdowns, URL
and filter round-tripping, query-parameter mapping, the request cache (TTL, per key,
forced bypass, no caching of failures), the palette's contrast against WCAG AA,
including the two pairs a stylesheet composes that nobody had listed, and the account
seam, which translates the wire both ways, shows the server's own sentence when a
sign-in is refused, and holds no token anywhere. Run with npm test.
End-to-end (196 Playwright tests): the real stack. The inventory view shows 100 of
100,000, filtering and tile navigation sync the URL both directions (including browser
Back and deep links), Load More appends a page, every sidebar section and document opens,
the diagrams open on their own pages, the Admin tab reports on the running system, a
browser error reaches it, a transient API failure recovers via the retry banner, the
phone drawer works at 375 pixels, the keyboard path walks from the skip link through
every view switch, a bid round-trips through the API, survives a reload, and resets,
the simulated room answers a bid so the high-bidder badge changes hands, the sign-in
form creates an account that survives a reload and a bid made under it appears in that
account's list, and axe runs eleven scans across nine views at WCAG 2.1 AA, including both
halves of the account page. Run with npm run test:e2e (launches both servers itself, uses your
installed Chrome). All three suites run once per version in the ship gate, and only a green gate
pushes to main, which deploys. The eight tests that need the real Cosmos DB account run in the
same gate, beside the whole API suite booted a second time on Cosmos DB; CI, which runs the
suites on a pull request and has no Azure credential, filters those eight out.
ADR: The tests, explained walks all three suites for a developer new to the stack.
The four promises this section made when the build started have all shipped:
docs/app-architecture/STYLE.md (naming,
layering, comments that explain why and how, never what) and docs/app-architecture/ARCHITECTURE.md
(the onion, the wire contract, the derive-don't-store principle), both served under
App Architecture, with an .editorconfig enforcing the mechanical half.detail and a trace identifier, one shape for queries and bids alike, structured
JSON request logging, and a React error boundary that reports render crashes to the
Admin tab. Recorded in ADR: Error handling.Four more came off the list afterwards, on time that was no longer the deadline's:
What is genuinely still open, in priority order:
The quick version:
npm run docker # build the image and serve everything at http://localhost:8080
npm run docker:stop # stop it
That is all a visitor needs. The rest of this section explains what those two wrap.
The repo ships a multi-stage Dockerfile that builds the frontend, publishes the API, and produces a single runtime image serving both on port 8080.
Build the image:
docker build -t theyard:local .
Run it:
docker run --rm -d -p 8080:8080 --name theyard theyard:local
Then open http://localhost:8080. The API serves the SPA with a fallback route, so deep
links to item URLs work. A container HEALTHCHECK probes /healthz every 30 seconds;
docker ps shows the container as healthy once the app is accepting traffic.
Stop it:
docker stop theyard
Notes:
app user.APP_VERSION and APP_COMMIT baked in, which
is what the footer and /api/version report.infra/main.bicepapi/TheYard.Domain/Shared/Auction/VehicleSearchIndex.csVehicleFilter.csCompileapi/TheYard.Application/Shared/Auction/Stock.csapi/TheYard.Tests/SearchIndexBenchmarkTests.csVehicleSearchIndexTests.csTheYard.Domain and
nowhere else. The wire carries the derived facts (auction_ends_at, min_next_bid)
so the browser only formats and counts down. This was not free: early versions mirrored
the math in TypeScript, and cross-language drift bit twice (a timezone anchor, then
DST) before the consolidation. The architecture exists because the bug class it
eliminates actually happened.
Where: api/TheYard.Domain/Shared/Auction/AuctionSchedule.cs, BidRules.cs, and
AuctionClock.cs; api/TheYard.Api/Shared/Wire/VehicleWire.cs (derived facts onto the wire);
src/lib/auction.ts (all that remains client-side).Vehicle,
VehicleFilter, BidState, SearchResult) is a sealed record: records give
value-based comparison, and sealing keeps that trustworthy, because record equality
includes a hidden runtime-type check (EqualityContract) that inheritance would
poison without a compile error. Sealing also states intent (a wire contract is not an extension point),
lets the JIT devirtualize the generated Equals and GetHashCode, and is the
low-regret default: unsealing later is non-breaking, sealing later is not. The payoff
shows up in practice: determinism tests compare whole vehicle lists by value, and
non-destructive with mutations power the bid overlay and the synthetic variants.
Where: api/TheYard.Data/Vehicle.cs; with usage in
api/TheYard.Domain/Shared/Auction/StandingRules.cs (the bid overlay), api/TheYard.Application/Shared/Auction/BidBook.cs and
api/TheYard.Infrastructure/Shared/Auction/SyntheticVehicleSource.cs; value-equality assertions in
api/TheYard.Tests/SyntheticVehicleSourceTests.cs.IVehicleSource, IPhotoManifestSource, IBidStore); Infrastructure and its Cosmos DB twin
adapt the stores and the files; the host only
binds, asks the request's one handler and serializes. The proof it is not ceremony: the 100k scale-up is a decorator on
a port (SyntheticVehicleSource) and nothing above it changed, and the test suite
swaps in-memory fakes at the same seams. The build holds the rings: OnionTests reads
the compiled assemblies with NetArchTest and fails on any dependency that points outward
or an endpoint that reaches a store, ten rules in all (ADR-088, Onion and SOLID).
Where: api/TheYard.Data/ to api/TheYard.Domain/ to
api/TheYard.Application/ (Ports.cs, AuctionParts.cs, StandingReader.cs, Stock.cs, BidBook.cs) to
api/TheYard.Infrastructure/ to api/TheYard.Api/Shared/ (what every slice shares, with
Program.cs a loop over the slices) and api/TheYard.Api/Features/ (one folder per slice);
fakes in api/TheYard.Tests/StockTests.cs; the rules in
api/TheYard.Tests/OnionTests.cs. The whole picture is written
down in docs/app-architecture/ARCHITECTURE.md, served as Architecture overview.api/TheYard.Api/Features/Docs/LiveSamples.cs, DocumentationCatalog.cs,
api/TheYard.Tests/LiveSamplesTests.cs, DocumentationCatalogTests.cs, ChangelogTests.cs.az