The model is a detail. A Clean Architecture port for LLM-backed use cases, with swappable adapters in Java and Node.js/TypeScript. Companion code for the book Código Sintético / Synthetic Code (Chapter 16).
Robert C. Martin's dependency rule says dependencies point inward: the business rules must not depend on the
database — or, in the agentic era, on the model. The provider, the model identifier, the SDK all live in the
outer ring. The core imports a port, never anthropic or openai. Switching providers becomes a
configuration change, not a rewrite.
┌─────────────────────────────────────────────┐
│ Outer ring: adapters (know the SDK) │
│ AnthropicAdapter · OpenAIAdapter · Fake │
│ ┌─────────────────────────────────────┐ │
│ │ Inner ring: use cases (policy) │ │
│ │ ReviewCodeUseCase │ │
│ │ depends on ─▶ LlmPort (port) │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
dependencies point inward ───▶
java/ Java 17 + Spring-style port, use case, and Anthropic adapter
typescript/ Node 20 + TS port, with Anthropic and OpenAI adapters + a Fake for tests
- Testability — the use case runs against a
FakeLlmPortwith no network, no cost, in milliseconds. - Provider independence — routing
QUALITYto one provider andFASTto another lives in config. - Readable policy — an auditor reads the use case and sees the business rule, not SDK detail.
# TypeScript
cd typescript
npm install # typescript + @types/node only
npm run demo # runs against the FakeLlmPort — no API key required
npm test # 3 tests, Node's built-in runner, no network
# Java (requires JDK 17+)
cd java
mvn test # JUnit 5 against the FakeLlmPortThe default build excludes the real AnthropicAdapter/OpenAIAdapter (the only
files that import a vendor SDK), so everything compiles and runs with no provider
present. To compile the real adapters, install the SDKs and build with the adapters
config:
npm install @anthropic-ai/sdk openai
npx tsc -p tsconfig.adapters.jsonThat separation is the lesson: you do not need a model to exercise the policy.
La regla de dependencia de Robert C. Martin dice que las dependencias apuntan hacia adentro: las reglas de
negocio no deben depender de la base de datos —ni, en la era agéntica, del modelo—. El proveedor, el
identificador del modelo y el SDK viven en el anillo exterior. El núcleo importa un puerto, nunca
anthropic ni openai. Cambiar de proveedor pasa a ser un cambio de configuración, no una reescritura.
java/ Puerto, caso de uso y adaptador Anthropic (Java 17, estilo Spring)
typescript/ Puerto en TS con adaptadores Anthropic y OpenAI + un Fake para tests (Node 20)
- Testeabilidad — el caso de uso corre contra un
FakeLlmPort: sin red, sin costo, en milisegundos. - Independencia de proveedor — enrutar
QUALITYa uno yFASTa otro vive en configuración. - Política legible — un auditor lee el caso de uso y ve la regla de negocio, no el detalle del SDK.
# TypeScript
cd typescript
npm install # solo typescript + @types/node
npm run demo # corre contra el FakeLlmPort — no requiere clave de API
npm test # 3 tests con el runner nativo de Node, sin red
# Java (requiere JDK 17+)
cd java
mvn test # JUnit 5 contra el FakeLlmPortLa compilación por defecto excluye los adaptadores reales AnthropicAdapter/OpenAIAdapter
(los únicos archivos que importan un SDK de proveedor), de modo que todo compila y corre sin
ningún proveedor presente. Para compilar los adaptadores reales, instala los SDK y usa la
configuración de adaptadores:
npm install @anthropic-ai/sdk openai
npx tsc -p tsconfig.adapters.jsonEsa separación es la lección: no necesitas un modelo para ejercitar la política.
- 🇬🇧 The Model Is a Detail: Clean Architecture for AI Agents — Medium
- 🇪🇸 El modelo es un detalle: Clean Architecture para agentes de IA — LinkedIn
Model ids (e.g. claude-sonnet-4-6) and SDK signatures are ephemeral. The pattern (port + adapter) is what
is stable. Verify the current provider SDK before deploying.
MIT © Sergio Perez Ruiz. See LICENSE.