<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>REST API — Clean Architecture</title>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{
font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,sans-serif;
background:#0a0a0f;color:#e2e8f0;
display:flex;justify-content:center;align-items:flex-start;
min-height:100vh;padding:24px 16px;
}
.card{
width:100%;max-width:780px;
background:linear-gradient(145deg,rgba(15,15,25,0.95),rgba(10,10,18,0.98));
border:1px solid rgba(255,255,255,0.08);border-radius:16px;
overflow:hidden;
}
.card-header{
padding:28px 32px 20px;
border-bottom:1px solid rgba(255,255,255,0.06);
}
.card-header h1{font-size:1.35rem;font-weight:700;color:#f1f5f9;margin-bottom:6px}
.card-header p{font-size:0.82rem;color:#94a3b8;line-height:1.5}
.badge-row{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}
.badge{
font-size:0.68rem;padding:3px 10px;border-radius:20px;
background:rgba(34,211,238,0.1);color:#22d3ee;
border:1px solid rgba(34,211,238,0.2);
}
.body{padding:24px 32px 28px}
/* ── Concentric circles diagram ── */
.diagram{
display:flex;justify-content:center;margin-bottom:28px;
}
.circles{position:relative;width:280px;height:280px}
.ring{
position:absolute;border-radius:50%;
display:flex;align-items:center;justify-content:center;
text-align:center;font-weight:600;font-size:0.7rem;letter-spacing:0.02em;
}
.ring-4{
width:280px;height:280px;top:0;left:0;
background:rgba(59,130,246,0.08);border:1.5px solid rgba(59,130,246,0.25);color:#60a5fa;
}
.ring-3{
width:210px;height:210px;top:35px;left:35px;
background:rgba(34,211,238,0.08);border:1.5px solid rgba(34,211,238,0.25);color:#22d3ee;
}
.ring-2{
width:145px;height:145px;top:67.5px;left:67.5px;
background:rgba(168,85,247,0.1);border:1.5px solid rgba(168,85,247,0.3);color:#c084fc;
}
.ring-1{
width:80px;height:80px;top:100px;left:100px;
background:rgba(250,204,21,0.12);border:1.5px solid rgba(250,204,21,0.35);color:#facc15;
}
.ring-label{
position:absolute;font-size:0.6rem;color:#64748b;
}
.rl-top{top:-18px;left:50%;transform:translateX(-50%)}
.rl-bot{bottom:-18px;left:50%;transform:translateX(-50%)}
/* ── Folder tree ── */
.section-title{
font-size:0.78rem;font-weight:700;color:#22d3ee;
text-transform:uppercase;letter-spacing:0.08em;margin-bottom:12px;
}
.tree{
background:rgba(0,0,0,0.35);border:1px solid rgba(255,255,255,0.06);
border-radius:10px;padding:18px 20px;
font-family:'SF Mono',SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;
font-size:0.72rem;line-height:1.7;overflow-x:auto;white-space:pre;
color:#cbd5e1;margin-bottom:24px;
}
.tree .folder{color:#22d3ee;font-weight:600}
.tree .comment{color:#64748b;font-style:italic}
/* ── Principles ── */
.principles{
display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:24px;
}
.principle{
background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);
border-radius:10px;padding:14px 16px;
}
.principle h4{font-size:0.72rem;color:#22d3ee;margin-bottom:4px}
.principle p{font-size:0.68rem;color:#94a3b8;line-height:1.5}
/* ── Dependency Rule ── */
.dep-rule{
background:rgba(34,211,238,0.05);border:1px solid rgba(34,211,238,0.15);
border-radius:10px;padding:16px 20px;margin-bottom:24px;
text-align:center;
}
.dep-rule .arrow{font-size:1rem;color:#22d3ee;margin:6px 0}
.dep-rule p{font-size:0.72rem;color:#94a3b8}
.dep-rule strong{color:#e2e8f0}
/* ── When to Use ── */
.when-grid{
display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:24px;
}
.when-item{
background:rgba(255,255,255,0.02);border:1px solid rgba(255,255,255,0.06);
border-radius:8px;padding:10px 14px;
}
.when-item .label{font-size:0.65rem;color:#64748b;margin-bottom:2px}
.when-item .val{font-size:0.7rem;color:#e2e8f0}
.when-item .val.yes{color:#4ade80}
.when-item .val.no{color:#f87171}
/* ── Sources ── */
.sources{
border-top:1px solid rgba(255,255,255,0.06);
padding:16px 32px;
}
.sources h4{font-size:0.68rem;color:#64748b;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.06em}
.sources a{
display:inline-block;font-size:0.7rem;color:#3b82f6;
text-decoration:none;margin-right:16px;margin-bottom:4px;
transition:color 0.2s;
}
.sources a:hover{color:#60a5fa;text-decoration:underline}
@media(max-width:600px){
.body{padding:18px 16px 22px}
.principles,.when-grid{grid-template-columns:1fr}
.card-header{padding:20px 16px 16px}
.sources{padding:14px 16px}
.circles{transform:scale(0.85);transform-origin:center}
}
</style>
</head>
<body>
<div class="card">
<!-- Header -->
<div class="card-header">
<h1>REST API — Clean Architecture (Node.js)</h1>
<p>Enterprise-grade API structure with separated domain, application, infrastructure, and interface layers.</p>
<div class="badge-row">
<span class="badge">Node.js</span>
<span class="badge">Express</span>
<span class="badge">TypeScript</span>
<span class="badge">Clean Architecture</span>
</div>
</div>
<div class="body">
<!-- Concentric circles diagram -->
<div class="section-title">Architecture Layers</div>
<div class="diagram">
<div class="circles">
<div class="ring ring-4">
Frameworks & Drivers
<span class="ring-label rl-top" style="color:#60a5fa">Express / Prisma / JWT</span>
</div>
<div class="ring ring-3">
Interface Adapters
</div>
<div class="ring ring-2">
Use Cases
</div>
<div class="ring ring-1">
Entities
</div>
<span class="ring-label" style="position:absolute;bottom:-22px;left:50%;transform:translateX(-50%);font-size:0.6rem;color:#64748b">
Dependencies point inward →
</span>
</div>
</div>
<!-- Folder tree -->
<div class="section-title">Project Structure</div>
<div class="tree"><span class="folder">src/</span>
├── <span class="folder">domain/</span> <span class="comment">(Enterprise rules)</span>
│ ├── <span class="folder">entities/</span> <span class="comment">(User, Order, Product)</span>
│ ├── <span class="folder">value-objects/</span> <span class="comment">(Email, Money)</span>
│ └── <span class="folder">repositories/</span> <span class="comment">(Interfaces only)</span>
├── <span class="folder">application/</span> <span class="comment">(Use cases)</span>
│ ├── <span class="folder">use-cases/</span> <span class="comment">(CreateUser, GetOrder)</span>
│ ├── <span class="folder">dtos/</span> <span class="comment">(Request/Response shapes)</span>
│ └── <span class="folder">services/</span> <span class="comment">(Orchestration)</span>
├── <span class="folder">infrastructure/</span> <span class="comment">(External concerns)</span>
│ ├── <span class="folder">database/</span> <span class="comment">(Postgres, Prisma, Drizzle)</span>
│ ├── <span class="folder">http/</span> <span class="comment">(Express app, middleware)</span>
│ ├── <span class="folder">auth/</span> <span class="comment">(JWT, OAuth)</span>
│ └── <span class="folder">external/</span> <span class="comment">(APIs, S3, email)</span>
└── <span class="folder">interfaces/</span> <span class="comment">(Adapters)</span>
├── <span class="folder">controllers/</span> <span class="comment">(Route handlers)</span>
├── <span class="folder">routes/</span> <span class="comment">(Express routes)</span>
├── <span class="folder">middleware/</span> <span class="comment">(Auth, validation, error)</span>
└── <span class="folder">validators/</span> <span class="comment">(Zod schemas)</span></div>
<!-- Key Principles -->
<div class="section-title">Key Principles</div>
<div class="principles">
<div class="principle">
<h4>Dependency Inversion</h4>
<p>Inner layers define interfaces; outer layers implement them. Domain never imports Express or Prisma.</p>
</div>
<div class="principle">
<h4>Testability</h4>
<p>Business logic is unit-tested without databases, HTTP, or any I/O. Mock at the boundary.</p>
</div>
<div class="principle">
<h4>Framework Independence</h4>
<p>Swap Express for Fastify, Prisma for Drizzle — domain and use cases remain untouched.</p>
</div>
<div class="principle">
<h4>Screaming Architecture</h4>
<p>Folder structure tells you what the system does, not what framework it uses.</p>
</div>
</div>
<!-- Dependency Rule -->
<div class="dep-rule">
<p><strong>The Dependency Rule</strong></p>
<div class="arrow">Frameworks → Adapters → Use Cases → Entities</div>
<p>Source code dependencies must always point <strong>inward</strong>, toward higher-level policies.</p>
</div>
<!-- When to Use -->
<div class="section-title">When to Use</div>
<div class="when-grid">
<div class="when-item">
<div class="label">Small CRUD / prototype</div>
<div class="val no">Overkill — use flat MVC</div>
</div>
<div class="when-item">
<div class="label">Medium API with business logic</div>
<div class="val yes">Good fit</div>
</div>
<div class="when-item">
<div class="label">Large enterprise / microservice</div>
<div class="val yes">Ideal — enforces boundaries</div>
</div>
<div class="when-item">
<div class="label">Team with mixed experience</div>
<div class="val yes">Clear rules reduce ambiguity</div>
</div>
</div>
</div>
<!-- Sources -->
<div class="sources">
<h4>Sources</h4>
<a href="https://mannhowie.com/clean-architecture-node" target="_blank" rel="noopener">Clean Architecture — Mann Howie</a>
<a href="https://github.com/panagiop/node.js-clean-architecture" target="_blank" rel="noopener">panagiop/node.js-clean-architecture</a>
<a href="https://github.com/jbuget/nodejs-clean-architecture-app" target="_blank" rel="noopener">jbuget/nodejs-clean-architecture-app</a>
</div>
</div>
</body>
</html>