Clean Architecture in TypeScript: A Pragmatic Guide
Clean architecture without the cargo cult. A working TypeScript reference for separating business logic from frameworks, databases, and HTTP.

What clean architecture is actually for
Clean architecture is a set of structural rules that keep business logic independent of the frameworks and infrastructure around it. The point is not purity; the point is that you can swap your HTTP framework, your database, or your job queue without rewriting the part of the code that customers care about.
The cost is some indirection and some discipline. In TypeScript, the cost is lower than people assume — the type system pays for most of the boilerplate.
The four-layer structure
Domain: pure TypeScript, no dependencies. Entities, value objects, domain services. This layer compiles and runs without a framework. Application: use cases that orchestrate domain logic. They depend on the domain and on abstract ports (interfaces) for outbound communication. Infrastructure: adapters that implement the ports — a Postgres repository, an S3 file store, a Stripe payments gateway. Interface: HTTP controllers, CLI commands, message consumers — the entry points.
Dependencies always point inward. Infrastructure depends on application depends on domain. The domain layer has no idea HTTP exists.
Ports and adapters in practice
A port is an interface that the application layer defines: UserRepository, EmailSender, PaymentGateway. An adapter is an implementation: PostgresUserRepository, SendgridEmailSender, StripePaymentGateway. The application layer never imports the adapter directly — it receives the port through constructor injection.
This is what makes the application layer testable. In a unit test you pass an in-memory implementation of the repository; no Postgres, no Docker, no flake.
Where TypeScript helps
Branded types (`type UserId = string & { readonly __brand: unique symbol }`) prevent passing a productId where a userId is expected. Discriminated unions model domain states without enum tagging. Readonly arrays and objects prevent accidental mutation in pure domain code.
Use a strict tsconfig: noImplicitAny, strictNullChecks, noUncheckedIndexedAccess. The boilerplate of clean architecture mostly disappears when the compiler catches the bugs the structure is designed to prevent.
Where it pays off
Clean architecture is overkill for a CRUD app with three endpoints. It pays off in systems with significant business logic — pricing engines, workflow engines, anything regulated. The investment is upfront; the return is the third migration the application survives without a rewrite.
When NOT to use it
For prototypes, marketing sites, and small CRUD services, clean architecture is bureaucracy. A two-layer structure — handlers and a repository — is enough. The discipline kicks in when the business logic outgrows what one person can hold in their head.
Reader questions, answered
Is this the same as hexagonal architecture?+
Essentially yes. Hexagonal, clean, and onion architectures are variations of the same idea — dependency direction inward, ports and adapters.
Do I need a DI container?+
Constructor injection in TypeScript works fine without one. Add a container only when wiring becomes painful.

Raza Ahmad is a technology author and IT infrastructure specialist based in Melbourne, Australia. He writes practitioner-grade guides on cloud computing (Azure and AWS), cybersecurity, enterprise networking with Cisco platforms, Linux administration, DevOps, and virtualization. His work focuses on translating complex infrastructure topics into clear, accurate guidance that engineers, system administrators, and IT decision makers can put to work in production environments. Every article published under his byline is fact-checked against current vendor documentation, official standards, and Raza's own hands-on experience operating the technologies he covers.
More from Software Engineering

Domain-Driven Design for Microservices Without the Cargo Cult
DDD is the most useful and the most misused framework in modern software design. Here is how to apply it to microservice boundaries without becoming a parody of itself.

Monorepo vs Polyrepo: A Decision Framework for 2026
Both Google and Amazon ship at scale; one runs a single repository, the other runs thousands. Here is how to decide which model fits your team.

Choosing Between REST, GraphQL, and gRPC in 2026
Three serious API styles, three very different operational profiles. A practical decision framework for picking the one your team can actually live with.
One email. The technology stories that actually matter for engineers.
A curated digest of the week's most useful tutorials, reviews, and analysis — no clickbait, no AI summaries of someone else's work.
Free. Unsubscribe anytime. See our privacy policy.