Next.js: Implementing Clean Architecture

Posted by


Clean Architecture is a software design pattern that aims to create modular, maintainable, and scalable applications. The main principles of Clean Architecture are separation of concerns, dependency inversion, and testability. In this tutorial, I will show you how to implement Clean Architecture in a Next.js application.

Step 1: Set up a Next.js project

First, you need to create a new Next.js project. You can do this by running the following command in your terminal:

npx create-next-app@latest my-clean-architecture-app

This will set up a new Next.js project with the necessary folder structure and dependencies.

Step 2: Create the project structure

Next, you need to create the directory structure for your project. In Clean Architecture, the code is divided into layers, each with a specific responsibility. The typical layers in Clean Architecture are:

  • Entities: Contains the domain models and business logic of your application.
  • Use Cases: Contains the application-specific use cases.
  • Interface Adapters: Contains the implementation details such as UI components, database access, and external API integration.
  • Frameworks & Drivers: Contains the external dependencies such as the Next.js framework itself.

Here is an example directory structure for a Clean Architecture Next.js project:

├── components
├── entities
├── pages
├── services

Step 3: Implement the Entities

In the entities directory, you can define the domain models and business logic of your application. For example, if you are building a todo list app, you could define a Todo entity like this:

// entities/Todo.js

export default class Todo {
  constructor(title, completed) {
    this.title = title;
    this.completed = completed;
  }
}

Step 4: Implement the Use Cases

In the services directory, you can implement the application-specific use cases. For example, you could define a TodoService class that handles CRUD operations for todos:

// services/TodoService.js

import Todo from '../entities/Todo';

export default class TodoService {
  constructor() {
    this.todos = [];
  }

  createTodo(title) {
    const todo = new Todo(title, false);
    this.todos.push(todo);
    return todo;
  }

  getTodos() {
    return this.todos;
  }
}

Step 5: Implement the Interface Adapters

In the pages and components directories, you can implement the interface adapters such as React components and API integration. For example, you could create a TodoList component that displays the list of todos:

// components/TodoList.js

import { useEffect, useState } from 'react';
import TodoService from '../services/TodoService';

const todoService = new TodoService();

const TodoList = () => {
  const [todos, setTodos] = useState([]);

  useEffect(() => {
    setTodos(todoService.getTodos());
  }, []);

  return (
    <div>
      {todos.map(todo => (
        <div key={todo.title}>
          <p>{todo.title}</p>
          <p>{todo.completed ? 'Completed' : 'Incomplete'}</p>
        </div>
      ))}
    </div>
  );
};

export default TodoList;

Step 6: Use the components in your Next.js pages

Finally, you can use the interface adapters in your Next.js pages. For example, you could create a pages/index.js file that renders the TodoList component:

// pages/index.js

import TodoList from '../components/TodoList';

const Home = () => {
  return (
    <div>
      <h1>Todo List</h1>
      <TodoList />
    </div>
  );
};

export default Home;

That’s it! You have now implemented Clean Architecture in a Next.js application. This approach allows you to easily test and maintain your code, as well as scale your application as it grows. I hope this tutorial was helpful to you. Happy coding!

0 0 votes
Article Rating
45 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
@nikolovlazar
1 month ago

This live stream series is A LOT, so I distilled everything we learned + built a proper demo app that's open source into a 53 minute tutorial: https://youtu.be/jJVAla0dWJo

@jiachen1078
1 month ago

thank you!

@kiransilwal
1 month ago

1:35:48 Effect looks good. Is it just like Promise<Either<Failure,Success>> from fp-ts? I found that way of error handling easy and light weight.

@kiransilwal
1 month ago

My only question is on generating nanoid. In the repository layer we are tightly coupling our persistence/data layers with a package. Please correct me if I am wrong, wouldn’t it be better if we pass that package through the constructor as a service so that it is not tightly coupled to the library?

@bwest-dev6241
1 month ago

Hi, thanks for the great tutorial and series. Really enjoying it. Small problem I had watching was would have been nice if it was from scratch so we (i could code along) as you explained things. like the structure, creating of some of those files, but I still enjoyed it. h

@user-es2ri7lb7h
1 month ago

Could you please let me know why you are calling const supabase = createClient() for each function?

@kristian970
1 month ago

Thank you for that stream!

@ynspc
1 month ago

That keyboard loved it. 😀

@jss_developer
1 month ago

I definitely see the value in this, but for my new MVP this might be an overkill atm, but once its launched and others start working with me then this would make absolute sense, less files edited, more concise folder structures. this makes sense actually. Ill need to take some time to organize it like this.

@nitindevatraj
1 month ago

We need more stuff like this, ❤

@tconroy
1 month ago

Just so you know for the future: when passing arguments to constructors in TS, you can automatically assign them to properties on the class with a syntax like `constructor(private id: string) {}` — that way you don't have to repeat both the property definition and the assignment.

@okkashaally2115
1 month ago

Please create your development environment setup series

@okkashaally2115
1 month ago

Please create you development environment setup series

@Deus-lo-Vuilt
1 month ago

I came to review concepts, hopefully later we can see a complete auth system with the backend and frontend to see the entire flow with architecture, how to handle everything with jwt and so on.

@tharindudarshana6480
1 month ago

No relying on underlying implementation is called abstraction – https://youtu.be/BaUsKK6AX5Q?t=246 – its a fundamental concept in Software engineering. To make a more sense, your car is a better example, you don't need to know anything about how it works just to drive and use it for your needs 🤫

@Deus-lo-Vuilt
1 month ago

Thanks :3

@matyascsoti4400
1 month ago

My dude, i am a self taught junior developer and whilst i can write clean code, after 2 years now i realized how our apps we / i am working on have 0 architecture, 0 high level thought, and they are a complete mess on this level and the problems they bring. And it seems my peers are absolutely oblivious to this as they did not signal this and also we never had an 'architectural discussion' ( if that is even something happening at other places 😀 ), and as such i have no direct peer to learn from. So these kind of videos are gems inside the superficial hello world application videos most of youtube is filled with.

@ProgrammingwithUmair321
1 month ago

Great session man. I like your keyboard setup too 🔥😃

@jonasj2627
1 month ago

What do you think about using row level security?

@w3mw
1 month ago

What keyboard are you using? 😀

Will you create a video about neovim how to install it and with your config? 😀