diff --git a/docs/architecture/data-flow-explainer.html b/docs/architecture/data-flow-explainer.html index 44757c9..75e8f63 100644 --- a/docs/architecture/data-flow-explainer.html +++ b/docs/architecture/data-flow-explainer.html @@ -1529,14 +1529,15 @@ footer .colophon {
Three things in every feature live near tests but play different roles. The mock repository is a real implementation of the repository interface — it's also the default DI binding. The contract is a portable test suite that runs against any implementation. The factory is a builder for valid entity values. The mock isn't only a test thing; that's the part that surprises people.
+Three artifacts that sit near tests, at different distances from runtime. The mock repository is a real implementation of the interface — runtime code (DI, dev mode, storybook) reaches it. The contract is a test suite that runs against any implementation of the repo interface, mock or real. The factory builds valid entity values. The relationship between them is the interesting bit.
The mock is the surprising one. People assume it lives in __mocks__/ because tests use it — but the DI container needs it as the default binding at runtime, and reaching into __mocks__/ from production code crosses a boundary. So the mock lives next to the real implementation, in infrastructure/repositories/. They're siblings.
The mock is a test artifact, but it's also more than that — it's a real implementation of the repository interface, and runtime code reaches it directly. DI binds it as the default; dev mode runs against it when Payload isn't booted; storybook stories that need data resolve to it. The contract and factory are only reached from test files. That difference in reach is what determines where each lives.
+So the mock sits in infrastructure/repositories/ next to the real impl — they're sibling implementations of the same interface, both legitimate citizens of the runtime layer. The contract and factory live under __-prefixed directories that nothing outside *.test.ts ever imports from.
The mock has two jobs.
+The mock is reached from two directions. Both are legitimate, neither is "the test version":
BlogModule binds IArticlesRepository to MockArticlesRepository at module-load time. Anything resolving that symbol — use cases, controllers, the whole chain — gets the mock until bindProductionBlog(config) swaps it for the real Payload-backed one. See §03.new MockArticlesRepository() and pass it directly into the use-case factory function. No DI involved — just a closure with a fake repo.BlogModule binds IArticlesRepository to MockArticlesRepository at module-load time. Anything resolving that symbol — use cases, controllers, tRPC procedures, the dev server — gets the mock until bindProductionBlog(config) swaps it for the real Payload-backed one. See §03.new MockArticlesRepository() and pass it directly into the use-case factory function. Same class, different consumer — just a closure with a fake repo.The contract and factory are pure test ergonomics — they only show up in *.test.ts files. Both live under __-prefixed directories that signal "test territory; not part of the public surface; not imported by runtime code." Their roles:
The contract and factory are reached from one direction only — tests. They never appear in runtime imports. Their roles:
it() blocks parameterized by buildSubject. Run it against the mock, run it against the real impl. If they diverge — your mock is lying to you and you'd never have caught it without the contract.articleFactory.build({ slug: "x" }) hands you a valid Article with sensible defaults; you only override the fields the test actually cares about. Used by the contract and by every use-case / controller test.it() blocks parameterized by buildSubject. Run it against the mock, run it against the real Payload-backed impl. If they diverge — your mock is lying about Payload's behavior, and you'd never catch it without the contract. This is its whole reason to exist: it tests the mock so you can trust it, alongside testing the real impl.articleFactory.build({ slug: "x" }) hands you a valid Article with sensible defaults; you only override the fields the test cares about. Used by the contract and by every use-case / controller test that needs entity values without writing 8 lines of inline fixtures.