Sitemap

Clean Architecture in Next.js — The Guide You Need

7 min readMay 1, 2025

--

Interested in Clean Architecture topic? Need a practical example of how to implement it in modern Web Application?
Then, this article is for you!

Press enter or click to view image in full size
Clean Architecture — The Guide You Need

I hope you’re also interested in real code example, so I’ve prepared my own vision on how to implement it in modern Web Application with usage of AWS services and good tests coverage: GitHub Repo

More than that, each directory contains it’s own README.md file with full explanation about the module purpose!

And this article would not be possible not only without my own experience but also without this great sources which I totally recommend to follow to extend your vision even more:

Why another Clean-Architecture article?

There’s so much about Clean Architecture already on the plate, why should you invest your time into one more?

In this material I’ve decided to develop my own vision of Clean Architecture implementation in modern Web Applciation and explain the topic through my own from-the-trenches experience and also come ap with a practical ready-to-use example which might be a perfect template or just a good knowledge reference for your own goals.

How to read this article?

Advice for novices

If you’re a novice, you don’t need a decade of enterprise patterns behind you to get value here. Just follow the article together with exploring the sample Web App and I promise you will get the value.

git clone https://github.com/Yuvelir1993/next-clean-architecture -b clean-architecture-next-js

Advice for a skilled ones

If you’ve already wrestled with layered architectures, or you find yourself grinding your teeth whenever someone re-explains what an entity is — feel free to treat this article like an à-la-carte menu rather than a tasting course.

Explore the code parts that matter to you, use the headings as jump links, mine the repo for real evidence, and ignore any theory you’ve already internalised. My promise: you’ll either leave with a pattern you can lift tomorrow — or with concrete ammo to dismantle it in your next architecture review. Either way, time well spent.

Basics

Before we dive in, let’s take a step back. Clean Architecture didn’t appear overnight — it evolved from earlier ideas such as Hexagonal Architecture and DDD. Each of these patterns is a successful approach to the same headache: business rules getting tangled with frameworks, databases, and UI code. Such coupling can easily lead to change paralysis, brittle tests, and vendor lock-in. Even minor shakes in technology end up shaking the core business logic.

The Clean Architecture pattern distills those earlier lessons into one guiding rule: keep domain logic at the centre and push details to the edges, so the application can outlive tech-stack changes and its core business logic stays protected.

It takes the approach which keeps parts of your architecture in layers, where each one serves it’s purpose and not dependent on implementation details of the other ones, but always keeps focus on the core business domain.

Press enter or click to view image in full size
Here you can see how such architecture is implemented and which dependencies are possible.

Main Concepts

Entities, Aggregates & Value Objects

These types model the heart of the business. Often they’re coined in conversations with domain experts and map directly to the part of the real-world that this web-app cares about. They carry invariants, speak pure business language and remain completely unaware of HTTP, DynamoDB or Next.js.

Use Cases

Orchestrates one business action — say, “Create Project” — exclusively through it’s declared ports (interfaces). Answers on question what to be done.
The power of such isolation is that the same business use-case can be driven by different clients (UI, CLI, …) and can drive whichever services or data stores it’s wired into. Swapping a UI framework, a message broker, or even the persistence layer has little-to-no impact on the business flow itself.

Ports & Adapters

Belongs to a business flow periphery. Answers on question how to do one particular thing.

Adapters driving the core by translating external input (HTTP, events) into a use-case call allowing any client to communicate with the application.
Ports are driven by the core: they fulfil interfaces defined in the application layer (use-cases) to reach the outside world.

Implementation

Let’s dive into a real-world Web Application which implements this architecture style.
The Web Application implemented for this article is a multi-user portal where users can create and delete their projects.

Press enter or click to view image in full size
Welcome page
Press enter or click to view image in full size
User’s projects dashboard

Tech stack:

  • Next.js
  • Redux
  • AWS Cognito
  • AWS DynamoDB
  • Terraform

Code Organizing

To organize code accordingly to all abovementioned principles, I propose such folder structure.

Press enter or click to view image in full size
Project’s folders — top view
Press enter or click to view image in full size
Project’s folders — deep view

To learn more, refer tothe repo and explore on your own with looking into README files — here I have more useful explanations.

Dependencies inside application

Inversion of Control (dependency injection)

Worth to mention additionally about how to make possible so periphery dependencies are pointing inwards and still staing decoupled and “swappable”.

Get Pavlo Lozovikov’s stories in your inbox

Join Medium for free to get updates from this writer.

To achieve that I’ve used Inversify — a lightweight dependency-injection container for TypeScript. Briefly, it allows to bind each port to a concrete class at runtime, so we can keep one stable interface while switching implementations (e.g., production vs. test, DynamoDB vs. in-memory) with only a configuration tweak (like env variable).

Dependencies visualization and validation

As project grows and more people are onboarded, it becomes harder and harder to keep with one of the the main Clean Architecture requirements: keeping the dependencies between all of the components right.
To achieve that in an automated way, I suggest to use a dependency-cruiser which shows and validates your dependencies (All CLI examples are in the root README). Also consider to include it into your CI/CD!

Press enter or click to view image in full size
Dependency graph

Benefits & Drawbacks

Use the checklist below to decide whether Clean Architecture’s long-term payoffs outweigh its upfront ceremony for your project.

✅ Pros

  • Framework independence— swap the UI layer, authentication provider, or even DynamoDB without touching core logic.
  • High testability — clean boundaries let you precisely focus your testing logic on particular application parts.
  • Maintainability — any technology churn (UI, cloud, DB, …) stays at the edges, so updates rarely causing ripples of the applciation core.
  • Refactor safety — small blast radius means faster, lower-risk evolution of features, bugfixes or refactoring improvements.
  • Smoother releases — layered code reduces cross-module collisions, so changes can be shipped more granularly.
  • Data-leak prevention — repositories and DTOs expose only the data each client needs.
  • Cross-platform reuse — one set of use-cases can power up the web, CLI, mobile, or any other background.
  • Domain clarity — the structure speaks with you in ubiquitous (domain) language and code tells the story of the business rather than the framework.

⚠️ Cons

  • Overhead on small apps — for a quick MVP the extra abstraction layers can exceed the feature code.
  • Steep learning curve— newcomers must grasp entities, ports, adapters, and dependency rules before they feel at home.
  • File sprawl & indirection — more folders and interfaces mean extra navigation cost and extra test automation to carry the dependency rules between layers.
  • Risk of premature abstraction — you might build interfaces you never need (YAGNI) if the domain stays simple.
  • Slower initial velocity— writing ports, DTOs, and tests up front delays first visible output, which can frustrate stakeholders.

So, now we’re at the point where we discovered Clean Architecture’s basics and concepts, walked through entities → use-cases → adapters in a real Next.js repo, and weighed the trade-offs.

The takeaway: isolating and protecting your core business buys long-term flexibility and allows for a smoother evolution of your app.

Is Clean Architecture a right fit for you?

Strong signals to say “yes”:

  • A deep roadmap with business rules that will keep evolving.
  • Multiple delivery channels (web, mobile, CLI, background jobs).
  • Rich domain language woven through the codebase.
  • Heavy integration surface — several external APIs, queues, or data stores.

Reasons to think twice:

  • A small, short-living application.
  • A hard deadline that rewards shipping fast here and now.

What’s next? — TODOs for motivated readers

And now, my motivated reader, you are ready to try things on your own!
To continue with a practical hands-on experience, use a carefully prepared Web App:

git clone https://github.com/Yuvelir1993/next-clean-architecture -b clean-architecture-next-js

And make one of next exercises or come up with your own. And do not hesitate to make PRs so other people can build things on top!

1. Swap a detail: replace DynamoDB with a Postgres adapter — only infrastructure/ code should change.
2. Add a driver: build a tiny CLI that invokes the existing createProject use-case.
3. Hard-mode test: spin up a Cognito mock, write an end-to-end test, and watch the layers stay intact.

💪🏻Keep your code and architecture clean, and future-you will ship features in hours, not sprints.💪🏻

--

--